This document describes the albedo estimation workflow in OSVAS, which automatically estimates monthly-averaged surface albedo values (NIR and VIS spectra) from validation radiation data for SURFEX simulations.
SURFEX requires monthly surface albedo parameters for both vegetation and soil surfaces across the NIR and VIS spectral bands:
XUNIF_ALBNIR_VEG - Vegetation near-infrared albedo (12 monthly values)XUNIF_ALBVIS_VEG - Vegetation visible albedo (12 monthly values)XUNIF_ALBNIR_SOIL - Soil near-infrared albedo (12 monthly values)XUNIF_ALBVIS_SOIL - Soil visible albedo (12 monthly values)Rather than using generic parameterizations, the albedo estimation workflow derives these values from actual observations at the validation site, improving model realism for the study location.
Surface albedo is calculated as:
\[\alpha = \frac{SW_{OUT}}{SW_{IN}}\]where:
Monthly albedos are computed as the average of daily mean albedos within each calendar month during the validation 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_albedo: true # ← Set to true to enable albedo estimation
Make sure validation data download is enabled and configured with SW_OUT and SW_IN variables:
OSVAS_steps:
Get_validation: true # ← Must be true
Validation_data:
validation_start: '2017-11-01 00:00:00'
validation_end: '2018-01-31 23:59:00'
dataset1:
doi: https://api.dataplatform.knmi.nl/.../cesar_surface_radiation_lc1_t10/...
timedelta: 10
variables:
SW_OUT: SWU # ← Required
SW_IN: SWD # ← Required
LW_OUT: LWU
LW_IN: LWD
When estimate_albedo: true is set and validation data downloading is enabled, the albedo estimation executes automatically as Step 2b in the OSVAS workflow:
Step 1: Create forcing data
Step 2: Get validation data
Step 2b: ✨ Estimate surface albedos from validation data (NEW)
├─ Read SW_OUT/SW_IN from SQLite OBSTABLEs
├─ Select midday observations (11:00-13:00 UTC)
├─ Calculate daily albedos
├─ Compute monthly averages
├─ Generate namelist format blocks
└─ Update all experiment namelists
Step 3: Run SURFEX simulations (with updated albedos)
Step 4: Extract model outputs
...
Location: namelists/{STATION_NAME}/albedo_estimates.nam
Content: Formatted namelist blocks ready to be included in SURFEX namelists:
! Estimated albedos from validation data (SW_OUT/SW_IN 11:00-13:00 UTC)
! Monthly averages for vegetation and soil surfaces
! Vegetation NIR albedo (monthly)
XUNIF_ALBNIR_VEG(10,1) = 0.19024633,
XUNIF_ALBNIR_VEG(10,2) = 0.18576372,
...
XUNIF_ALBNIR_VEG(10,12) = 0.1538921,
! Vegetation VIS albedo (monthly)
XUNIF_ALBVIS_VEG(10,1) = 0.19024633,
...
XUNIF_ALBVIS_VEG(10,12) = 0.1538921,
! Soil NIR albedo (monthly)
XUNIF_ALBNIR_SOIL(10,1) = 0.19024633,
...
! Soil VIS albedo (monthly)
XUNIF_ALBVIS_SOIL(10,1) = 0.19024633,
...
Location: namelists/{STATION_NAME}/OPTIONS.nam_{EXPNAME}
The script replaces the existing albedo blocks with the estimated values. Backup copies are created:
OPTIONS.nam_{EXPNAME}.backup - Original namelist before updateYou can also run the albedo estimation scripts manually for testing or batch processing:
python3 scripts/python_scripts/estimate_albedo.py \
Cabauw \
$OSVAS \
--validation-period 2017-11-01 2018-01-31
Parameters:
station_name: Station name (e.g., Cabauw, Loobos)osvas_root: Path to OSVAS root directory--validation-period START_DATE END_DATE (optional): Only process data within this period (YYYY-MM-DD format)--output PATH (optional): Save estimates to custom path (default: namelists/{station}/albedo_estimates.nam)Output:
======================================================================
Estimating surface albedos for Cabauw
======================================================================
Step 1: Reading validation data from OBSTABLEs
Found 3 OBSTABLE files
Read: OBSTABLE_2017.sqlite
Read: OBSTABLE_2018.sqlite
Read: OBSTABLE_2019.sqlite
Loaded 8760 records with valid SW_OUT/SW_IN
Step 2: Selecting midday observations (11:00-13:00 UTC)
Selected 1460 records between 11:00-13:00 UTC
Step 3: Calculating daily albedos (SW_OUT/SW_IN)
Calculated 365 daily average albedos
Step 4: Computing monthly averages
Monthly albedo averages:
Month 1: 0.34562891
Month 2: 0.32891234
...
Month 12: 0.35123456
Step 5: Generating namelist format
Using vegtype: 10
Step 6: Saving results
✅ Albedo estimates saved to: /home/user/OSVAS/namelists/Cabauw/albedo_estimates.nam
NIR albedo: min=0.12345678, max=0.45678901, mean=0.28901234
VIS albedo: min=0.12345678, max=0.45678901, mean=0.28901234
======================================================================
✅ Albedo estimation completed successfully
======================================================================
python3 scripts/python_scripts/update_namelist_albedos.py \
Cabauw \
$OSVAS \
--expnames DIFMEB_v9 DIFMEB_v9DSL \
--albedo-file namelists/Cabauw/albedo_estimates.nam
Parameters:
station_name: Station nameosvas_root: OSVAS root directory--expnames EXP1 EXP2 ... (optional): Experiment names to update (default: read from config YAML)--albedo-file PATH (optional): Path to albedo estimates (default: namelists/{station}/albedo_estimates.nam)--no-backup (optional): Don’t create backup copiesOutput:
======================================================================
Updating namelists with estimated albedos for Cabauw
======================================================================
Using experiments from config: DIFMEB_v9, DIFMEB_v9DSL
Step 1: Reading albedo estimates
Read albedo estimates from: namelists/Cabauw/albedo_estimates.nam
Step 2: Updating experiment namelists
Processing: DIFMEB_v9
✓ Found XUNIF_ALBNIR_VEG block
✓ Found XUNIF_ALBVIS_VEG block
✓ Found XUNIF_ALBNIR_SOIL block
✓ Found XUNIF_ALBVIS_SOIL block
✓ Backup created: OPTIONS.nam_DIFMEB_v9.backup
✓ Updated XUNIF_ALBNIR_VEG
✓ Updated XUNIF_ALBVIS_VEG
✓ Updated XUNIF_ALBNIR_SOIL
✓ Updated XUNIF_ALBVIS_SOIL
✓ Namelist updated: namelists/Cabauw/OPTIONS.nam_DIFMEB_v9
Processing: DIFMEB_v9DSL
...
======================================================================
✅ Updated 2/2 experiment namelists
======================================================================
The albedo estimation script performs the following QC checks:
Realistic albedo values depend on surface type and season:
| Surface Type | Season | NIR Albedo Range | VIS Albedo Range |
|---|---|---|---|
| Dense vegetation (grass) | Summer | 0.30-0.45 | 0.15-0.25 |
| Dense vegetation (grass) | Winter | 0.25-0.35 | 0.12-0.20 |
| Crops | Growing | 0.25-0.35 | 0.15-0.20 |
| Crops | Harvest | 0.25-0.40 | 0.12-0.25 |
| Bare soil | Wet | 0.15-0.25 | 0.08-0.15 |
| Bare soil | Dry | 0.25-0.40 | 0.15-0.25 |
If estimated albedos fall outside reasonable ranges, check:
The script prints monthly albedo values:
Monthly albedo averages:
Month 1: 0.28905634
Month 2: 0.29123456
Month 3: 0.30456789
...
Plot these values to identify seasonal patterns and anomalies:
import matplotlib.pyplot as plt
months = range(1, 13)
albedo = [0.289, 0.291, 0.304, ...] # from output
plt.plot(months, albedo, 'o-')
plt.xlabel('Month')
plt.ylabel('Albedo')
plt.title('Seasonal albedo cycle')
plt.show()
Causes:
Get_validation: false)Solution:
ls sqlites/OBSTABLES/validation_data/{STATION_NAME}/sqlite3 sqlites/OBSTABLES/validation_data/{STATION_NAME}/OBSTABLE_YYYY.sqlite "SELECT COUNT(*), COUNT(SW_OUT), COUNT(SW_IN) FROM SYNOP;"Causes:
Solution:
estimate_albedo.py (default: 11:00-13:00 UTC)sqlite3 sqlites/OBSTABLES/validation_data/{STATION_NAME}/OBSTABLE_YYYY.sqlite "SELECT min(valid_dttm), max(valid_dttm) FROM SYNOP;" | awk '{print strftime("%Y-%m-%d %H:%M:%S", $1), strftime("%Y-%m-%d %H:%M:%S", $2)}'Causes:
Solution:
Causes:
Station_metadata.vegtypeSolution:
vegtype matches in both namelist and YAML configgrep XUNIF_ALB namelists/{STATION_NAME}/OPTIONS.nam_*By default, the script uses the same albedo value for both NIR and VIS bands. To differentiate them (e.g., vegetation typically has higher NIR than VIS albedo), modify estimate_albedo.py:
In the main() function, replace:
nir_albedos = monthly_albedos
vis_albedos = monthly_albedos
With a differentiation factor:
# Vegetation typically has higher NIR albedo than VIS
# Use a typical ratio for grass/crop (e.g., NIR = 2.0 × VIS)
nir_ratio = 1.8 # NIR/VIS ratio
vis_albedos = monthly_albedos
nir_albedos = [a * nir_ratio for a in vis_albedos]
Then rerun the scripts:
python3 scripts/python_scripts/estimate_albedo.py Cabauw $OSVAS
python3 scripts/python_scripts/update_namelist_albedos.py Cabauw $OSVAS
By default, soil and vegetation use the same estimated values. To differentiate them (e.g., soil typically darker than vegetation), modify the namelist generation in estimate_albedo.py:
# Different soil albedo (e.g., 80% of vegetation albedo)
soil_nir_albedos = [a * 0.8 for a in nir_albedos]
soil_vis_albedos = [a * 0.8 for a in vis_albedos]
To estimate albedos for all stations in batch:
for station in Cabauw Loobos Majadas_del_tietar Meteopole; do
echo "Processing $station..."
python3 scripts/python_scripts/estimate_albedo.py $station $OSVAS
python3 scripts/python_scripts/update_namelist_albedos.py $station $OSVAS
done