← RTM-Suite home

Model comparison & verification

RTM-Suite ships every model twice — once in R, once in Python. This page runs independent simulations from shared, randomly-sampled Look-Up Tables through both language implementations and compares them directly, plus one section that validates a model against real measured data rather than the other language. Coverage spans leaf optics, canopy radiative transfer, soil (dry-soil mixing and wet-soil MARMIT), the full soil-plant-atmosphere chain (SPART), and the full coupled SCOPE 2.1 model — the same robustness checks any user could re-run themselves, with the scripts linked in each section.

Leaf × canopy models Soil & atmosphere (SPART, MARMIT) Full SCOPE 2.1 Validation vs. real measured data 600-simulation deep dive Reproduce this yourself
15 / 15
leaf × canopy combinations, R²=1.0
3 / 3
soil/atmosphere models (SPART, MARMIT-1, MARMIT-2), R²=1.0
1 / 1
full SCOPE 2.1 pipeline, R²=1.0
R²=0.99
PROSPECT-D vs. 308 real measured leaves

R vs PythonLeaf × canopy models — all 15 combinations

Every leaf model (PROSPECT-D, PROSPECT-PRO, Liberty, Fluspect-B, Fluspect-B-Cx) run through every canopy model (fourSAIL, foursail2, INFORM) — 15 combinations, 100 simulations each from an independent randomly-sampled LUT per combination, full reflectance spectrum compared wavelength-by-wavelength. Liberty combinations show a small fraction of rows excluded on one side (89–93/100 valid) where an extreme random parameter draw pushed the iterative Liberty solver out of its valid domain in one language before the other — every row that did converge on both sides agrees to Liberty's own precision (RMSE ≈ 10-12, vs ≈10-16 for the other four leaf models).

Heatmap of R^2 between R and Python across all 5 leaf models x 3 canopy models, all cells 1.000000
15-panel grid of simulated reflectance spectra, R (black) vs Python (red dashed) fully overlapping, one panel per leaf x canopy model combination
Canopy modelLeaf modelValid simsRMSE
fourSAILPROSPECT-D100/1008.2 × 10-161.00000000
fourSAILPROSPECT-PRO100/1005.4 × 10-161.00000000
fourSAILLiberty89/1004.0 × 10-121.00000000
fourSAILFluspect-B100/1004.2 × 10-161.00000000
fourSAILFluspect-B-Cx100/1005.4 × 10-161.00000000
foursail2PROSPECT-D100/1004.3 × 10-161.00000000
foursail2PROSPECT-PRO100/1002.9 × 10-161.00000000
foursail2Liberty93/1005.3 × 10-121.00000000
foursail2Fluspect-B100/1003.3 × 10-161.00000000
foursail2Fluspect-B-Cx100/1003.2 × 10-161.00000000
INFORMPROSPECT-D100/1006.0 × 10-161.00000000
INFORMPROSPECT-PRO100/1003.3 × 10-161.00000000
INFORMLiberty92/1004.9 × 10-121.00000000
INFORMFluspect-B100/1003.6 × 10-161.00000000
INFORMFluspect-B-Cx100/1003.6 × 10-161.00000000

R vs PythonSoil & atmosphere — SPART, MARMIT-1, MARMIT-2

Beyond leaf/canopy optics: the full soil-plant-atmosphere chain (SPART, TOC and TOA reflectance for a real sensor) and wet-soil reflectance (MARMIT, both published versions).

SPART — soil-plant-atmosphere, Sentinel-2A bands

100 simulations, one shared LUT (getLUT(inputsSPART), seed 21), fixed realistic sea-level clear-sky atmosphere (matching the tutorial's own choice to avoid getLUT()'s known unphysical-atmosphere edge case at its widest default ranges). Both TOC canopy-BRDF reflectance and TOA (after SMAC atmospheric correction) compared, all 13 Sentinel-2A bands.

SPART TOC and TOA scatter plots (R vs Python, all on the 1:1 line) plus an example Sentinel-2A band spectrum overlay
ProductValid simsRMSE
TOC (canopy BRDF)100/1004.2 × 10-161.00000000
TOA (post SMAC atmospheric correction)100/1006.4 × 10-151.00000000

MARMIT — wet-soil reflectance, both model versions

60 random draws (soil ID 1–17 from the bundled Bablet_2016 database, water-layer thickness L 0.01–0.15 cm, wet-surface fraction eps 0.1–0.9), run through both MARMIT-1 (Bablet et al. 2018, the simpler original model) and MARMIT-2 (adds soil-particle refractive-index/size effects) in both languages.

MARMIT-1 and MARMIT-2 example wet-soil spectra, R vs Python fully overlapping
VersionValid simsRMSEMax abs diff
MARMIT-160/603.5 × 10-101.7 × 10-81.00000000
MARMIT-260/601.8 × 10-81.7 × 10-71.00000000

R vs PythonFull SCOPE 2.1

600 SCOPE simulations, one shared LUT (getLUT.SCOPE(), seed 7), default options — the full coupled soil/canopy/photosynthesis/energy-balance/fluorescence pipeline, not just radiative transfer. Top-of-canopy reflectance compared across 2,053 of 2,162 spectral-domain wavelengths (the remaining ~109 are a structural, non-contiguous gap matching atmospheric water-vapor absorption windows around 1400/1900/2500 nm — excluded from both sides rather than dropping otherwise-valid simulations).

Scatter plot of R vs Python mean TOC reflectance across 600 SCOPE simulations, and an example spectrum overlay
MetricValue
Simulations compared600 / 600
Wavelengths compared2,053 / 2,162
RMSE2.5 × 10-16
Max absolute difference4.4 × 10-15
Bias (mean Python − R)7.4 × 10-18
R² (Python predicting R)1.00000000

Real data not R vs PythonPROSPECT-D vs. 308 real measured leaves (Angers database)

Everything above checks that R and Python agree with each other — strong evidence the Python port is faithful, but not by itself evidence that either implementation matches reality. This section instead validates PROSPECT-D against the bundled Angers leaf-optics database (308 real leaves with lab-measured chlorophyll a+b, carotenoids, anthocyanin, equivalent water thickness and leaf mass per area, paired with lab-measured reflectance and transmittance spectra) — the same kind of dataset PROSPECT itself was calibrated and validated against in the original literature. Measured biochemical traits go in as-is; the mesophyll structure parameter N (not independently measurable) is fit per leaf over a 1.0–3.0 grid, the standard PROSPECT calibration procedure.

Four example leaves spanning a wide chlorophyll range: measured (black) vs PROSPECT-D simulated (red) reflectance and 1-transmittance, closely overlapping
Histogram of R^2 between simulated and measured spectra across all 308 real leaves, heavily concentrated near 1.0
MetricValue
Leaves validated308 / 308
Best-fit N (mesophyll structure)median 1.475, range 1.0–2.7 — matches published typical values
RMSE (R + T combined, 0–1 units)median 0.0147, 95th pct 0.0277
R² (simulated vs. measured)median 0.9912, mean 0.9875, worst leaf 0.9082

Liberty, Fluspect and INFORM don't have an equivalent bundled field/lab dataset in this repo, so this real-data check currently covers PROSPECT-D only (the leaf model both the Angers database and the wider PROSPECT literature target directly).

R vs PythonPROSPECT-D + fourSAIL, 600-simulation deep dive

A larger single-combination run for extra statistical depth on the most commonly used pairing: 600 simulations, one shared LUT (getLUT(), seed 42), full 400–2500 nm reflectance spectrum compared wavelength-by-wavelength.

Scatter plot of R vs Python mean reflectance across 600 simulations, and an example spectrum overlay, for ToolsRTM/toolsrtm
MetricValue
Simulations compared600 / 600
RMSE (all wavelengths × all sims)8.1 × 10-16
Max absolute difference1.2 × 10-14
Bias (mean Python − R)1.8 × 10-16
R² (Python predicting R)1.00000000

Reproduce this yourself

Each comparison is two scripts under Scripts/Comparison/: an R script builds the shared LUT and the R-side reference output, and a companion Python script reads that same LUT, runs the Python port, and produces the figure + error stats above.

# Leaf x canopy models, all 15 combinations
Rscript Scripts/Comparison/compare_R_Python_allmodels_600sims.R
python Scripts/Comparison/compare_R_Python_allmodels_600sims.py

# SPART + MARMIT (both versions)
Rscript Scripts/Comparison/compare_R_Python_spart_marmit.R
python Scripts/Comparison/compare_R_Python_spart_marmit.py

# Full SCOPE 2.1
Rscript Scripts/Comparison/compare_R_Python_scope_600sims.R
python Scripts/Comparison/compare_R_Python_scope_600sims.py

# PROSPECT-D + fourSAIL, 600-sim deep dive
Rscript Scripts/Comparison/compare_R_Python_toolsrtm_600sims.R
python Scripts/Comparison/compare_R_Python_toolsrtm_600sims.py

# PROSPECT-D vs. real measured leaves (Angers database, R only -- no Python port of this validation)
Rscript Scripts/Comparison/validate_prospectD_vs_angers.R

This is a large-sample statistical comparison, complementary to the existing per-function precision verification (single test cases, checked to floating-point precision, 50 automated tests).