05. Soil & Atmosphere

What you will learn

  • What MARMIT, BSM, SMAC, and SPART each simulate.

  • How soil moisture changes a soil reflectance spectrum, and how much that matters relative to the canopy above it.

  • Why top-of-canopy (TOC) and top-of-atmosphere (TOA) reflectance diverge sharply at specific wavelengths.

Concept

Two independent soil models feed two different canopy/atmosphere chains:

Model

What it represents

Used by

MARMIT

Starts from a real dry reference soil spectrum and adds a physically modelled liquid-water film, so the same soil can be simulated at any moisture level.

Standalone, or as rsoil into any canopy model (04. Canopy Radiative Transfer Models).

BSM (Brightness-Shape-Moisture)

Builds a soil spectrum from three empirical parameters (BSMBrightness, BSMlat, BSMlon) plus a wetting term – no reference spectrum needed.

Built into spart_toa().

SMAC

Atmospheric radiative transfer (gas absorption + aerosol scattering) converting TOC reflectance into what a sensor measures above the atmosphere.

Built into spart_toa().

SPART

The full chain: BSM soil -> fourSAIL canopy -> SMAC atmosphere -> TOA reflectance, already resampled to a real sensor’s bands.

spart_toa().

Python tools used

Function

Key arguments

get_marmit_rsoil()

soil_id (dry reference spectrum), L (water-film thickness, cm), eps (roughness). Returns .rsoil_dry/.rsoil_wet (2101-element each) and .smc (derived soil moisture content).

spart_toa()

Trait dict (leaf + canopy + atmosphere: Pa, aot550, uo3, uh2o), sensor (a SmacSensor, e.g. sentinel2a_msi()), leaf_model, plus BSM soil kwargs (BSMBrightness, BSMlat, BSMlon, SMp). Returns .wl_smac (band centers), .rfl_toc_brdf, .rfl_toa – one value per sensor band, not the full 1nm spectrum.

Run the example

from toolsrtm import get_marmit_rsoil, spart_toa, sentinel2a_msi

# 1. MARMIT: dry -> wet soil spectrum
soil = get_marmit_rsoil(soil_id=3, L=0.05, eps=0.4, version="marmit1")
print("Soil moisture content:", float(soil.smc))
for wl in (550, 850, 1600):
    i = wl - 400
    print(f"{wl}nm: dry={soil.rsoil_dry[i]:.4f}  wet={soil.rsoil_wet[i]:.4f}")

# 2. SPART: BSM soil -> fourSAIL canopy -> SMAC atmosphere -> TOA
result = spart_toa(
    dict(N=1.5, Cab=40, Car=8, Anth=1, Cbrown=0, EWT=0.01, LMA=0.009, alpha=40,
         Prot=0.002, CBC=0.007, LIDFa=-0.35, LIDFb=-0.15, TypeLidf=1,
         LAI=3, hspot=0.01, tts=30, tto=0, psi=0,
         Pa=1000, aot550=0.3246, uo3=0.348, uh2o=1.4116),
    sensor=sentinel2a_msi(), leaf_model="PROSPECT-PRO",
    BSMBrightness=0.5, BSMlat=25, BSMlon=45, SMp=15,
)
print("Sentinel-2A bands (nm):", result.wl_smac)
print("TOC:", result.rfl_toc_brdf.round(4))
print("TOA:", result.rfl_toa.round(4))

Result

Printed output (exact, deterministic):

Soil moisture content: 39.900691712115204
550nm: dry=0.1316  wet=0.1064
850nm: dry=0.2608  wet=0.2151
1600nm: dry=0.4758  wet=0.3178
Sentinel-2A bands (nm): [ 445.  520.  560.  654.  701.  743.  779.  789.  871.  942. 1372. 1639. 2256.]
TOC: [0.0159 0.0414 0.0556 0.0238 0.0662 0.3305 0.4021 0.4032 0.4064 0.4029 0.2806 0.2248 0.0805]
TOA: [0.1182 0.1057 0.0899 0.0488 0.0803 0.3104 0.3846 0.3679 0.3904 0.1215 0.0022 0.2091 0.0736]
Dry vs wet soil reflectance spectrum from MARMIT, real output of the code above

Real output: MARMIT’s dry-reference vs. wetted soil reflectance – the SWIR water-absorption dips (~1400/1900nm) deepen and overall brightness drops as the soil wets.

SPART TOC and TOA reflectance across Sentinel-2A bands, real output of the code above

Real output: TOC and TOA reflectance diverge sharply in the water-vapour bands (~940/1370nm), where the atmosphere absorbs most of the signal before it reaches the sensor.

Interpretation

Wetting the soil (MARMIT) lowers reflectance everywhere, but not by an equal fraction: at 1600nm (a SWIR water-absorption region) the drop is large (0.476 -> 0.318, -33%), while at 550nm (visible) it’s smaller (0.132 -> 0.106, -19%) – soil moisture leaves its strongest fingerprint in the SWIR, the same spectral region leaf equivalent-water-thickness (EWT, 02. Parameters & Traits) affects, for the same physical reason (liquid water absorbs strongly there).

The TOC-vs-TOA comparison from SPART tells a different, atmosphere-driven story: at most bands TOC and TOA are reasonably close (e.g. 779nm: 0.403 vs. 0.385), but at the two bands SPART itself sits on real water-vapour absorption features (942nm, 1372nm), the atmosphere removes most of the signal before it reaches “space” – TOC 0.403 collapses to TOA 0.122 at 942nm, and TOC 0.281 collapses to a near-zero 0.002 at 1372nm. This is exactly why real sensors either avoid placing bands there (Sentinel-2’s narrow B9/B10 bands specifically target these features for atmospheric correction, not surface retrieval) or need real atmospheric correction before the data is usable for vegetation analysis.

Try it yourself

  • Raise L (MARMIT) from 0.05 to 0.15 (much wetter) and see how much further the SWIR dips deepen.

  • Change aot550 (aerosol optical thickness) from 0.32 to 0.05 (clear sky) and compare how much the visible-band TOA values shift – aerosol scattering matters most in the blue/visible, not the SWIR.

  • Feed MARMIT’s soil.rsoil_wet directly as rsoil into 04. Canopy Radiative Transfer Models’s foursail() call, and compare the resulting TOC reflectance against the flat rsoil=0.15 baseline used there.

Common mistakes

  • MARMIT and BSM are two independent soil models – spart_toa always uses BSM internally (BSMBrightness/BSMlat/BSMlon/ SMp), not whatever MARMIT spectrum you may have built separately.

  • spart_toa returns one value per sensor band (13 for Sentinel-2A), not the full 2101-element native spectrum – rfl_toc_brdf/rfl_toa are already resampled.

  • Atmospheric water-vapour bands can swing from a plausible reflectance value at TOC to near-zero at TOA – a real atmospheric effect, not a sign the simulation broke.

Next

06. SCOPE – SCOPE, the ecosystem-scale model that replaces the “soil brightness + fixed temperature” shortcuts above with a real coupled energy balance.


Using R? -> ToolsRTM Tutorial 03: SPART and Tutorial 16: MARMIT + fourSAIL + SPART