This release is a pre-release and may not be stable for production use.
FluxPrint
FluxPrint is an open-source Python package implementing flux footprint models
for eddy covariance data analysis. It provides footprint-model implementations
behind a single, consistent interface so researchers can compare spatially
resolved fluxes with field measurements, and add new models by following a small
convention. It is designed for interoperability with ecosystem flux datasets
(e.g. FLUXNET). See Figure 1 for the conceptual scheme.
Figure 1. Conceptual scheme for FluxPrint.
Why FluxPrint?
Most eddy-covariance users already have Kljun's official FFP scripts or EddyPro's built-in footprint estimates. FluxPrint adds, on top of the same science:
- Verified fidelity: the Kljun et al. (2015) implementation is pinned against the original FFP code (vendored in this repository) by regression tests asserting bitwise-identical fields — a dedicated CI job fails if any registered model ever diverges from its reference implementation.
- A batch workflow: one call takes a half-hourly table (FLUXNET/ICOS
style), validates and estimates missing micrometeorology per record, and
returns a typed
FootprintSeriesready for climatologies. - A typed object API: georeferencing, source-area contours, plots, NetCDF/GeoTIFF/Shapefile export, and provenance metadata on every result.
Features
- Footprint models behind one interface, selected by name: Kljun et al.
(2015) and Hsieh et al. (2000, with the Detto et al. 2006 crosswind
expansion); Kormann & Meixner (2001) is planned. A new model is only its
physics — a per-record kernel registered with
@footprint_modelinherits the grid, record loop, validation, smoothing and provenance. - A typed footprint object (
Footprint): a 2-D source-area field on a fixed grid centred on the tower, plusFootprintSeriesfor time-ordered stacks. - Two coordinate frames: a local, tower-centred metric grid, and a
georeferenced projected grid (
georeference()); lon/lat is display-only. - Source-area contours:
contours()for the 50/80% isopleths,plot()for a quick look,to_shapefile()for GIS. - Serialization: NetCDF is the native format (CF grid mapping included), with GeoTIFF conversion; outputs carry provenance (package/model version, citation, settings).
- Aggregation: collapse a
FootprintSeriesinto a climatology.
Installation
pip install fluxprint
Or from source for development:
pip install git+https://github.com/pedrohenriquecoimbra/fluxprint
import fluxprint is light: the geo/plotting stack (xarray/netcdf4,
rasterio, pyproj, fiona/shapely, matplotlib) is imported lazily by
the operations that need it. Those libraries are still installed as
dependencies today; a future release will move them into the already-declared
extras (fluxprint[netcdf], [tiff], [crs], [shapefile]; [viz] adds
folium for interactive maps, [all] installs everything).
Quickstart
From a met table to a climatology (the batch path)
import pandas as pd
import fluxprint
data = pd.read_csv("halfhours.csv", na_values=[-9999]) # FLUXNET-style table
climatology = fluxprint.wrapper(
data=data,
zm=20.0, # aerodynamic height: z - d, see note below
pblh=1500.0, # boundary-layer height [m] - not in FLUXNET tables, so
# supply it (or a PBLH/BLH column, or opt into the crude
# 1000 m constant with fill_all=True)
tower=(4321000.0, 3210000.0), # tower position, for georeferencing
tower_crs="EPSG:3035",
dst="climatology.nc", # optional: write the result
)
Columns are matched case-insensitively with common aliases (USTAR/u*,
WD/wind_direction, WS/wind_speed, OL/L, SIGMA_V/v_sd, ...;
H, the sensible heat flux, is matched exactly so FFP's lowercase h — the
boundary-layer height — is never mistaken for it). Missing variables are
estimated when physically possible — e.g. the Obukhov length from
USTAR/H/TA/PA — and every estimated variable is recorded in the
result's attrs["estimated_inputs"]. Crude constant fallbacks (including the
pblh=1000 constant) require an explicit fill_all=True and warn loudly.
Use fluxprint.calculate_footprint(data, by="timestamp_column", ...) for one
footprint per group (a FootprintSeries) instead of a single climatology.
zm is the aerodynamic height. All models define
zm = z - d(measurement height above the zero-plane displacement), not the instrument height. Over vegetation the difference is substantial. If you passmeasurement_heighttogether withdisplacement(orcanopy_height, using d ≈ 0.67·h), FluxPrint deriveszmfor you.
Compute a single footprint directly
from fluxprint.model import get_model
kljun = get_model("kljun2015")
fp = kljun(
zm=2.0, # measurement height above displacement [m]
z0=0.01, # roughness length [m] (or pass umean instead)
ustar=0.5, # friction velocity [m s-1]
pblh=1000.0, # boundary-layer height [m]
mo_length=-50.0, # Obukhov length [m]
v_sigma=0.5, # std. dev. of lateral velocity [m s-1]
wind_dir=180.0, # wind direction [deg] (orients the grid north-up)
dx=2.0, # grid spacing [m]
tower=(4321000.0, 3210000.0),
tower_crs="EPSG:3035",
)
fp.total() # fraction of the flux captured by the grid (also in attrs)
fp.peak_xy() # (x, y) of the footprint peak, metres from the tower
fp.contours([0.5, 0.8]) # 50%/80% source-area isopleths
fp.plot(rs=[0.5, 0.8]) # quick-look plot (matplotlib)
Inputs may be scalars (one record) or equal-length sequences (composited into a
single footprint, fp.n records). Records failing the model's validity checks
(including NaN gaps) are rejected per record.
Georeference and export
geo = fp.georeference("EPSG:3035") # local metres -> projected coords (needs pyproj)
geo.to_netcdf("footprint.nc") # needs xarray/netcdf4; CF grid mapping included
geo.to_tiff("footprint.tif") # needs rasterio
geo.to_shapefile("contours.shp", rs=[0.5, 0.8]) # needs fiona
Adding a model
A model is a callable mapping micrometeorological inputs to one 2-D Footprint
in the local frame. Register it by name and it becomes selectable everywhere:
from fluxprint.model import register_model
from fluxprint.footprint import Footprint
@register_model("my_model", description="My footprint parameterisation")
def calc(*, zm, ustar, pblh, mo_length, v_sigma, wind_dir, z0=None, umean=None,
domain=None, dx=None, dy=None, tower=None, tower_crs=None, time=None,
**kwargs) -> Footprint:
f = ... # 2-D field on a regular grid centred on the tower
return Footprint.from_grid(f, dx=dx, tower=tower, tower_crs=tower_crs, time=time)
from fluxprint.model import available_models, get_model
available_models() # ['kljun2015', 'my_model']
get_model("my_model")(...)
The registry is backed by regorator. New models are expected to come with a
reference oracle in tests/test_reference_regression.py pinning them against
their original implementation — CI enforces this.
API reference
Top level (import fluxprint)
calculate_footprint(data, by=None, model="kljun2015", on_error="skip", ...)— footprint each group of a table; returns aFootprintSeries.on_errordecides what a failed group costs: drop it ("skip"), abort ("raise"), or keep an all-NaN member with the reason inattrs["error"]("nan"). The series is eager — right for grouped climatologies (tens to hundreds of members); for one footprint per record over long records, usemap_footprintsbelow instead.map_footprints(data, model="kljun2015", ...)— the xarray/dask-native compute path: maps the model's per-record kernel over arrays of any dimensionality and returns aDataArraywith dims(*input_dims, y, x). Lazy over dask-backed inputs; a bad record costs a NaN plane, never the job; rejections are summarised once per block.wrapper(...)—calculate_footprint+ aggregation + optional file output.empty_footprint(model, domain=..., dx=...)— a NaN template on the model's grid. This is the template API: use it to learn the exact output grid/shape (~2 ms) instead of running the model on placeholder meteorology.ALIASES— the table of column aliasesprocess_footprint_inputsrecognizes (model inputs and estimator drivers).read_handler/read_from_url/read_from_file— table/NetCDF/TIFF readers.- Legacy
write_to_*helpers are deprecated: use theFootprint.to_*methods. fluxprint.commons.start_logging(path)— opt-in file logging for applications: attaches a handler to thefluxprintlogger (the package itself stays silent by default via aNullHandler).
fluxprint.model
get_model(name)— return the registered model callable.available_models()— list registered model names.register_model(name, description="", **attrs)— decorator to register a model.FootprintModel— the callable protocol models conform to.
fluxprint.footprint.Footprint
Footprint.from_grid(f, dx, dy=None, **meta)— build a local, tower-centred footprint.georeference(target_crs)/to_lonlat()— local → projected; display lon/lat.total(),peak_xy(),normalized(),contours(rs)— analysis helpers.captured_fraction— fraction of the flux the domain captures (=total()).level_for(r)— the source-area field level for fractionr(fractions are of the full unit-integral footprint, not of the captured total).smoothed()— the standard FFP 3×3 double-convolution smoothing, applicable to any footprint (fluxprint.footprint.smooth_fieldis the array primitive).weighted_mean(field, min_coverage=None)— footprint-weighted mean of a gridded quantity; owns the NaN mask and the coverage threshold, and returns a uniform field unchanged whatever the normalization or truncation.replace(**changes)— variant constructor (fp.replace(time=None)); copiesattrs, shares the arrays unless replaced.plot(rs=None)— matplotlib quick look (never callsplt.show()).to_netcdf/from_netcdf,to_tiff/from_tiff,to_xarray/from_xarray,to_shapefile.
fluxprint.footprint.FootprintSeries
aggregate(smooth=True)— collapse the stack to a 2-D climatology.georeference(target_crs),to_netcdf/from_netcdf,to_xarray/from_xarray.
See CHANGELOG.md for release notes and deprecations.
Examples
See the sample/ directory for usage examples.
Citing FluxPrint
If FluxPrint contributes to your research, please cite the software (see CITATION.cff) alongside the footprint-model paper you used (e.g. Kljun et al., 2015 — the models' own citations and DOIs are stamped into every output's metadata).
Contributing
Contributions are welcome — see CONTRIBUTING.md for the development setup, and open a pull request from a feature branch.
License
Licensed under the European Union Public Licence v. 1.2 (EUPL-1.2). See the
LICENSE file for details. The vendored Kljun et al. (2015) FFP
reference code (fluxprint/model/Kljun_et_al_2015_original/) is distributed
under its own permissive licence (see the license.txt alongside it),
copyright (C) 2015–2023 Natascha Kljun.
Acknowledgments
- Kljun, N., Calanca, P., Rotach, M. W., Schmid, H. P. (2015): The simple two-dimensional parameterisation for Flux Footprint Predictions (FFP), Geosci. Model Dev. 8, 3695–3713, doi:10.5194/gmd-8-3695-2015.
- Hsieh, C.-I., Katul, G., Chi, T. (2000): An approximate analytical model for footprint estimation of scalar fluxes in thermally stratified atmospheric flows, Adv. Water Resour. 23, 765–772, doi:10.1016/S0309-1708(99)00042-1.
- Kormann, R., Meixner, F. X. (2001): An analytical footprint model for non-neutral stratification, Boundary-Layer Meteorol. 99, 207–224, doi:10.1023/A:1018991015119.
Contact
Metadata
Release files for fluxprint 0.4.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fluxprint-0.4.0a1.tar.gz | 122.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fluxprint-0.4.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 223.9 kB
Release files / fluxprint-0.4.0a1.tar.gz
| Download URL | fluxprint-0.4.0a1.tar.gz |
|---|---|
| Size | 122.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b0965a206e295ecb456386d5c0672c6b7d1bcea2c77566a8c6274d71e491cf7b
|
|
BLAKE2b-256 checksum How to use checksums |
3730ef3a8831f700700c82ea986361f003453b7e283007045764597169e347ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.1.1 CPython/3.8.12
|
Release files / fluxprint-0.4.0a1-py3-none-any.whl
| Download URL | fluxprint-0.4.0a1-py3-none-any.whl |
|---|---|
| Size | 101.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
145c765841e116aa973764750953f52a4de5d7f03d274bac68a2c1136aee43cb
|
|
BLAKE2b-256 checksum How to use checksums |
ff24414b0df118aec3715a7a56e36d5c0e6982021d1b4e2d587ae093f4153ec3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.1.1 CPython/3.8.12
|