Release Notes and Migration
For: users moving to the registration-ready vSmartMOM 2.0 interface.
Next: Quick Start, Configure a Scene, Compute Jacobians.
This page summarizes the user-visible changes in the 2.0 line. It is written as a migration guide, not as a complete git history.
v2.1.0 — Fourier / stream resolution + source-term refactor
vSmartMOM = Vector Simulated Measurements of the Atmosphere using Radiative Transfer based on the Matrix Operator Method.
This is the package version that ships the schema generation we internally called v0.7 (Fourier / Stream Resolution refactor) plus the v0.6 source-term abstraction. Treat it as a breaking schema migration: max_m / l_trunc still parse for tactical reasons but are no longer the recommended idiom in any in-tree YAML — every example uses nstreams + truncation: auto now.
The new public input is nstreams (weighted streams per hemisphere). The schema-generation v0.7 label persists in some docs (stream_l_cap = 2·nstreams - 1 and the per-band trait dispatch); the package version on the registry is v2.1.0.
What changed for users
# Before (still works — legacy schema)
radiative_transfer:
quadrature_type: GaussLegQuad()
max_m: 14
l_trunc: 25
# After (new schema, recommended)
radiative_transfer:
nstreams: 8 # public contract: stream_l_cap = 2·N - 1
truncation: auto # NoTruncation if phase fits, δBGE otherwise
# quadrature_type omitted ⇒ GaussLegQuad() (Sanghavi: cheaper +
# 5–50× more accurate per stream than RadauQuad on Rayleigh)Highlights
nstreams ≥ 3is enforced — Rayleigh contributes through m=2.truncation: automirrors VLIDORT'sDO_DELTAM_SCALING: the resolver picksNoTruncation()when the phase function fits withinstream_l_cap,δBGE(N, Δ_angle)otherwise. Logs the choice via@infoso the user always sees what was applied.GaussLegQuad()is the default quadrature whenquadrature_typeis omitted.RadauQuad()remains an option but is documented as expert/legacy. (See the natraj benchmark indocs/src/pages/benchmarks.mdfor the per-scheme accuracy comparison.)JSON Schema at
schemas/vsmartmom-parameters.schema.jsondescribes the v0.7 schema. Wired to TOML via the repo's.taplo.toml; YAML editors honour it via# yaml-language-server: $schema=.... Setup recipe inSchema.md.Per-block schema docs under
docs/src/pages/IO/Schema/— one page per top-level YAML/TOML block.
Internal cleanup
Nstreamsandm_max_bandsare first-class fields onQuadPointsandSolverConfig. Forward and lin RT paths share a single_derive_m_max_bandshelper, fixing a latent precedence-bug in the lin path at evenl_max.Per-component
component_m_max(c, ctx)traits drive the per-band Fourier loop bound. Cox-Munk / RPV / Ross-Li / canopy now run to their fulluser_l_capinstead of the silently-half-truncated count-aggregator output.
Migration guidance
YAMLs that explicitly set
max_m/l_trunckeep working byte-identically — they hit the legacy parsing branch.YAMLs that hand-set
max_mto upper-bound a Cox-Munk loop (e.g.config/ocean_coxmunk.yaml) get a numeric change: the Fourier loop now runs to the user-set order rather than the half-truncated aggregator output. The new behavior matches the user's explicit request.lin Jacobians may shift by one Fourier moment for configs with even
l_max(intentional fix to a latent precedence bug; documented in the Phase B commit).
See docs/dev_notes/fourier_stream_resolution_plan.md for the design rationale.
Platform Support
vSmartMOM 2.0 supports Julia 1.10 and newer Julia 1.x releases listed in Project.toml. CUDA is an optional weak dependency, and the package includes compatibility with CUDA.jl 6.
CPU remains the default portable path:
params = read_parameters(joinpath(pkgdir(vSmartMOM), "config", "quickstart.yaml"))
params.architecture = CPU()
model = model_from_parameters(params)
R, T = rt_run(model)GPU runs use the same high-level flow, but set params.architecture = GPU() and load CUDA in the environment that runs the model.
Model Hierarchy
model_from_parameters(params) now returns an RTModel{ARCH, FT}. The model organizes solver state into named sub-objects:
solver: polarization, quadrature, truncation, and numerical settings.geometry: solar/viewing geometry and observer altitude.quad_points: quadrature abscissae and weights.atmosphere: atmospheric profile and spectral bands.optics: Rayleigh, aerosol, gas, and linearized optical-property state.surfaces: one surface model per spectral band.sources: source-term scene (v0.6 — see below). DefaultSolarBeam()preserves historical unit-Stokes-I behavior.
The old flat vSmartMOM_Model container has been replaced. Compatibility getters still expose many legacy fields, but new code should prefer the hierarchical fields and accessor functions documented in the Library.
v0.6 Source-Term Refactor
Source terms (solar, surface fluorescence, blackbody, future thermal / lidar / lunar) are now first-class composable types under AbstractSource. The MOM solver is mathematically affine — optical properties define the operator A, sources define the additive RHS b — and v0.6 makes that explicit at the type level.
# Default (unchanged behavior — unit Stokes-I solar beam):
R, T = rt_run(model)
# Explicit solar with custom irradiance (mW · m⁻² · cm⁻¹):
R, T = rt_run(model; sources = SolarBeam(F₀ = solar_spec))
# Solar + surface fluorescence:
R, T = rt_run(model; sources = SolarBeam() + SurfaceSIF(SIF₀ = sif_spec))
# Thermal-IR / Carbon-I-style scene (1500 K blackbody at 2-2.4 µm):
spec_band = collect(model.atmosphere.spec_bands[1])
R, T = rt_run(model; sources = BlackbodySource(1500, spec_band))Highlights:
RTModel.sources::AbstractSourceis a new top-level field. Set it withmodel_from_parameters(params; sources = …)or override per-call with thesources=kwarg onrt_run/rt_run_lin/rt_run_ss.SolarBeam(FT-deferred — the model's float type drives precision viaprepare_source),SurfaceSIF,BlackbodySource(constructor sugar that returns aSolarBeamwith Planck-derived F₀),NoSource,SourceSet(compile-time-unrolled tuple) ship as built-ins.Composition via
+:SolarBeam() + SurfaceSIF()⇒SourceSet((SolarBeam, SurfaceSIF)).NoSourceis the identity.Multiple-dispatch entry points
contribute!(forward) andsource_tangent!(linearized) replace the legacyif SFIbranching across the elastic linearized path. Surfaces use a parallelsurface_source_contribute!(prepared_sources, surface, …)double- dispatch table for surface-side contributions (SIF on Lambertian surfaces today; non-Lambertian dispatches to a no-op).The Sanghavi 2014 App. C analytic tangents and the Bug-22 beam- attenuation chain-rule fix are unchanged inside
get_elem_rt_SFI_fused!; they're now reached viasource_tangent!(::PreparedSolarBeam, …)— the named hand-written linearization seam.AD seam at
prepare_source(::AbstractSource, FT, pol_n, nSpec, arr_type): user-parameter space (potentiallyDual) above; kernel space (plainFT) below. A futureprepare_source_with_tangentwill mirror this signature and return source-parameter Jacobians; v0.6 reserves theForwardDiffSourceJacobianAD-mode trait for that path.Source-unit convention adopted:
F₀andSIF₀in mW · m⁻² · cm⁻¹,rt_runoutputs in mW · m⁻² · sr⁻¹ · cm⁻¹.BlackbodySourcedefaults tofactor = π(Lambertian-disk → hemisphere irradiance) so itsF₀is comparable to a unitSolarBeam(F₀ = 1)baseline.
Legacy compatibility: the inject_surface_SIF!(brdf, …, SIF₀, arch) path that consumes RS_type.SIF₀ still runs in parallel for back-compat with existing rs.SIF₀ = …; rt_run_test_ss(rs, model, 1) test patterns. If a user supplies BOTH model.sources containing a SurfaceSIF AND sets RS_type.SIF₀, the surface SIF is double-counted; choose one API at a time. A future PR will retire RS_type.F₀ / RS_type.SIF₀ ownership in favour of the dispatch system.
Full architecture writeup: Sources. Test coverage is in test/test_sources.jl (Phase 1 → 5b regression, 86/86 pass).
Parameter Loading
Use read_parameters as the public entry point:
params = read_parameters("scene.yaml")
params = read_parameters(Dict("architecture" => "CPU"))
params = read_parameters(GeosChemSource("GEOSChem.Custom.nc4", 1, 1, 1))The explicit lower-level aliases remain available:
parameters_from_file(path)for extension-dispatched YAML/TOML files.parameters_from_dict(dict)for in-memory dictionaries.parameters_from_source(src)forIOSourcevalues.parameters_from_yaml(path)for YAML/YML files only.
parameters_from_yaml("scene.toml") now raises ArgumentError; use read_parameters("scene.toml") or parameters_from_file("scene.toml").
Jacobians
Linearized RT is exposed through LinMode, model_from_parameters_lin, and rt_run_lin:
params = read_parameters(joinpath(pkgdir(vSmartMOM), "test", "test_parameters", "JacobianTestFast.yaml"))
model, lin_model = model_from_parameters_lin(params)
R, T, R_jac, T_jac = rt_run_lin(model, lin_model)Use ParameterLayout to map Jacobian columns to aerosol, gas, surface, and canopy parameter blocks. Raman/inelastic linearization is not complete in this release line; keep linearized scenes on the elastic path unless a feature branch or later release states otherwise.
Test and Data Policy
The registration-oriented test suite avoids machine-local data assumptions. RAMI remains useful as an external canopy validation workflow, but it is not a unit-test fixture in the default test environment.
The EMIT/MODTRAN comparison scripts remain in the repository as benchmark and analysis utilities, but their execution tests are disabled in CI because they depend on external LUTs and machine-local scene inputs. The Phase 6 test suite still parses those scripts so ordinary import and syntax drift is caught.
SIF helper functions are exported because the data loaders exist, but the end-to-end SIF workflow is kept under Experimental Helpers until the data-fixture and product policy are settled.
Documentation and Citation
The manual has been reorganized around task pages first and a module-grouped Library second. The docs build runs with checkdocs = :exports and without warnonly, so exported public symbols and cross references are checked as part of the release gate.
The repository now ships a root CITATION.bib with the JOSS software paper and the core method references. See References for the human-readable citation guide.
Known WIP Areas
Aerosol scene input is documented, but the high-level API is still being stabilized.
Internal export cleanup is intentionally deferred until after v2.0.0 so this release does not tighten public API surface area at the same time as the registration cutover.
Unified offline source-function integration and thermal emission are design topics, not implemented user-facing features in this release line.
Future Developments
Mixed automatic differentiation and hand-coded linearization.
Coupled ocean-atmosphere radiative transfer.
More line-shape and line-mixing functionality, including expansion to more spectral databases.
Raman performance and memory-pressure improvements.
Correlated-k and other spectral dimension-reduction methods.