Skip to content

Surfaces API ​

Surface models are implemented in CoreRT because they form the lower boundary of the adding-doubling column.

Surface Types ​

vSmartMOM.CoreRT.AbstractSurfaceType Type

Abstract Type for Surface Types

source
vSmartMOM.CoreRT.LambertianSurfaceScalar Type
julia
LambertianSurfaceScalar{FT} <: AbstractSurfaceType

Isotropic Lambertian surface with a single scalar albedo α shared across all spectral points in a band. The BRDF is ρ = α/π, independent of viewing and illumination angles. Only the m = 0 Fourier moment is nonzero.

Supports analytical Jacobians with respect to albedo in linearized RT.

Fields

  • albedo::FT: hemispherical albedo ∈ [0, 1].
source
vSmartMOM.CoreRT.LambertianSurfaceSpectrum Type
julia
LambertianSurfaceSpectrum{FT} <: AbstractSurfaceType

Isotropic Lambertian surface with a per-spectral-point albedo vector. The vector length must match the number of spectral points in the band.

Fields

  • albedo::AbstractArray{FT,1}: spectral albedo vector, one value per grid point.
source
vSmartMOM.CoreRT.LambertianSurfaceLegendre Type

Defined by Legendre polynomial terms as function of spectral grid, which is scaled to [-1,1] (degree derived from length of a_coeff)

source
vSmartMOM.CoreRT.LambertianSurfaceSpline Type

Defined by a simple spline from Interpolations.jl

source
vSmartMOM.CoreRT.rpvSurfaceScalar Type
julia
rpvSurfaceScalar{FT} <: AbstractSurfaceType

Rahman-Pinty-Verstraete (1993) four-parameter empirical BRDF model. Combines a Minnaert angular shape, a Henyey-Greenstein-like hot-spot function, and a geometric bowl/bell correction. Scalar only (Stokes-I); no built-in linearization.

Fields

  • ρ₀: Overall reflectance level parameter (scalar)

  • ρ_c: Hotspot function parameter (1.0 = no hotspot)

  • k: Anisotropy shape parameter. k < 1.0 (> 1.0) corresponds to a bowl (bell) shape.

  • Θ: Asymmetry parameter, Θ < 0.0 (> 0.0) corresponds to a predominantly backward (forward) scattering.

source
vSmartMOM.CoreRT.RossLiSurfaceScalar Type
julia
RossLiSurfaceScalar{FT} <: AbstractSurfaceType

Ross-Li kernel-based BRDF (Lucht, Schaaf & Strahler, 2000). Decomposes the surface reflectance into three semi-physical kernels: isotropic, RossThick (volumetric canopy scattering), and LiSparse (geometric mutual shadowing). Standard MODIS/RAMI parameterization. Scalar only (Stokes-I); no built-in linearization.

By default the incident and reflected zenith angles are capped at 80° before the kernels are evaluated (zenith_cap_deg = 80), a vSmartMOM regularization of grazing quadrature nodes, where the LiSparse kernel becomes large and negative. zenith_cap_deg = nothing evaluates the kernels at the true angles, as MODIS (MCD43) and VLIDORT do.

RossLiSurfaceScalar(fvol, fgeo, fiso; zenith_cap_deg = 80)

Fields

  • fvol: Volumetric RossThick fraction

  • fgeo: Geometric LiSparse fraction

  • fiso: Isotropic reflectance fraction

  • zenith_cap_deg: Kernel zenith-angle cap [deg]; nothing = no cap (MODIS/VLIDORT definition)

source
vSmartMOM.CoreRT.CoxMunkSurface Type
julia
CoxMunkSurface{FT} <: AbstractSurfaceType

Cox-Munk (1954) ocean surface: Fresnel reflection from wind-roughened wave facets. Supports full polarization (Mueller matrix) and optional Lambertian whitecap contribution.

Isotropic Gaussian slope distribution with total mean-square slope ⟨zx² + zy²⟩ = 0.003 + 0.00512·U (Cox & Munk 1954), i.e. per-axis variance σ² = ½(0.003 + 0.00512·U); see CoreRT.wind_to_sigma2.

With n_water = nothing the solvers use the Segelstein (1981) index (water_refractive_index) at the wavelength of every spectral point; a vector n_water is used element-wise and must have one entry per spectral point of the band. The exact direct-solar glint correction (apply_ss_correction!) is applied at TOA only; interior observer levels carry the Fourier-series glint.

Fields

  • wind_speed: 10-m wind speed [m/s]

  • n_water: Complex refractive index n + ik of water: nothing → Segelstein (1981) at each spectral point's wavelength; a scalar for the whole band; or a vector with one entry per spectral point

  • whitecap_albedo: Whitecap Lambertian albedo (Koepke 1984)

  • include_whitecaps: Include whitecap contribution

  • shadowing: Include Smith (1967) shadow masking

source
vSmartMOM.CoreRT.CanopySurface Type
julia
CanopySurface{FT} <: AbstractSurfaceType

Composite lower boundary: a vegetation canopy (one or more scattering layers) backed by a soil BRDF. When used as the surface in rt_run, the canopy sub-layers are processed internally via the adding-doubling method before interacting with the soil, producing an effective canopy+soil reflectance.

The direct beam enters the effective layer only through its reflected source (j₀⁺ = 0), so rt_run's BHR-down output bhr_dw is the diffuse downwelling flux for a canopy (no direct solar term), and the forward BOA radiance carries no collimated beam at a view on the solar ordinate. TOA radiances include the reflected direct beam.

Requires CanopyOptics.jl for leaf-angle distribution and scattering models.

source
vSmartMOM.CoreRT.CanopySurface_from_prospect Function
julia
CanopySurface_from_prospect(leaf_prospect, prospect_grid; soil, LAI, kwargs...)

Convenience constructor: compute spectral leaf R/T from PROSPECT and build a CanopySurface with the spectral leaf optics on the PROSPECT wavelength grid. If LAD is omitted, CanopySurface uses CanopyOptics.spherical_leaves().

Arguments

  • leaf_prospect: a CanopyOptics.LeafProspectProProperties (N, Ccab, Ccar, ...)

  • prospect_grid: wavelength range in nm (default 400:1:2500)

  • Remaining keyword arguments are forwarded to CanopySurface(...).

source

Surface Construction and Optics Helpers ​

vSmartMOM.CoreRT.create_surface_layer! Function
julia
create_surface_layer!(lambertian, added_layer, SFI, m, pol_type,
                      quad_points, τ_sum, architecture)

Computes (in place) surface optical properties for any Lambertian surface flavour (LambertianSurfaceScalar, LambertianSurfaceSpectrum, LambertianSurfaceLegendre, LambertianSurfaceSpline) as an AddedLayer. The four differ only in how the spectral albedo is produced (surface_albedo); the scaffold — including the m>0 / j₀⁺ conventions documented at the top of this file — is shared.

- `lambertian` a Lambertian surface struct (one of the four flavours)
- `SFI` bool if SFI (Source Function Integration) is used
- `m` Fourier moment (starting at 0)
- `pol_type` Polarization type struct
- `quad_points` Quadrature points struct
- `τ_sum` total optical thickness from TOA to the surface
- `architecture` Compute architecture (GPU,CPU)
source
julia
create_surface_layer!(RS_type, lambertian, added_layer, added_layer_lin, iparam, 
                      SFI, m, pol_type, quad_points, τ_sum, τ̇_sum, architecture)

Compute surface reflection/transmission matrices and their derivatives for a scalar Lambertian surface.

Sets the added_layer forward matrices and added_layer_lin derivative matrices for the surface "layer", including the source function contribution from the solar beam attenuated through the full atmosphere.

Arguments

  • RS_type::noRS: No Raman scattering.

  • lambertian::LambertianSurfaceScalar: Surface with scalar albedo .

  • added_layer::AddedLayer: Output forward matrices (modified in-place).

  • added_layer_lin::AddedLayerLin: Output linearized matrices (modified in-place).

  • iparam::Int: Parameter index for the surface albedo derivative.

  • SFI::Bool: Source Function Integration flag.

  • m::Int: Fourier moment (only   has nonzero surface contribution).

  • pol_type: Polarization type.

  • quad_points: Quadrature points and weights.

  • τ_sum: Total optical depth from TOA to surface [nSpec].

  • τ̇_sum: Derivative of total τ [nSpec × Nparams].

  • architecture: CPU or GPU.

source

Linearized Lambertian-Legendre surface. Each coefficient is one consecutive surface state-vector element, so iparams[k] stores ∂y/∂ρₖ₋₁.

source
julia
create_surface_layer!(brdf::AbstractSurfaceType, added_layer, SFI, m,
                      pol_type, quad_points, τ_sum, architecture)

Build an AddedLayer from any analytic BRDF that implements reflectance(brdf, stokes_index, μᵢ, μᵣ, Δϕ). RPV and Ross-Li specialize that scalar interface; this shared method performs the Fourier integration and operator assembly.

source
julia
create_surface_layer!(canopy::CanopySurface, added_layer, SFI, m, pol_type,
                      quad_points, τ_sum, architecture;
                      spec_bands_wn=nothing, m_max=2, F₀=nothing,
                      soil_spec_wn=spec_bands_wn)

Compute the effective canopy+soil surface layer for Fourier moment m. F₀ is the (nStokes, nSpec) solar Stokes vector at TOA used by both the leaf and soil source terms (nothing: unit unpolarized beam). m_max is the Fourier loop bound in order semantics.

When spectral leaf optics are provided, spec_bands_wn (the concatenated computation wavenumber grid) is required for initializing the spectral cache on first call. soil_spec_wn (default spec_bands_wn) is the grid on which a soil with spectral properties is resolved; solvers that do not set up the spectral canopy cache pass only this one.

Internally:

  1. Compute Z-matrices from the cached analytic stack (or coarse-grid interpolation).

  2. For each canopy sub-layer: build CoreDirectionalScatteringOpticalProperties, run elemental! (canopy kernel) + doubling!.

  3. Accumulate sub-layers via interaction!.

  4. Create soil surface layer and interact with the canopy composite. A soil with spectral properties (a CoxMunkSurface with n_water = nothing) is resolved on soil_spec_wn first.

  5. Copy the resulting R⁻⁺ and J₀ into the output added_layer.

The direct beam enters only through the reflected source j₀⁻; the layer's j₀⁺ is zero. The BHR-down diagnostic (bhr_dw of rt_run) is therefore the diffuse downwelling flux at the canopy top, without the direct solar flux, and the forward BOA radiance carries no collimated beam at a view on the solar ordinate. TOA radiances, hdr and bhr_uw include the reflected direct beam.

TODO: Canopy + Raman (RRS/VS) coupling is not currently implemented. The

canopy BRDF code is noRS-only; calling rt_run with CanopySurface + RRS/VS

has undefined behavior (the inelastic ieR/ieT branches in the kernels

never see a canopy-adapted contribution and will return garbage or crash).

Future work: couple the canopy path to the inelastic path — this is a

separate project, not part of the sanghavi-unified merge.

source
julia
create_surface_layer!(RS_type, canopy::CanopySurface, added_layer, added_layer_lin,
                      iparam, SFI, m, pol_type, quad_points, τ_sum, τ̇_sum, F₀, architecture;
                      soil_spec_wn = nothing)

Linearized surface dispatch for CanopySurface. Computes the forward canopy+soil reflectance (same as the non-linearized dispatch) and the derivative of the soil albedo parameter at iparam via finite differences. soil_spec_wn is forwarded to the forward builder (spectral soil properties).

Canopy-specific parameter derivatives (LAI, leaf R/T) are not yet implemented in the linearized path; their Jacobian slots should be populated via the hybrid AD boundary (ForwardDiff through canopy optical properties).

source
julia
create_surface_layer!(surf::CoxMunkSurface, added_layer, SFI, m, pol_type,
                      quad_points, τ_sum, architecture; F₀ = nothing)

Cox–Munk surface layer for Fourier moment m. With one refractive index for the whole band (scalar n_water, or a vector with equal entries) this is the shared analytic-surface assembly: one reflectance matrix for every spectral point. A spectrally varying n_water (one entry per spectral point; resolve n_water = nothing first with _resolve_spectral_surface) gives a reflectance matrix per spectral point: the Fourier reflectance is evaluated at the knots of _n_water_knots and interpolated on the device to the other points, and the reflected direct beam j₀⁻ uses each point's matrix.

source
julia
create_surface_layer!(RS_type::noRS, surf::CoxMunkSurface, added_layer, added_layer_lin,
                      iparam, SFI, m, pol_type, quad_points, τ_sum, τ̇_sum, F₀, architecture)

Compute forward + linearized surface reflection for a Cox-Munk surface.

The surface parameter is wind speed. The derivative ∂R_surf/∂U is computed analytically via the derivative chain through the slope PDF, shadow factor, and whitecap fraction — all sharing the wind-speed-independent Fresnel and geometry computations with the forward evaluation.

A spectrally varying n_water is handled as in the forward builder: the reflectance and its wind derivative are evaluated at the knots of _n_water_knots and interpolated with the same weights, so forward and Jacobian use the same per-point operator.

source
vSmartMOM.CoreRT.water_refractive_index Function
julia
water_refractive_index(λ_nm)

Complex refractive index n + i k (k ≥ 0) of liquid water at 25 °C and vacuum wavelength λ_nm (nanometres), from Segelstein (1981).

Between the tabulated wavelengths, n and k are interpolated linearly in wavelength. The table spans 33.96 nm to 10 m; a wavelength outside that range throws an ArgumentError.

Returns a Complex{Float64}. CoxMunkSurface(n_water = nothing) evaluates it at the wavelength of every spectral point.

Reference: D. J. Segelstein, "The complex refractive index of water", M.S. thesis, University of Missouri–Kansas City (1981).

source
vSmartMOM.CoreRT.fresnel_coefficients Function
julia
fresnel_coefficients(n_rel, cos_θᵢ)

Complex Fresnel amplitude reflection coefficients for s- and p-polarization.

Arguments

  • n_rel::Complex: relative refractive index (n_transmitted / n_incident)

  • cos_θᵢ::Real: cosine of the incidence angle

Returns

  • (r_s, r_p): complex amplitude coefficients
source
vSmartMOM.CoreRT.fresnel_mueller Function
julia
fresnel_mueller(r_s, r_p, n_stokes)

Mueller matrix for Fresnel reflection given complex amplitude coefficients, in the facet basis (ŝ, p̂ = k × ŝ) with ŝ normal to the plane of reflection. Stokes parameters follow vSmartMOM's convention for a basis (e₁, e₂) = (ŝ, p̂): Q = |E₁|² − |E₂|², U = 2Re(E₁E₂_), V = 2Im(E₁E₂_), with the refractive index n = n_r + i n_i (n_i ≥ 0). For n_stokes = 4 (IQUV):

M = [(|rs|²+|rp|²)/2   (|rs|²-|rp|²)/2    0              0           ]
    [(|rs|²-|rp|²)/2   (|rs|²+|rp|²)/2    0              0           ]
    [0                  0                   Re(rs·rp*)    -Im(rs·rp*)  ]
    [0                  0                   Im(rs·rp*)     Re(rs·rp*)  ]

The U–V elements follow from U' + iV' = 2 E₁'E₂'* = (rs rp*)(U + iV); this sign is confirmed against VLIDORT 2.8.3's complex-index Cox–Munk kernel (test_coxmunk_polarization.jl).

source
vSmartMOM.CoreRT.stokes_rotation_matrix Function
julia
stokes_rotation_matrix(φ, n_stokes)

Rotation matrix L(φ) for the Stokes reference plane.

Rotates the Stokes vector reference plane by angle φ (radians). Convention matches vSmartMOM's D = [1, 1, -1, -1]:

L = [1    0       0      0]
    [0   cos2φ   sin2φ   0]
    [0  -sin2φ   cos2φ   0]
    [0    0       0      1]
source
vSmartMOM.CoreRT.invalidate_canopy_cache! Function
julia
invalidate_canopy_cache!(canopy::CanopySurface)

Force recomputation of the canopy cache on the next create_surface_layer! call. Use after changing canopy parameters (LAI, LAD, etc.) between runs.

source