{ "cells": [ { "cell_type": "markdown", "id": "ebac9de3", "metadata": {}, "source": [ "# Footprint representativeness after Chu et al. (2021)\n", "\n", "A footprint climatology tells you *where the flux came from*. This notebook\n", "answers the complementary question: **how well does that source area stand in\n", "for the target area the site is being used to represent?** — the model grid\n", "cell, the satellite pixel window, or the fixed-radius disc a network product\n", "assigns to the tower.\n", "\n", "It walks through the whole method of\n", "\n", "> Chu, H., Luo, X., Ouyang, Z., et al. (2021). Representativeness of\n", "> Eddy-Covariance flux footprints for areas surrounding AmeriFlux sites.\n", "> *Agricultural and Forest Meteorology* **301–302**, 108350.\n", "> [doi:10.1016/j.agrformet.2021.108350](https://doi.org/10.1016/j.agrformet.2021.108350)\n", "\n", "in four steps:\n", "\n", "1. **Build a monthly day/night climatology** from half-hourly tower data.\n", "2. **Load a land-cover raster and an EVI stack** onto the footprint grid.\n", "3. **Run `assess_representativeness`** — the geometry metrics of Eqs. 1–3 and\n", " the categorical and continuous evaluations of Sect. 2.4.\n", "4. **Produce the four figures** of `representativeness_plotting`.\n", "\n", "The theory, all seven equations, and the classification tables are on the\n", "[Footprint representativeness](../representativeness.rst) page. The\n", "[validation](../validation.md) page compares these metrics against the\n", "authors' own published values.\n", "\n", "The tower record is the bundled AmeriFlux **US-CRT** BASE file (Curtice\n", "Walter-Berger cropland, north-west Ohio), May–October 2011." ] }, { "cell_type": "code", "execution_count": 1, "id": "25a75b18", "metadata": { "execution": { "iopub.execute_input": "2026-08-30T16:13:54.290514Z", "iopub.status.busy": "2026-08-30T16:13:54.290266Z", "iopub.status.idle": "2026-08-30T16:13:55.919626Z", "shell.execute_reply": "2026-08-30T16:13:55.918726Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "reading tower data from data/\n" ] } ], "source": [ "import tempfile\n", "from pathlib import Path\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pandas as pd\n", "import rasterio\n", "import xarray as xr\n", "from matplotlib.colors import BoundaryNorm, ListedColormap\n", "from pyproj import Transformer\n", "from rasterio.transform import from_origin\n", "\n", "from fluxfootprints import (\n", " ASYMMETRY_THRESHOLD,\n", " BIAS_THRESHOLD,\n", " assess_representativeness,\n", " build_climatology,\n", " compute_aerodynamic_params,\n", " evaluate_landcover,\n", " load_amf_df,\n", " load_config,\n", " monthly_climatologies,\n", " plot_bias_density,\n", " plot_footprint_target_scatter,\n", " plot_landcover_composition,\n", " plot_level_bars,\n", " representativeness_table,\n", " sample_raster_on_grid,\n", ")\n", "\n", "pd.set_option(\"display.width\", 160)\n", "pd.set_option(\"display.max_columns\", 40)\n", "\n", "DATA = Path(\"..\") / \"..\" / \"data\" # repository data/ directory\n", "WORK = Path(tempfile.mkdtemp(prefix=\"representativeness-\")) # scratch GeoTIFFs\n", "\n", "SITE = \"US-CRT\"\n", "TZ = -5 # US-CRT records local standard time\n", "START, END = \"2011-05-01\", \"2011-10-31\" # one growing season\n", "STEP, HALF = 10.0, 1000.0 # 10 m cells on a +/- 1 km domain\n", "RADII = (100.0, 250.0, 500.0, 1000.0) # target-area radii [m]\n", "\n", "print(f\"reading tower data from {DATA.resolve().name}/\")" ] }, { "cell_type": "markdown", "id": "293ea7b5", "metadata": {}, "source": [ "> **Note on the target radii.** Chu et al. (2021) evaluate 250 m to 3000 m\n", "> (`fluxfootprints.TARGET_RADII`). US-CRT is a short-canopy cropland site with a\n", "> 2.5 m tower, so its footprint is only a couple of hundred metres across; a\n", "> 1 km domain resolves it well and a 3 km disc would tell us nothing the 1 km\n", "> one does not. The radii above are chosen to bracket *this* footprint. Pass\n", "> `radii=TARGET_RADII` to reproduce the paper's series.\n", "\n", "## 1. Build a monthly day/night climatology\n", "\n", "Chu et al. aggregate every half-hourly footprint of a calendar month into a\n", "**daytime** and a **nighttime** climatology, truncate each at the contour\n", "enclosing 80 % of the source weight, and rescale the retained cells to sum to\n", "one. That truncated climatology is the unit of analysis for everything\n", "downstream.\n", "\n", "First the tower record. `load_config` reads the site's `.ini`, `load_amf_df`\n", "reads the AmeriFlux BASE file, and the columns are renamed to the solver's\n", "names." ] }, { "cell_type": "code", "execution_count": 2, "id": "0763052f", "metadata": { "execution": { "iopub.execute_input": "2026-08-30T16:13:55.921974Z", "iopub.status.busy": "2026-08-30T16:13:55.921549Z", "iopub.status.idle": "2026-08-30T16:13:56.257624Z", "shell.execute_reply": "2026-08-30T16:13:56.256522Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "US-CRT: 41.6285 N, -83.3471 E\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "5158 half-hours from 2011-05-02 20:00:00 to 2011-10-31 17:30:00\n" ] }, { "data": { "text/html": [ "
| \n", " | wind_dir | \n", "umean | \n", "ustar | \n", "ol | \n", "sigmav | \n", "zm | \n", "z0 | \n", "
|---|---|---|---|---|---|---|---|
| TIMESTAMP_START | \n", "\n", " | \n", " | \n", " | \n", " | \n", " | \n", " | \n", " |
| 2011-05-02 20:00:00 | \n", "267.2680 | \n", "2.18254 | \n", "0.16236 | \n", "27.346434 | \n", "0.48027 | \n", "2.232 | \n", "0.0492 | \n", "
| 2011-05-03 05:00:00 | \n", "37.9146 | \n", "2.49536 | \n", "0.15085 | \n", "58.736718 | \n", "0.66084 | \n", "2.232 | \n", "0.0492 | \n", "
| 2011-05-03 05:30:00 | \n", "59.4592 | \n", "4.49470 | \n", "0.27739 | \n", "686.206897 | \n", "0.73097 | \n", "2.232 | \n", "0.0492 | \n", "
| 2011-05-03 06:00:00 | \n", "64.3045 | \n", "3.98159 | \n", "0.26283 | \n", "401.209677 | \n", "0.83509 | \n", "2.232 | \n", "0.0492 | \n", "
| 2011-05-03 06:30:00 | \n", "56.1142 | \n", "3.40681 | \n", "0.22406 | \n", "354.092527 | \n", "0.86838 | \n", "2.232 | \n", "0.0492 | \n", "
<xarray.Dataset> Size: 4MB\n",
"Dimensions: (month: 6, period: 2, x: 201, y: 201)\n",
"Coordinates:\n",
" * month (month) datetime64[ns] 48B 2011-05-01 ... 2011-10-01\n",
" * period (period) <U9 72B 'daytime' 'nighttime'\n",
" * x (x) float64 2kB -1e+03 -990.0 -980.0 ... 990.0 1e+03\n",
" * y (y) float64 2kB -1e+03 -990.0 -980.0 ... 990.0 1e+03\n",
"Data variables:\n",
" footprint_climatology (month, period, x, y) float64 4MB 0.0 0.0 ... 0.0 0.0\n",
" n_times (month, period) int64 96B 591 214 727 ... 347 509 297\n",
" contour_level (month, period) float64 96B 4.949e-06 ... 4.877e-06\n",
" contour_n_cells (month, period) int64 96B 154 130 158 ... 157 214 160\n",
"Attributes:\n",
" partition: month+daynight\n",
" contour_fraction: 0.8\n",
" min_times: 1\n",
" n_timesteps: 5158\n",
" description: Monthly footprint climatologies truncated at the 80% s...\n",
" reference: Chu, H., et al. (2021). Agric. For. Meteorol., 301-302...| period | \n", "daytime | \n", "nighttime | \n", "
|---|---|---|
| month | \n", "\n", " | \n", " |
| 2011-05-01 | \n", "591 | \n", "214 | \n", "
| 2011-06-01 | \n", "727 | \n", "162 | \n", "
| 2011-07-01 | \n", "713 | \n", "157 | \n", "
| 2011-08-01 | \n", "646 | \n", "187 | \n", "
| 2011-09-01 | \n", "608 | \n", "347 | \n", "
| 2011-10-01 | \n", "509 | \n", "297 | \n", "
| period | \n", "daytime | \n", "nighttime | \n", "
|---|---|---|
| month | \n", "\n", " | \n", " |
| 2011-05-01 | \n", "154 | \n", "130 | \n", "
| 2011-06-01 | \n", "158 | \n", "128 | \n", "
| 2011-07-01 | \n", "172 | \n", "151 | \n", "
| 2011-08-01 | \n", "180 | \n", "150 | \n", "
| 2011-09-01 | \n", "218 | \n", "157 | \n", "
| 2011-10-01 | \n", "214 | \n", "160 | \n", "
| \n", " | period | \n", "fetch | \n", "area | \n", "symmetry | \n", "seasonal_overlap | \n", "daynight_overlap | \n", "
|---|---|---|---|---|---|---|
| 0 | \n", "daytime | \n", "98.873 | \n", "18266.667 | \n", "0.597 | \n", "0.918 | \n", "0.959 | \n", "
| 1 | \n", "nighttime | \n", "102.545 | \n", "14600.000 | \n", "0.469 | \n", "0.842 | \n", "0.959 | \n", "
| \n", " | period | \n", "radius | \n", "dominant_class | \n", "value_footprint | \n", "value_target | \n", "chi2 | \n", "p_value | \n", "level | \n", "
|---|---|---|---|---|---|---|---|---|
| 0 | \n", "daytime | \n", "100.0 | \n", "82 | \n", "1.0 | \n", "1.000 | \n", "0.000 | \n", "1.0 | \n", "high | \n", "
| 1 | \n", "daytime | \n", "250.0 | \n", "82 | \n", "1.0 | \n", "0.957 | \n", "88.851 | \n", "0.0 | \n", "low | \n", "
| 2 | \n", "daytime | \n", "500.0 | \n", "82 | \n", "1.0 | \n", "0.699 | \n", "3375.424 | \n", "0.0 | \n", "low | \n", "
| 3 | \n", "daytime | \n", "1000.0 | \n", "82 | \n", "1.0 | \n", "0.524 | \n", "28581.048 | \n", "0.0 | \n", "low | \n", "
| 4 | \n", "nighttime | \n", "100.0 | \n", "82 | \n", "1.0 | \n", "1.000 | \n", "0.000 | \n", "1.0 | \n", "high | \n", "
| 5 | \n", "nighttime | \n", "250.0 | \n", "82 | \n", "1.0 | \n", "0.957 | \n", "88.851 | \n", "0.0 | \n", "low | \n", "
| 6 | \n", "nighttime | \n", "500.0 | \n", "82 | \n", "1.0 | \n", "0.699 | \n", "3375.424 | \n", "0.0 | \n", "low | \n", "
| 7 | \n", "nighttime | \n", "1000.0 | \n", "82 | \n", "1.0 | \n", "0.524 | \n", "28581.048 | \n", "0.0 | \n", "low | \n", "
| \n", " | mean_footprint_evi | \n", "mean_target_evi | \n", "mean_bias | \n", "within_10pct | \n", "
|---|---|---|---|---|
| radius | \n", "\n", " | \n", " | \n", " | \n", " |
| 100.0 | \n", "0.439 | \n", "0.437 | \n", "0.010 | \n", "1.0 | \n", "
| 250.0 | \n", "0.439 | \n", "0.438 | \n", "-0.003 | \n", "1.0 | \n", "
| 500.0 | \n", "0.439 | \n", "0.440 | \n", "-0.057 | \n", "0.5 | \n", "
| 1000.0 | \n", "0.439 | \n", "0.419 | \n", "-0.023 | \n", "0.333 | \n", "
| \n", " | period | \n", "radius | \n", "slope | \n", "intercept | \n", "r_squared | \n", "p_value | \n", "n | \n", "sufficient | \n", "level | \n", "
|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", "daytime | \n", "100.0 | \n", "1.014 | \n", "-0.007 | \n", "1.000 | \n", "0.0 | \n", "6 | \n", "True | \n", "high | \n", "
| 1 | \n", "daytime | \n", "250.0 | \n", "0.989 | \n", "0.005 | \n", "0.999 | \n", "0.0 | \n", "6 | \n", "True | \n", "high | \n", "
| 2 | \n", "daytime | \n", "500.0 | \n", "0.785 | \n", "0.096 | \n", "0.995 | \n", "0.0 | \n", "6 | \n", "True | \n", "medium | \n", "
| 3 | \n", "daytime | \n", "1000.0 | \n", "0.628 | \n", "0.144 | \n", "0.986 | \n", "0.0 | \n", "6 | \n", "True | \n", "medium | \n", "
| 4 | \n", "nighttime | \n", "100.0 | \n", "1.017 | \n", "-0.009 | \n", "1.000 | \n", "0.0 | \n", "6 | \n", "True | \n", "high | \n", "
| 5 | \n", "nighttime | \n", "250.0 | \n", "0.991 | \n", "0.003 | \n", "0.999 | \n", "0.0 | \n", "6 | \n", "True | \n", "high | \n", "
| 6 | \n", "nighttime | \n", "500.0 | \n", "0.787 | \n", "0.095 | \n", "0.995 | \n", "0.0 | \n", "6 | \n", "True | \n", "medium | \n", "
| 7 | \n", "nighttime | \n", "1000.0 | \n", "0.629 | \n", "0.143 | \n", "0.986 | \n", "0.0 | \n", "6 | \n", "True | \n", "medium | \n", "
| \n", " | SITE_ID | \n", "PERIOD | \n", "TARGET_AREA | \n", "VARIABLE | \n", "N | \n", "INTERCEPT | \n", "INTERCEPT_LCL | \n", "INTERCEPT_UCL | \n", "SLOPE | \n", "SLOPE_LCL | \n", "SLOPE_UCL | \n", "R2 | \n", "RMSE | \n", "MAE | \n", "REP_EVI | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", "US-CRT | \n", "daytime | \n", "100.0 | \n", "EVI | \n", "6 | \n", "-0.007 | \n", "-0.021 | \n", "0.006 | \n", "1.014 | \n", "0.984 | \n", "1.046 | \n", "1.000 | \n", "0.006 | \n", "0.005 | \n", "high | \n", "
| 1 | \n", "US-CRT | \n", "daytime | \n", "250.0 | \n", "EVI | \n", "6 | \n", "0.005 | \n", "-0.010 | \n", "0.020 | \n", "0.989 | \n", "0.955 | \n", "1.024 | \n", "0.999 | \n", "0.006 | \n", "0.005 | \n", "high | \n", "
| 2 | \n", "US-CRT | \n", "daytime | \n", "500.0 | \n", "EVI | \n", "6 | \n", "0.096 | \n", "0.060 | \n", "0.129 | \n", "0.785 | \n", "0.710 | \n", "0.868 | \n", "0.995 | \n", "0.052 | \n", "0.045 | \n", "medium | \n", "
| 3 | \n", "US-CRT | \n", "daytime | \n", "1000.0 | \n", "EVI | \n", "6 | \n", "0.144 | \n", "0.095 | \n", "0.185 | \n", "0.628 | \n", "0.534 | \n", "0.739 | \n", "0.986 | \n", "0.091 | \n", "0.077 | \n", "medium | \n", "
| 4 | \n", "US-CRT | \n", "nighttime | \n", "100.0 | \n", "EVI | \n", "6 | \n", "-0.009 | \n", "-0.023 | \n", "0.004 | \n", "1.017 | \n", "0.987 | \n", "1.047 | \n", "1.000 | \n", "0.007 | \n", "0.006 | \n", "high | \n", "
| 5 | \n", "US-CRT | \n", "nighttime | \n", "250.0 | \n", "EVI | \n", "6 | \n", "0.003 | \n", "-0.012 | \n", "0.018 | \n", "0.991 | \n", "0.957 | \n", "1.026 | \n", "0.999 | \n", "0.006 | \n", "0.004 | \n", "high | \n", "
| 6 | \n", "US-CRT | \n", "nighttime | \n", "500.0 | \n", "EVI | \n", "6 | \n", "0.095 | \n", "0.058 | \n", "0.128 | \n", "0.787 | \n", "0.712 | \n", "0.870 | \n", "0.995 | \n", "0.051 | \n", "0.044 | \n", "medium | \n", "
| 7 | \n", "US-CRT | \n", "nighttime | \n", "1000.0 | \n", "EVI | \n", "6 | \n", "0.143 | \n", "0.094 | \n", "0.184 | \n", "0.629 | \n", "0.535 | \n", "0.741 | \n", "0.986 | \n", "0.091 | \n", "0.077 | \n", "medium | \n", "