This document describes the LAI estimation workflow in OSVAS, which automatically estimates monthly-averaged Leaf Area Index (LAI) values from Copernicus Global Land Service (CGLS) satellite data for SURFEX simulations.
SURFEX requires monthly Leaf Area Index (LAI) parameters for vegetation surfaces:
XUNIF_LAI - Vegetation Leaf Area Index (12 monthly values)Rather than using generic parameterizations that may not represent local vegetation conditions, the LAI estimation workflow derives these values from satellite observations (CGLS LAI at 300 m resolution), improving model realism for the study location.
Leaf Area Index is defined as the total one-sided area of leaves per unit ground area:
\[LAI = \frac{\text{Total leaf area}}}\]where:
The CGLS LAI product used in OSVAS is derived from Sentinel-3 OLCI and PROBA-V satellite observations via the BIOPAR process (available through openEO at Copernicus Data Space Ecosystem):
Monthly LAI values are computed as the average of all 10-day satellite observations within each calendar month during the observation period.
Edit your station’s YAML config file (e.g., config_files/Stations/Cabauw/Cabauw.yml):
Station_metadata:
Station_type: KNMI
Station_name: Cabauw
SID: 4300000008
elev: 0
lat: 51.9703
lon: 4.9264
vegtype: 10
estimate_lai: true # ← Set to true to enable LAI estimation
The first run requires authentication to access CGLS data. The script uses OpenID Connect (OIDC) device-code flow:
python3 scripts/python_scripts/estimate_lai.py Cabauw $OSVAS --run-period 2017-11-01 2018-01-31
On first run:
Alternative: Use --no-auth if already authenticated in the current environment:
python3 scripts/python_scripts/estimate_lai.py Cabauw $OSVAS --run-period 2017-11-01 2018-01-31 --no-auth
The LAI estimation scripts require the openeo Python package (included in OSVASENV):
# Already installed in conda environment
conda list | grep openeo
If missing, install manually:
pip install openeo
When estimate_lai: true is set in the configuration and validation workflow is enabled, LAI estimation executes automatically as Step 2c in the OSVAS workflow:
Step 1: Create forcing data
Step 2: Get validation data
Step 2b: Estimate surface albedos from validation data
Step 2c: ✨ Estimate LAI from CGLS satellite data (NEW)
├─ Connect to openEO backend (Copernicus Data Space)
├─ Fetch CGLS LAI using BIOPAR process
├─ Compute monthly climatology
├─ Generate namelist format blocks
└─ Update all experiment namelists
Step 3: Run SURFEX simulations (with updated LAIs)
Step 4: Extract model outputs
...
Location: namelists/{STATION_NAME}/lai_estimates.nam
Content: Formatted namelist block ready to be included in SURFEX namelists:
! XUNIF_LAI estimates from CGLS LAI 300m (openEO/Copernicus Data Space)
! Location : lat=51.9703, lon=4.9264
! Period : 2017-11-01 – 2018-01-31
! Generated by estimate_lai.py
XUNIF_LAI(10, 1) = 0.521,
XUNIF_LAI(10, 2) = 0.412,
XUNIF_LAI(10, 3) = 0.634,
...
XUNIF_LAI(10,12) = 0.387,
Location: namelists/{STATION_NAME}/OPTIONS.nam_{EXPNAME}
The script either:
&NAM_DATA_ISBA parameter groupBackup copies are created:
OPTIONS.nam_{EXPNAME}.backup_lai - Original namelist before LAI updateYou can run the LAI estimation scripts manually for testing or batch processing:
python3 scripts/python_scripts/estimate_lai.py \
Cabauw \
$OSVAS \
--run-period 2017-11-01 2018-01-31
Parameters:
station_name: Station name (e.g., Cabauw, Loobos)osvas_root: Path to OSVAS root directory--run-period START_DATE END_DATE (optional): Observation period (YYYY-MM-DD format)
--start-year YYYY (alternative): First year of multi-year climatology (default: 2015)--end-year YYYY (alternative): Last year of multi-year climatology (default: 2023)
--start-year 2018 --end-year 2022 for 2018–2022 average--output-file PATH (optional): Save estimates to custom path (default: namelists/{station}/lai_estimates.nam)--backend URL (optional): openEO backend URL (default: https://openeofed.dataspace.copernicus.eu)--no-auth (optional): Skip OIDC authentication (if already authenticated)--list-collections (optional): List all available LAI collections on backend and exit--collection NAME (optional): Use load_collection with named collection instead of BIOPAR--print-only (optional): Format pre-computed LAI values without connecting to openEO--monthly-lai JSON (optional): Pre-computed monthly LAI as JSON string (for –print-only mode)Output:
======================================================================
LAI estimation for station: Cabauw
======================================================================
Station : Cabauw
Location : lat=51.9703, lon=4.9264
Vegtype : 10
Fetch period: 2017-11-01 – 2018-01-31
Step 1: Connecting to openEO
✓ Connected
Step 2: Fetching LAI time series
Using BIOPAR process (Algorithm Plaza / VITO)
Processing 2017-11-01 → 2017-12-31 ...
→ 24 observations
Processing 2018-01-01 → 2018-01-31 ...
→ 28 observations
Total valid observations: 52
Step 3: Computing monthly climatology
Month 1: 0.521
Month 2: 0.412
Month 3: 0.634
Month 4: 0.876
...
Month 12: 0.387
Step 4: Writing namelist block
XUNIF_LAI(10, 1) = 0.521,
XUNIF_LAI(10, 2) = 0.412,
XUNIF_LAI(10, 3) = 0.634,
...
XUNIF_LAI(10,12) = 0.387,
✓ Written to: /home/user/OSVAS/namelists/Cabauw/lai_estimates.nam
======================================================================
✅ LAI estimation complete
======================================================================
python3 scripts/python_scripts/update_namelist_lais.py \
Cabauw \
$OSVAS \
--expnames DIFMEB_v9 DIFMEB_v9DSL
Parameters:
station_name: Station nameosvas_root: OSVAS root directory--expnames EXP1 EXP2 ... (optional): Experiment names to update (default: read from config YAML)--lai-file PATH (optional): Path to LAI estimates (default: namelists/{station}/lai_estimates.nam)--no-backup (optional): Don’t create backup copiesOutput:
======================================================================
Updating namelists with estimated LAIs for Cabauw
======================================================================
Using experiments from config: DIFMEB_v9, DIFMEB_v9DSL
Step 1: Reading LAI estimates
Read LAI estimates from: namelists/Cabauw/lai_estimates.nam
Step 2: Updating experiment namelists
Processing: DIFMEB_v9
✓ Replaced existing XUNIF_LAI block
✓ Backup created: OPTIONS.nam_DIFMEB_v9.backup_lai
✓ Namelist updated: namelists/Cabauw/OPTIONS.nam_DIFMEB_v9
Processing: DIFMEB_v9DSL
ℹ️ No existing XUNIF_LAI block found — inserting into &NAM_DATA_ISBA
✓ Inserted XUNIF_LAI block into &NAM_DATA_ISBA
✓ Backup created: OPTIONS.nam_DIFMEB_v9DSL.backup_lai
✓ Namelist updated: namelists/Cabauw/OPTIONS.nam_DIFMEB_v9DSL
======================================================================
✅ Updated 2/2 experiment namelists
======================================================================
The LAI estimation script performs the following QC checks:
Realistic LAI values depend on vegetation type and season:
| Vegetation Type | Season | LAI Range | Notes |
|---|---|---|---|
| Grass / herbaceous | Summer | 2.0-4.0 | Peak growing season |
| Grass / herbaceous | Winter | 0.2-0.8 | Dormant period |
| Crops (cereals) | Growing | 1.5-3.5 | Depends on crop type |
| Crops (cereals) | Harvest | 0.1-0.5 | Post-harvest senescence |
| Deciduous forest | Summer | 4.0-6.0 | Full leaf out |
| Deciduous forest | Winter | 0.0-0.5 | Leafless or buds only |
| Evergreen forest | Year-round | 4.0-8.0+ | Dense canopy |
If estimated LAI falls outside expected ranges, check:
vegtype in YAML) matches the actual site conditionsThe script prints monthly LAI values for visual inspection:
Step 3: Computing monthly climatology
Month 1: 0.521
Month 2: 0.412
Month 3: 0.634
...
Plot these values to identify seasonal patterns and anomalies:
import matplotlib.pyplot as plt
months = range(1, 13)
lai = [0.521, 0.412, 0.634, 0.876, 1.234, 1.456, ...] # from output
plt.plot(months, lai, 'o-')
plt.xlabel('Month')
plt.ylabel('LAI (m²/m²)')
plt.title('Seasonal LAI cycle')
plt.show()
A typical grass or crop site should show:
Sharp jumps or flat values may indicate data quality issues or CGLS retrieval problems.
Causes:
Solution:
--run-period 2017-01-01 2018-12-31--start-year 2017 --end-year 2022curl -s https://openeofed.dataspace.copernicus.eu | head
Causes:
Solution:
Causes:
Solution:
vegtype in YAMLCauses:
--collection with a collection name not available on backendSolution:
--list-collections--collection argument--backend https://openeofed.dataspace.copernicus.euCauses:
vegtype in LAI estimates file differs from target namelistSolution:
vegtype is consistent: check both estimate output and namelistpython3 scripts/python_scripts/estimate_lai.py [station] [osvas_root]cat namelists/{station}/lai_estimates.namIf you have LAI values from another source (literature, ICOS measurements, previous analysis), you can format them directly without connecting to openEO:
python3 scripts/python_scripts/estimate_lai.py Cabauw $OSVAS \
--print-only \
--monthly-lai '{"1":0.5,"2":0.4,"3":0.6,"4":0.8,"5":1.2,"6":1.5,"7":1.6,"8":1.4,"9":1.0,"10":0.7,"11":0.5,"12":0.4}'
This outputs a formatted namelist block without openEO authentication.
If BIOPAR is unavailable or you prefer a different CGLS LAI product, list available collections:
python3 scripts/python_scripts/estimate_lai.py Cabauw $OSVAS --list-collections | grep -i lai
Then specify a collection:
python3 scripts/python_scripts/estimate_lai.py Cabauw $OSVAS \
--run-period 2017-11-01 2018-01-31 \
--collection 'CGLS_LAI300_V2_202101'
To estimate LAI for all stations in batch:
for station in Cabauw Loobos Majadas_del_tietar Meteopole; do
echo "Processing $station..."
python3 scripts/python_scripts/estimate_lai.py $station $OSVAS --run-period 2017-11-01 2018-01-31
python3 scripts/python_scripts/update_namelist_lais.py $station $OSVAS
done
To understand temporal coverage of satellite observations, check the output count of valid observations. Low counts may indicate:
If severe, consider: