3 · Layer Optical Properties — the bridge
For: anyone reading the RT basics arc top-to-bottom. This is the page that connects "physics inputs" (gas absorption, Mie scattering) to "what the solver does." Centerpiece of the thread.
Prev: 2 · Vector RTE & Discretization · Next: 3a · Gas Absorption
Concepts/02 ended on a four-array abstraction:
Every atmospheric layer reduces to four arrays
For each Fourier moment
| Symbol | Meaning | Shape |
|---|---|---|
| optical depth | (nSpec,) | |
| single-scattering albedo | (nSpec,) | |
| forward-direction phase matrix | (NquadN, NquadN, nSpec) | |
| backscatter phase matrix | (NquadN, NquadN, nSpec) |
These are also the three core RT variables the linearized solver differentiates against (
The solar direction is a rectangular operator, not a diffuse stream
With external_solar=true, the collimated direction (NquadN,nStokes,nSpec), map the fixed solar Stokes vector into every diffuse output stream.
The elemental solver converts these phase columns into four explicit solar column operators:
| Operator | Meaning | Shape |
|---|---|---|
| direct-solar to diffuse reflection | (NquadN,nStokes,nSpec) | |
| direct-solar to diffuse scattered transmission | (NquadN,nStokes,nSpec) |
This representation avoids dummy zero-weight solar nodes and gives elastic and Raman SFI the same operator contract.
How the four arrays are built (the operator chain)
The Julia type that carries them is CoreScatteringOpticalProperties, and the constructor is constructCoreOpticalProperties in src/CoreRT/LayerOpticalProperties/compEffectiveLayerProperties.jl:314–375. The build happens once per band, per Fourier moment, before the layer loop:
Gas opacity τ_abs ─┐
(HITRAN LBL) │
│
Rayleigh ─────────┐ │
(greek + ω̃_Cab) │ │
▼ ▼
Aerosol_1 ──→ constructCoreOpticalProperties ──→ Per-layer
(GreekCoefs, (τ, ϖ₀,
ω̃, k, fᵗ) Z⁺⁺, Z⁻⁺)
▲ │
│ │
Aerosol_2 ────────┘ ▼
MOM solverThe build proceeds in five steps (the relevant lines from compEffectiveLayerProperties.jl:314–375):
Pick the Rayleigh greek source. For pure-elastic runs (
noRS), use the full Rayleigh greek — rotational Raman is rolled into the effective depolarization. For Raman-aware runs (RRS,VS_*), use the Cabannes greek and handle rotational Raman explicitly. Selector at lines 8–9. See Concepts/08.Build the Rayleigh layer's
from the chosen greek and the quadrature points via Scattering.compute_Z_moments(pol_type, μ, greek, m).For each aerosol, run
compute_Z_momentsagain (with that aerosol'sGreekCoefs), wrap in aCoreScatteringOpticalPropertiesviacreateAero(which also applies δ-M truncation — see Concepts/03c), and mix it into the layer with+.Add gas absorption to the total optical depth:
, . Coded ascombo + CoreAbsorptionOpticalProperties(τ_abs)(line 363).Stack layers vertically with
*(line 372):prod([band_layer_props[i][iz] for i = 1:length(iBand)]). The result is aVector{CoreScatteringOpticalProperties}, one entry per atmospheric layer, ready for Concepts/04.
The walkthrough in Concepts/03c goes through the body of the loop in detail, including the scattering-vs-total τ split that makes the doubling step cheap.
The two operators on CoreScatteringOpticalProperties
Mixing scatterers in a layer (src/CoreRT/types.jl:1743–1783):
function Base.:+(x::CoreScatteringOpticalProperties, y::CoreScatteringOpticalProperties)
τ = x.τ .+ y.τ # additive optical depth
wx = x.τ .* x.ϖ # scattering-weight from each
wy = y.τ .* y.ϖ
w = wx .+ wy
ϖ = w ./ τ # τϖ-weighted SSA
Z⁺⁺ = (wx .* xZ⁺⁺ .+ wy .* yZ⁺⁺) ./ w # τϖ-weighted phase matrix
Z⁻⁺ = (wx .* xZ⁻⁺ .+ wy .* yZ⁻⁺) ./ w
CoreScatteringOpticalProperties(τ, ϖ, Z⁺⁺, Z⁻⁺)
endThe weighting is
Stacking layers vertically (src/CoreRT/types.jl:1785+):
function Base.:*(x::CoreScatteringOpticalProperties, y::CoreScatteringOpticalProperties)
arr_type = array_type(architecture(x.τ))
x = expandOpticalProperties(x, arr_type)
y = expandOpticalProperties(y, arr_type)
CoreScatteringOpticalProperties(
[x.τ; y.τ], [x.ϖ; y.ϖ],
cat(x.Z⁺⁺, y.Z⁺⁺, dims=3),
cat(x.Z⁻⁺, y.Z⁻⁺, dims=3))
endThis stacks the spectral axis (cat(..., dims=3)) for Vector{CoreScatteringOpticalProperties}) — it's a multi-band concatenation along the spectral axis, used when several bands share the same atmospheric column.
A worked example
Consider an OCO-2-like O₂ A-band layer at altitude iz, with one Rayleigh contribution and one fine-mode aerosol:
# inside constructCoreOpticalProperties, conceptually:
# (1) Rayleigh: τ_rayl from molecular cross-section, ϖ_Cab ≈ 1 for pure scattering
rayl = CoreScatteringOpticalProperties(τ_rayl, ϖ_Cab, RaylZ⁺⁺, RaylZ⁻⁺)
# (2) Aerosol fine mode, with δ-M applied via createAero
aer = createAero(τ_aer_fine, aer_optics_fine, AerZ⁺⁺, AerZ⁻⁺)
# → modifies (τ, ϖ) for forward-peak truncation per SS2015
# (3) Mix scatterers in the layer
combo = rayl + aer
# → τ, ϖ, Z⁺⁺, Z⁻⁺ are τϖ-weighted averages
# (4) Add gas absorption (τ_abs is per-wavelength)
layer_optics = combo + CoreAbsorptionOpticalProperties(τ_abs)
# → τ_λ = τ_abs + τ_scat
# ϖ_λ = τ_scat·ϖ / τ_λ
# Z⁺⁺, Z⁻⁺ unchangedAfter all Nz layers are built, rt_kernel! consumes them one at a time from TOA to BOA inside the Fourier-moment loop (Concepts/04).
What's next
To understand how each ingredient is computed:
3a · Gas Absorption — how
is built (HITRAN line-by-line, Voigt line shapes). 3b · Mie & Rayleigh — how
, , , are built (Mie series, GreekCoefs, NAI-2 vs PCW). 3c · Mixing & δ-M Truncation — how the ingredients combine, and the scattering-vs-total τ split that makes the doubling tractable.
If you only care about what the solver does with these arrays, skip to Concepts/04.
Code anchors
| Concept | Source |
|---|---|
| Build per-layer (τ, ϖ, Z) | src/CoreRT/LayerOpticalProperties/compEffectiveLayerProperties.jl:314–375 |
Aerosol δ-M wrapper (createAero) | compEffectiveLayerProperties.jl:478–491 |
| Cabannes vs Rayleigh greek | compEffectiveLayerProperties.jl:8–9 |
Mix scatterers (operator +) | src/CoreRT/types.jl:1743–1783 |
Stack layers (operator *) | src/CoreRT/types.jl:1785+ |
CoreScatteringOpticalProperties type | src/CoreRT/types.jl::CoreScatteringOpticalProperties |
| Gas absorption type | src/CoreRT/types.jl::CoreAbsorptionOpticalProperties |
Hands-on tutorials
Runnable examples with Plotly figures:
References
Sanghavi et al. (2014), JQSRT 133:412–433, doi:10.1016/j.jqsrt.2013.09.004. §2 (layer-averaged optics, Eqs. C.22–C.24 for the forward-only
definitions used here). Sanghavi & Frankenberg (2023), JQSRT 311:108791, doi:10.1016/j.jqsrt.2023.108791. §3.2 — separating
from for the constant- N_doubldesign (more in Concepts/03c).Crib sheet:
docs/dev_notes/theory_references.md§F.