scopeinpython.scope

The end-to-end SCOPE simulation wrapper: one LUT input row in, leaf optics (Fluspect-Cx) → soil (BSM or the bundled reference spectra) → optical BRDF (run_rtmo) → thermal energy balance (ebal) → fluorescence (rtmf, optional) → zeaxanthin (rtmz, optional) out. Direct, partial port of SCOPEinR::get.SCOPE.

Note

Verified against a real, unmodified SCOPEinR::get.SCOPE() call using the R package’s own bundled example inputs (SCOPEinR/inst/input/LUT_input.csv + setoptions.csv) – see python/scratch/scratch_scope_export.R. The exact-formula outputs (LAIsunlit/Pnsun_Cab/Pnsha_Cab/Pntot_Cab, the TOC reflectance spectrum) match to floating-point noise; the iterative-convergence outputs (temperatures, energy-balance totals) match at the same ~1-2% tolerance established for scopeinpython.ebal.ebal() itself, for the same reason (small floating-point divergence compounding over ~7-10 nonlinear iterations along a per-layer-loop biochemistry path – not a functional bug).

See the module docstring below, and Known limitations, for the full list of options.SCOPE branches this wrapper does not expose at all (directional BRDF, RTMt_planck, multi-layer mSCOPE, time-series mode, angle-file LIDF, measurement-file/MODTRAN irradiance) and which canopy-level “derived data products” beyond ScopeResult aren’t computed yet.

End-to-end SCOPE simulation wrapper: leaf optics -> soil -> optical BRDF -> thermal energy balance -> [fluorescence] -> [zeaxanthin], driven by a single LUT input row.

Direct, partial port of SCOPEinR::get.SCOPE (SCOPEinR/R/get.SCOPE.R), “SCOPE-lite” only (matches every other module in this port). Ties together fluspect_mscope(), get_bsm() (or the bundled reference soil spectra, SCOPEinR’s own default), run_rtmo(), ebal(), rtmf() and rtmz().

Not ported (same scope as documented for their own modules, or newly scoped out here):

  • get.SCOPE.parallel – R’s parallel-backend variant (foreach/ doParallel) of the same per-row loop below. Not a separate function here: parallelize get_scope() yourself (multiprocessing, joblib, a plain loop, …) – R’s parallel backend has no 1:1 Python equivalent worth porting.

  • options.calc_directional (full BRDF over many angles, get.brdf), options.calc_spectrum_planck (RTMt_planck.R, the per-wavelength thermal RTM), options.mSCOPE with more than one profile layer (run_rtmo only ever sees one leaf-optics spectrum per canopy layer – see its own docstring), options.simulation time-series mode, options.LIDF from an angle file (only LIDFa/LIDFb-derived LIDF is supported), and options.irradiance measurement-file / MODTRAN-atmosphere-file modes (only the bundled default spectrum, or a caller-supplied Esun_/Esky_) – none of these are exposed as parameters here at all, matching how e.g. scopeinpython.rtmt_sb.rtmt_sb() simply omits its own unported obsdir branch rather than accepting and silently ignoring the argument.

  • The canopy-level “derived data products” section of get.SCOPE.R beyond what’s listed on ScopeResultPnsun_Car/ Rnsun_Cab/Rnsun_PAR/LST/etc all need net-radiation or radiance breakdowns (Rnuc_Car, the directional-brightness-temperature Lote, …) that scopeinpython.rtmo.net_radiation_lite() and scopeinpython.rtmt_sb.rtmt_sb() don’t compute yet – see their own module docstrings for exactly what’s missing and why.

  • options.soil_heat_method 0/1 and options.calc_rss_rbs == 1 (recomputing rss/rbs from SMC/LAI via calc_rssrbs – not ported) – get_scope() always uses the LUT’s own rss/ rbs columns directly (R’s calc.rss_rbs == 0 default) and the simple G = 0.35*Rn soil-heat method (R’s soil_heat_method == 2 default), matching scopeinpython.ebal.ebal()’s own scope.

scopeinpython.scope.TDP_DEFAULT: dict = {'Q10': 2.0, 'delHaJ': 43540.0, 'delHaKc': 79430.0, 'delHaKo': 36380.0, 'delHaP': 53100.0, 'delHaR': 46390.0, 'delHaT': 37830.0, 'delHaV': 65330.0, 'delHdJ': 152040.0, 'delHdP': 150650.0, 'delHdR': 150650.0, 'delHdV': 149250.0, 'delSJ': 495.0, 'delSP': 490.0, 'delSR': 490.0, 'delSV': 485.0, 's1': 0.3, 's2': 313.15, 's3': 0.2, 's4': 288.15, 's5': 1.3, 's6': 328.15}

Fixed temperature-response coefficients for get_biochemical() (leafbio.TDP). Direct port of SCOPEinR::define_temp_response_biochem – always these exact constants, nothing here is derived from caller input.

scopeinpython.scope.get_zo_and_d(CR, CSSOIL, CD1, Psicor, LAI, hc, kappa)[source]

Roughness length for momentum (zom) and zero-plane displacement height (d), from vegetation height and LAI (Verhoef, McNaughton & Jacobs 1997). Direct port of SCOPEinR::get.zo_and_d (against the fixed R source: an undefined-variable bug in its degenerate-canopy branch, zo_and_d$d <- d with no d ever assigned on that branch – errors, or silently picks up a stale d left over from a previous call in the same R session – is fixed in SCOPEinR/R/zo_and_d.R to the evidently-intended d <- 0).

Returns:

(zom, d)

Return type:

tuple[float, float]

Parameters:
  • CR (float)

  • CSSOIL (float)

  • CD1 (float)

  • Psicor (float)

  • LAI (float)

  • hc (float)

  • kappa (float)

class scopeinpython.scope.ScopeOptions(calc_fluor=True, calc_xanthophyllabs=True, apply_t_corr=True, use_monin_obukhov=True, use_bsm_soil=False, k_maxit=100, maxEBer=1.0)[source]

Bases: object

Subset of R’s options.SCOPE this port actually implements (see module docstring for what isn’t exposed at all).

Parameters:
  • calc_fluor (bool)

  • calc_xanthophyllabs (bool)

  • apply_t_corr (bool)

  • use_monin_obukhov (bool)

  • use_bsm_soil (bool)

  • k_maxit (int)

  • maxEBer (float)

calc_fluor: bool = True
calc_xanthophyllabs: bool = True
apply_t_corr: bool = True
use_monin_obukhov: bool = True
use_bsm_soil: bool = False
k_maxit: int = 100
maxEBer: float = 1.0
class scopeinpython.scope.ScopeResult(rtmo: 'RTMoResult', ebal: 'EbalResult', rtmf: 'RTMfResult | None', rtmz: 'RTMzResult | None', nlayers: 'int', LAIsunlit: 'float', LAIshaded: 'float', Pnsun_Cab: 'float', Pnsha_Cab: 'float', Pntot_Cab: 'float', Ja: 'float', PNPQ: 'float', fqe: 'float | None')[source]

Bases: object

Parameters:
  • rtmo (RTMoResult)

  • ebal (EbalResult)

  • rtmf (RTMfResult | None)

  • rtmz (RTMzResult | None)

  • nlayers (int)

  • LAIsunlit (float)

  • LAIshaded (float)

  • Pnsun_Cab (float)

  • Pnsha_Cab (float)

  • Pntot_Cab (float)

  • Ja (float)

  • PNPQ (float)

  • fqe (float | None)

rtmo: RTMoResult
ebal: EbalResult
rtmf: RTMfResult | None
rtmz: RTMzResult | None
nlayers: int
LAIsunlit: float
LAIshaded: float
Pnsun_Cab: float
Pnsha_Cab: float
Pntot_Cab: float
Ja: float
PNPQ: float
fqe: float | None
scopeinpython.scope.get_scope(lut, options=None, spectral=None, Esun_=None, Esky_=None, rsoil=None)[source]

Run one full SCOPE simulation for a single LUT row. Direct port of the per-row body of SCOPEinR::get.SCOPE (see module docstring for exact scope).

Parameters:
  • lut (Mapping) – One row of SCOPE’s LUT_input.csv layout (a dict or pandas.Series with at least the columns used below – see SCOPEinR/inst/input/LUT_input.csv for the full reference set).

  • options (ScopeOptions)

  • spectral (SpectralConfig, optional) – Defaults to scopeinpython.spectral.get_spectra_scope().

  • Esun (array_like, shape (2162,), optional) – Top-of-atmosphere direct/diffuse irradiance, on spectral.wlS. Defaults to SCOPE’s own bundled example spectrum (R’s options.irradiance == 0 path).

  • Esky (array_like, shape (2162,), optional) – Top-of-atmosphere direct/diffuse irradiance, on spectral.wlS. Defaults to SCOPE’s own bundled example spectrum (R’s options.irradiance == 0 path).

  • rsoil (array_like, shape (2001,), optional) – Soil reflectance, 400-2400nm. If omitted, computed from options.use_bsm_soil and the LUT’s soil columns.

  • Esun_ (ndarray | None)

  • Esky_ (ndarray | None)

Return type:

ScopeResult