OSVAS

Leaf Area Index (LAI) Estimation from Satellite Data

Overview

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.

TL;DR: Check this short guide LAI_estimation_quickref and the implementation summary

Motivation

SURFEX requires monthly Leaf Area Index (LAI) parameters for vegetation surfaces:

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.

Physical Basis

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.

Configuration

1. Enable LAI Estimation in Station Configuration

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

2. Authentication with Copernicus Data Space Ecosystem

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:

  1. Script prints a URL and device code
  2. Visit the URL in a browser and enter the device code
  3. Credentials are cached automatically for future runs

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

3. Requirements

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

Workflow Integration

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
...

Output Files

1. Estimated LAI Namelist

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,

2. Updated Experiment Namelists

Location: namelists/{STATION_NAME}/OPTIONS.nam_{EXPNAME}

The script either:

Backup copies are created:

Manual Execution

You can run the LAI estimation scripts manually for testing or batch processing:

Step 1: Estimate LAI

python3 scripts/python_scripts/estimate_lai.py \
    Cabauw \
    $OSVAS \
    --run-period 2017-11-01 2018-01-31

Parameters:

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
======================================================================

Step 2: Update Namelists

python3 scripts/python_scripts/update_namelist_lais.py \
    Cabauw \
    $OSVAS \
    --expnames DIFMEB_v9 DIFMEB_v9DSL

Parameters:

Output:

======================================================================
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
======================================================================

Validation and Quality Control

Data Quality Checks

The LAI estimation script performs the following QC checks:

  1. Valid LAI observations: Only observations with non-null LAI values > 0 are used
  2. Physical LAI range: Typical vegetation LAI in [0.1, 8.0]; values outside this may indicate data quality issues
  3. Geographic location: Scene only selected within the bounding box around the station (±0.005°, ~0.5 km)
  4. Temporal coverage: Monthly averages use all available 10-day observations (partial months acceptable)
  5. Quality flags (when available): CGLS quality flags are used to filter suspicious observations

Expected Ranges

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:

Diagnostic Output

The 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.

Troubleshooting

Issue: “No LAI data found for target year”

Causes:

Solution:

  1. Specify longer period: --run-period 2017-01-01 2018-12-31
  2. Use multi-year climatology: --start-year 2017 --end-year 2022
  3. Check connectivity to Copernicus backend:
    curl -s https://openeofed.dataspace.copernicus.eu | head
    

Issue: “OpenID authentication failed”

Causes:

Solution:

  1. Check internet connection
  2. Ensure Copernicus account is active (free registration at https://dataspace.copernicus.eu/)
  3. Re-run script (device code will be regenerated)
  4. If persistent, try device code URL in incognito/private browser window

Issue: “LAI values seem unrealistic”

Causes:

Solution:

  1. Check site vegetation type matches vegtype in YAML
  2. Examine satellite data visually if possible (CGLS browser: https://phenology.vgt.vito.be/)
  3. Use different period with better data coverage
  4. Compare with literature values for similar vegetation/climate

Issue: “No midday observations” or “Collection not found”

Causes:

Solution:

  1. List available collections: --list-collections
  2. Use BIOPAR (default): omit --collection argument
  3. Check backend availability: --backend https://openeofed.dataspace.copernicus.eu

Issue: Namelist update fails with “Could not extract block”

Causes:

Solution:

  1. Verify vegtype is consistent: check both estimate output and namelist
  2. Re-run LAI estimation: python3 scripts/python_scripts/estimate_lai.py [station] [osvas_root]
  3. Check LAI estimates file manually: cat namelists/{station}/lai_estimates.nam

Advanced Usage

Using Pre-computed LAI Values

If 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.

Using Alternative LAI Collections

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'

Creating Multi-station LAI Estimates

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

Temporal Coverage Analysis

To understand temporal coverage of satellite observations, check the output count of valid observations. Low counts may indicate:

If severe, consider:

References

See Also