Models API
The Models module contains the runtime stepper — TransportModel, DrivenSimulation, the IC pipeline, and the recipe builders that turn a TOML config into a fully-wired runtime object.
AtmosTransport.Models.AbstractAdvectionContext Type
AbstractAdvectionContextThe program that reads an [advection] table. Forward transport runs and the 4D-Var inversion driver parse it with the same advection_spec and materialize; the context changes only the scheme default and the accepted PPM limiters:
ForwardAdvectionContext(scripts/run_transport.jl): an omittedschememeans upwind;limiterismonotoneorcw84.InversionAdvectionContext(scripts/inversions/cs_4dvar.jl):schemeis required, andlimiteralso acceptsnone.
AtmosTransport.Models.AbstractSchedule Type
AbstractScheduleWhen a Callback runs: a callable schedule(sim) -> Bool, evaluated after every step of a DrivenSimulation.
AtmosTransport.Models.Callback Type
Callback(func, schedule = IterationInterval(1))Run func(sim) after a step of a DrivenSimulation whenever schedule actuates. Pass callbacks to DrivenSimulation(...; callbacks = (; name = cb)).
AtmosTransport.Models.DrivenSimulation Type
DrivenSimulationLow-level window-driven runtime for custom driver/model wiring.
Most TOML-based runs should go through run_driven_simulation, which builds and validates this object for you. Construct DrivenSimulation directly only when you need a custom met driver, callback loop, or model assembly.
A DrivenSimulation keeps transport-window timing and forcing in the driver, while the model retains ownership of prognostic tracer and air-mass state. The runtime interpolates forcing within each met window and advances the model with the same step!(model, Δt) entry point used by the fixed-flux smoke harness.
For experienced users, the constructor is meant to be assembled from the same pieces used by run_driven_simulation: a met driver, a basis-compatible state, empty flux storage owned by the model, and a runtime physics recipe. A minimal LL/RG manual setup looks like this:
using TOML, AtmosTransport
using AtmosTransport.MetDrivers: air_mass_basis, driver_grid, flux_kind
# First run examples/generate_synthetic_quickstart.jl from the terminal.
cfg = TOML.parsefile("config/examples/minimal_template.toml")
paths = expand_binary_paths(cfg["input"])
FT = Float64
driver = TransportBinaryDriver(first(paths); FT = FT, arch = CPU())
recipe = build_runtime_physics_recipe(cfg, driver, FT)
validate_runtime_physics_recipe(recipe, driver)
grid = driver_grid(driver)
window1 = load_transport_window(driver, 1)
Basis = air_mass_basis(driver) === :dry ? DryBasis : MoistBasis
air = copy(window1.air_mass)
vmr = build_initial_mixing_ratio(
air, grid, Dict("kind" => "uniform", "background" => 400e-6);
surface_pressure = window1.surface_pressure,
)
co2 = pack_initial_tracer_mass(grid, air, vmr; mass_basis = Basis())
state = CellState(Basis, air; CO2 = co2)
fluxes = allocate_face_fluxes(grid.horizontal, nlevels(grid);
FT = FT, basis = Basis)
model = TransportModel(state, fluxes, grid, recipe.advection;
diffusion = recipe.diffusion,
convection = recipe.convection)
sim = DrivenSimulation(model, driver;
stop_window = min(total_windows(driver), 24),
chemistry = recipe.chemistry)
run_window!(sim)The full runner adds the practical edges around this skeleton: resolving many daily binaries, GPU adaptation, surface-flux source construction, snapshot output, progress reporting, and capability checks against every file.
SurfaceFluxSource lives with the surface-flux operator in src/Operators/SurfaceFlux/.
AtmosTransport.Models.DrivenSimulation Method
DrivenSimulation(model, driver; kwargs...)Construct a window-driven src runtime.
Keyword arguments:
start_window=1stop_window=total_windows(driver)initialize_air_mass=trueuse_midpoint_forcing=trueinterpolate_fluxes_within_window=nothing(derive from driver)air_mass_reset_mode=:preserve_tracer_mass— one of:none,:preserve_vmr, or:preserve_tracer_mass. When non-:none, each newly loaded window replaces prognostic air mass using the selected tracer invariant. For binary-scheduled runs, the same endpoint reset is applied before the once-per-window convection and closing chemistry half-step so physics sees the binary's authoritative window-end mass.physics_cadence=nothing—:window(the default fornothing) or:substep. On a binary with a per-window physics contract,:windowruns convection once per met window and splits exponential decay around all stored advection/emission substeps;:substepruns the full composition every advection substep (the cadence before 2026-05-31), for cadence-sensitivity comparisons on the same binary. The window-end air-mass reset is the same for both. Other binaries run the full operator suite every substep either way. The resultingStepPlanissim.plan; the cubed-sphere adjoint replays the same plan (CSStepSchedule).prefetch_windows=true— on a device backend with more than one Julia thread, load the next met window on a second thread while the current one runs.falseloads each window when it is needed and keeps one device window buffer instead of two. Window contents are the same either way.surface_sources=()chemistry=NoChemistry()— exponential decay is applied in exact half-window steps before and after the window's transport + surface sources; other chemistry operators run after transport and convectioncallbacks=NamedTuple()reuse_buffers_of=nothing— a previousDrivenSimulationof the same model, typically that of the preceding daily binary. If its driver has the same window layout (same_window_layout) and it kept a host staging window, its device windows, host staging window, expected-air-mass and humidity buffers and the model's convection forcing are refilled in place instead of allocated, and the startup trim of the device memory pool (a garbage collection and pool reclaim) is skipped. Its prefetch is finished first. Otherwise new buffers are allocated as without it. Window contents are the same either way.start_time=0— simulation clock origin [s]. Multi-binary runners MUST pass the accumulated run time here when rebuilding the sim per binary:sim.timefeedscurrent_time(meteo), which time-varying surface-flux sources use to select their emission slice (seconds since the RUN start, not the binary start). Restarting the clock at 0 each day silently replays day-1 fluxes — the December-2021 co2_natural +1 Pg/month surplus (plan 45 Stage-4 A/B experiment attributed the leak to exactly this).
AtmosTransport.Models.ForwardAdvectionContext Type
ForwardAdvectionContext()[advection] of a forward transport run. See AbstractAdvectionContext.
AtmosTransport.Models.InversionAdvectionContext Type
InversionAdvectionContext()[advection] of the 4D-Var inversion driver, whose gradient is the adjoint of the selected scheme. scheme must be given explicitly, and limiter = "none" selects the unlimited PPM (PPMScheme(NoLimiter())). Unlimited PPM is linear in the tracer, so the modeled observations are linear in the emissions and the observation cost is exactly quadratic in them (a limiter makes this map only piecewise linear). Its fields can overshoot and go negative, and forward runs do not offer it. See AbstractAdvectionContext.
AtmosTransport.Models.IterationInterval Type
IterationInterval(interval)Actuates after every interval-th step of the simulation (iterations interval, 2 interval, …).
AtmosTransport.Models.NaNChecker Type
NaNChecker()Callback function that stops the run with an error when the air mass or a tracer holds a NaN or Inf (interior cells only). It runs mapreduce on the state's backend, without copying fields to the host: one reduction for the air mass and one for all tracers on structured grids, one per panel for each on the cubed sphere (about 0.1 ms each on an L40S). The run config installs it as Callback(NaNChecker(), WindowInterval(n)) ([checks]).
AtmosTransport.Models.Simulation Type
SimulationLow-level fixed-step harness for custom in-memory experiments.
Most users should call run_driven_simulation with a run config. Use Simulation only when you already constructed a TransportModel and want to drive it with a custom time loop.
AtmosTransport.Models.TransportModel Type
TransportModel(state, fluxes, grid, advection; kwargs...)Minimal Oceanigans-style model object for standalone src transport runs.
Carries advection, chemistry, vertical diffusion, surface emissions, and convection operators. Constant-rate exponential decay is Strang-split around the transport block's emissions: decay(dt/2) → transport_block(dt) → convection_block(dt) → decay(dt/2)
Other chemistry operators, including exponential decay with a time-varying rate, retain the unsplit order: transport_block(dt) → convection_block(dt) → chemistry_block(dt)
where transport_block runs the full palindrome with diffusion and emissions at the center:
X → Y → Z → V(dt/2) → S(dt) → V(dt/2) → Z → Y → X (emissions active)
X → Y → Z → S(dt) → V(dt) → Z → Y → X (diffusive boundary coupling)
X → Y → Z → V(dt) → Z → Y → X (no emissions)step!(model, dt) executes the full runtime composition. Constant-rate exponential decay runs as two exact half-steps around transport/emissions; other chemistry, including time-varying-rate decay, runs once after transport and convection.
Defaults chemistry = NoChemistry(), diffusion = NoDiffusion(), emissions = NoSurfaceFlux(), and convection = NoConvection() make the inactive operator slots compile to no-op dispatches.
Convection fields
convection :: ConvT— operator type, defaults toNoConvection(). Concrete subtypes areCMFMCConvectionandTM5Convection.NoConvectionis a compile-time dead branch instep!.convection_forcing :: CF— per-step forcing container. Defaults toConvectionForcing()(all-nothing placeholder).DrivenSimulationconstruction allocates real buffers viaallocate_convection_forcing_like;_refresh_forcing!populates them fromsim.window.convectionat the first substep of each window.
Helpers with_convection(model, op) and with_convection_forcing(model, forcing) parallel with_chemistry / with_diffusion / with_emissions.
AtmosTransport.Models.WallTimeInterval Type
WallTimeInterval(seconds)Actuates at the first step at least seconds of wall time (monotonic clock) after it last actuated, or after it was constructed.
AtmosTransport.Models.WindowInterval Type
WindowInterval(interval)Actuates at the end of every interval-th met window this schedule has seen. The count lives in the schedule, so a callback reused by the simulations of consecutive input files (as the runner does) counts windows over the whole run. Give each Callback its own schedule.
AtmosTransport.Models.advection_spec Function
advection_spec(section[, context = ForwardAdvectionContext()]) -> AbstractAdvectionSpecParse an [advection] section into a typed spec. ppm_order is only meaningful for scheme = "linrood"; pairing it with scheme = "ppm" is rejected (the split PPM path takes no order knob). vertical applies to scheme = "ppm" (default same_as_horizontal) and scheme = "linrood" (default fv3_kord8). limiter applies to scheme = "ppm" only (monotone, the default, or cw84; the inversion driver also accepts none). In a forward run an omitted selector defaults to upwind; the inversion driver requires it. An omitted Lin–Rood ppm_order defaults to DEFAULT_LINROOD_PPM_ORDER (5). context names the program reading the table; see AbstractAdvectionContext.
AtmosTransport.Models.build_runtime_chemistry Method
build_runtime_chemistry(cfg, ::Type{FT}) -> AbstractChemistryOperatorRead the optional [chemistry] TOML section and produce the corresponding chemistry operator.
Supported kind values:
"none"(default) —NoChemistry()."decay"—ExponentialDecay(FT; ...). Half-lives are read from thehalf_lives_secondstable:[chemistry] kind = "decay" [chemistry.half_lives_seconds] rn222 = 330350.4 # RN222_HALF_LIFE, 3.8235 daysParameters.RN222_HALF_LIFEis this ²²²Rn value (3.8235 * 86400.0equals the literal330350.4in Float64). The keyword name must match the corresponding[tracers.<name>]symbol that the run is carrying (case-insensitive — the builder symbolizes the key as-is andExponentialDecay.apply!resolves it againststate.tracer_namesat call time).
Thin wrapper: parse the [chemistry] section into a typed AbstractChemistrySpec once (validated), then materialize at run precision FT. Spec types + parser live in RuntimePhysicsSpecs.jl.
AtmosTransport.Models.build_runtime_physics_recipe Method
build_runtime_physics_recipe(cfg, context, FT; halo_width=nothing)Parse typed advection, diffusion, convection, and chemistry specifications and the binary CFL check from cfg, materialize them for context and floating-point type FT, then run the complete runtime compatibility validation.
sourceAtmosTransport.Models.chemistry_spec Method
chemistry_spec(section) -> AbstractChemistrySpecParse a [chemistry] section into a typed spec. kind = "decay" with an empty (or absent) half_lives_seconds table reduces to NoChemistrySpec. Each half-life must be numeric, non-Boolean, and positive. Values are stored as Float64 seconds and converted to tracer precision during materialization.
AtmosTransport.Models.convection_chemistry_step! Method
convection_chemistry_step!(model::TransportModel, dt; meteo = nothing)Advance the non-transport physics blocks once: convection block → chemistry block.
sourceAtmosTransport.Models.convection_spec Method
convection_spec(section) -> AbstractConvectionSpecParse a [convection] TOML section into a typed spec, validating at the boundary.
Nondefault lmax_conv/n_merge require use_collab_lu = true; otherwise parsing throws an ArgumentError. They affect the workgroup-collaborative solver only. Eligibility is checked against the backend, tracer precision, and state depth at execution time. The legacy fallback uses the full column without aggregation.
AtmosTransport.Models.cs_adjoint_inputs Method
cs_adjoint_inputs(sim::DrivenSimulation; tracer = first tracer) -> NamedTupleEverything a cubed-sphere adjoint of sim's configuration needs, captured from the production simulation itself: panels_rm0 (the tracer tracer) and panels_m0 (the initial state), the face fluxes of every step as production refreshes them (panels_am_steps, panels_bm_steps, panels_cm_steps: window loading, intra-window interpolation, full-window flux scaling), mesh, and kwargs for the adjoint entry points: scheme, schedule = CSStepSchedule(sim), surface_flux, emission_rates (the tracer's static surface source per step, or nothing), the diffusion operator and a workspace with production's layer thickness per met window (diffusion_op, diffusion_workspace; the operator is copied per window when its Kz field is refreshed per window), and the convection operator, its forcing per step (one object per met window) and a fresh workspace.
require_adjoint_support(sim) runs first. The capture runs the simulation's met refresh through every window (not its tracer transport, which it leaves in an unspecified state), so sim cannot be run afterwards: build a second, identical simulation for the production run. Memory grows with the run: one set of face fluxes per step. A time-varying surface source of tracer is an error (pass its per-step rates yourself). Initial persistent operator state must be fresh: nonzero surface-source Kahan compensation, diffusion/Dkg or convection ledger carries, or a valid CMFMC CFL cache are rejected before capture. This also rejects continued models whose new simulation has iteration == 0.
inputs = cs_adjoint_inputs(sim; tracer = :CO2)
footprint = cs_surface_emission_footprint(inputs.panels_rm0, inputs.panels_m0,
inputs.panels_am_steps, inputs.panels_bm_steps, inputs.panels_cm_steps,
inputs.mesh, objective; base_emission_rates = inputs.kwargs.emission_rates,
Base.structdiff(inputs.kwargs, NamedTuple{(:surface_flux, :emission_rates)})...)AtmosTransport.Models.diffusion_spec Method
diffusion_spec(section) -> AbstractDiffusionSpecParse a [diffusion] section into a typed spec, validating at the boundary. An empty/absent section or kind = "none" is explicit "no diffusion". The legacy type = "..." schema is rejected (it used to silently fall through to NoDiffusion, hiding configs that expected diffusion to run); a present section with no kind is rejected too.
AtmosTransport.Models.run! Method
run!(simulation)Advance a Simulation or DrivenSimulation until its configured stop condition. Returns the mutated simulation.
sourceAtmosTransport.Models.run_window! Method
run_window!(sim::DrivenSimulation)Advance exactly the current meteorological window and return sim. If sim is positioned at a completed non-final window, the next window is loaded first.
sourceAtmosTransport.Models.step! Method
step!(model::TransportModel, dt; meteo = nothing)Advance model.state by one full runtime step: the step stages of the substep StepPlan. Exponential decay uses the symmetric composition decay(dt/2) → transport/emissions(dt) → convection(dt) → decay(dt/2). Other chemistry operators keep the transport/emissions(dt) → convection(dt) → chemistry(dt) order.
With defaults diffusion = NoDiffusion(), emissions = NoSurfaceFlux(), chemistry = NoChemistry(), convection = NoConvection(), every live component is a dead branch and the call is bit-exact equivalent to the advection-only path.
meteo is optional and defaults to nothing; pass a real meteorology object (AbstractMetDriver) or a DrivenSimulation to thread current_time(meteo) through operators that consume time-varying fields.
AtmosTransport.Models.transport_step! Method
transport_step!(model::TransportModel, dt; meteo = nothing)Advance model.state by one transport step (the plan's TransportStage): advection with vertical diffusion at the palindrome center and surface emissions. The diffusion operator's coupling policy selects either V(dt/2) -> S(dt) -> V(dt/2) or S(dt) -> V(dt) when both diffusion and a surface source are active; without a surface-flux operator the center is one solve V(dt).
Binary-scheduled driven runs use this block at the per-window advection substep cadence stored in the transport binary. Convection and chemistry are separate physics blocks and can run at the met-window cadence via convection_chemistry_step!.
AtmosTransport.Models.validate_runtime_physics_recipe Method
validate_runtime_physics_recipe(recipe, context; halo_width=nothing)Validate topology support, binary capabilities, and halo requirements for a materialized RuntimePhysicsRecipe. Returns recipe or throws ArgumentError before model allocation.
sourceAtmosTransport.Models.with_chemistry Method
with_chemistry(model::TransportModel, chemistry)Return a copy of model with its chemistry operator replaced. All other fields share storage with the original. Chemistry is installed into the model rather than held at the sim level, so this helper is primarily useful for tests that want to swap chemistry on a constructed model.
AtmosTransport.Models.with_convection Method
with_convection(model::TransportModel, convection)Return a copy of model with its convection operator replaced. All other fields — including convection_forcing — share storage with the original.
Note: with_convection does NOT allocate convection-forcing buffers. The model-side ConvectionForcing() placeholder stays as-is. DrivenSimulation construction is responsible for allocating real buffers via allocate_convection_forcing_like after the first window loads. For tests that bypass the sim layer, use with_convection_forcing(model, forcing) to inject allocated buffers directly. The model workspace is re-wrapped as needed so concrete operators can carry their own scratch storage without disturbing the advection workspace.
AtmosTransport.Models.with_convection_forcing Method
with_convection_forcing(model::TransportModel, forcing::ConvectionForcing)Return a copy of model with its per-step convection-forcing container replaced. All other fields — including the convection operator — share storage with the original.
Used by DrivenSimulation construction to install the allocated forcing buffers after the first window loads. Also useful for tests that inject forcing directly without going through the sim's _refresh_forcing! path.
AtmosTransport.Models.with_diffusion Method
with_diffusion(model::TransportModel, diffusion)Return a copy of model with its diffusion operator replaced. All other fields share storage with the original. Parallel to with_chemistry; useful for installing a diffusion operator into a model that was constructed with the default NoDiffusion().
AtmosTransport.Models.with_emissions Method
with_emissions(model::TransportModel, emissions)Return a copy of model with its surface-emissions operator replaced. All other fields share storage with the original. Parallel to with_chemistry and with_diffusion; used by DrivenSimulation to install the sim-level surface_sources tuple as a SurfaceFluxOperator inside the wrapped model, so the palindrome's S slot runs at the right place in the transport block without sim-level post-step hacks.
AtmosTransport.Models.InitialConditionIO Module
InitialConditionIOSingle owner of initial-condition I/O, vertical remap, and topology-dispatched VMR builders for the unified runtime.
Public API (exported via Models → AtmosTransport)
build_initial_mixing_ratio— topology-dispatched builder returning dry VMR on interior cells. Acceptskind = uniform | latitude_step | gaussian_blob | file | netcdf | file_field | catrine_co2for LL/RG meshes, plusbl_enhancedon LL. CS supports those shared kinds pluspressure_layerand signedcs_nativefields.pack_initial_tracer_mass— basis-aware VMR → conservative model storage conversion. Dispatches onmass_basis::AbstractMassBasis:DryBasis(the default runtime basis):rm = vmr .* air_mass.MoistBasis:rm = vmr .* air_mass .* (1 .- qv), because IC VMRs are dry andair_massis moist;qvmust be supplied.
FileInitialConditionSource— container for a loaded IC NetCDF (3D VMR + optional hybrid coefficients).
Contents
LL/RG and CS
build_initial_mixing_ratiofor the kinds above. CSpressure_layerselects a level per column from the stored surface pressure and normalizes its dry VMR to the configured global molecule count.Basis-aware
pack_initial_tracer_mass(DryBasis + MoistBasis); CS output is halo-padded with the halo zeroed.File conventions come from
GriddedIO: coordinates (find_coordinate), orientation (latlon_orientation,orient), fill values (read_rawwithMaskedAsZero), the source mesh of LL→CS conservative regridding (source_latlon_mesh(lon, lat; radius), usingSnappedFaces), time axes (decode_time) and flux units (parse_flux_units).Surface-flux loaders and builders (
initial_conditions/surface_flux*.jl): staticFileSurfaceFluxFieldand time-varying series, and LL/RG/CSbuild_surface_flux_sourcemethods. CSbuild_surface_flux_sourceconservatively LL→CS-regrids the flux (the regridder'sdst_areas× regridded density already yields kg/s per cell) and unpacks toNTuple{6, Matrix{FT}}, or returns aTimeVaryingSurfaceFluxSourcewhentime_varying = true— satisfying the per-cell rate contract ofSurfaceFluxSource(src/Operators/SurfaceFlux/sources.jl).
Private helpers (underscore-prefixed) stay unexported and are accessed by callers (including the canonical driven runtime) via AtmosTransport.Models.InitialConditionIO.<name> if needed.
AtmosTransport.Models.InitialConditionIO.FileInitialConditionSource Type
FileInitialConditionSource{FT}Container for a file-based initial condition (e.g. Catrine startCO2).
Fields
raw— 3D mixing ratio field(nlon_src, nlat_src, nlevel_src). With hybrid coefficients, surface first (k = 1is the surface, decided from the interface pressures, the order of_remap_hybrid_profile!); without them, top first like the model levels.qv— optional source specific humidity on the same grid and level order asraw. When absent, remapping assumes a unit source dry fraction (q_v = 0).lon,lat— source coordinate vectors [degrees]. May be in [-180,180) or [0,360); the bilinear sampler wraps to [0,360) internally viawrapped_longitude_360.ap,bp— hybrid half-level coefficients(nlevel_src + 1):p_half[k] = ap[k] + bp[k] × ps_total. The model column's total surface pressure anchors the source coordinate; a file surface-pressure field is neither needed nor read. Units:ap[Pa],bp[dimensionless].needs_vinterp—truefor files with hybrid coefficients, which are always conservatively remapped in pressure (_remap_hybrid_profile!); files without them are copied level by level and must have the target's level count.
AtmosTransport.Models.InitialConditionIO.FileSurfaceFluxField Type
FileSurfaceFluxField{FT}2-D flux density raw (Nx_src, Ny_src) loaded from a NetCDF emission file, in kg species m⁻² s⁻¹ after _load_file_surface_flux_field has normalised the native units, with the source cell areas cell_area [m²]: the file's cell_area/area variable, else the areas of the source mesh (_lonlat_cell_areas_m2, latitude-circle cells on the destination sphere). native_total_mass_rate = Σ raw × cell_area [kg s⁻¹] is the file's own total, which the conservative regrid of the per-cell rate raw × cell_area preserves and the bilinear path renormalises to.
AtmosTransport.Models.InitialConditionIO.TimeVaryingFileSurfaceFluxField Type
TimeVaryingFileSurfaceFluxField{FT}A stack of 2-D flux-density slices (Nx_src, Ny_src, ntime) loaded from a NetCDF emission file, keeping every time slice (no monthly averaging). Units are kg/m²/s after _load_timevarying_surface_flux_field has normalised the native units, identically per slice. times_sec holds the slice times in seconds since the run reference_time, ascending.
AtmosTransport.Models.InitialConditionIO.build_initial_mixing_ratio Method
build_initial_mixing_ratio(air_mass, mesh, cfg)
build_initial_mixing_ratio(air_mass, grid::AtmosGrid, cfg;
surface_pressure=nothing, pressure_basis=TotalSurfacePressure(),
mass_basis=DryBasis(), qv=nothing)Construct the initial dry-air volume mixing ratio described by cfg on the horizontal topology and vertical layout of air_mass.
Bare lat-lon and reduced-Gaussian meshes support the analytic uniform, latitude_step, and gaussian_blob modes; bl_enhanced is lat-lon only. Passing an AtmosGrid additionally enables file-backed modes (file, netcdf, file_field, and catrine_co2) with topology-aware horizontal mapping and conservative hybrid-coordinate vertical remapping. Cubed-sphere construction requires an AtmosGrid and also supports pressure_layer and cs_native. File-backed and pressure-layer construction require the transport window's surface_pressure where indicated by the selected mode. Each public call owns fresh output arrays. The driven cubed-sphere runner separately reuses private interior buffers between analytic initializers before copying each tracer into its independent packed state slot.
For file-backed hybrid remapping, pressure_basis states whether the binary's stored surface pressure is total or dry, mass_basis states what air_mass contains, and qv is the binary's window-start specific humidity when available. Source humidity participates only when init.humidity_variable is configured explicitly. In that case each target value is the source-dry-air mean ∫q(1-qv)dp / ∫(1-qv)dp; without it the mean is ∫qdp / ∫dp. For legacy dry-pressure binaries without any humidity payload, total pressure cannot be reconstructed: the builder keeps the stored dry pressure for interface placement and warns once that it is typically 0.5–2 % below total pressure in humid columns.
Returns an array shaped like air_mass for lat-lon and reduced-Gaussian grids, or an NTuple{6} of interior (Nc, Nc, Nz) arrays for cubed-sphere grids.
AtmosTransport.Models.InitialConditionIO.build_surface_flux_source Method
build_surface_flux_source(grid::AtmosGrid{<:CubedSphereMesh},
tracer_name, cfg, ::Type{FT})CS surface-flux builder. Conservatively LL→CS regrids the per-cell rate (flux density × source cell area, kg species/s; ConservativeFluxRegridding) onto the 6 CS panels as an extensive field, so the total is the file's own, then converts that physical rate to the model storage basis. Returns a SurfaceFluxSource whose cell_mass_rate is an NTuple{6, Matrix{FT}} of interior-only (Nc, Nc) panels.
cfg must set kind (non-none); any of the file-based surface-flux kinds _load_file_surface_flux_field understands work (gridfed_fossil_co2 or user-supplied file + variable). Conservative regridding is enforced — CS bilinear is not supported.
For kind = "cs_native" and time_varying = true, an aligned NetCDF (time,nf,Ydim,Xdim) series is loaded directly, multiplied by native cell areas, and converted to storage units. No regridding is applied.
If cfg["time_varying"] = true and the kind supports a 3-D (lon,lat,time) series (:lmdz_co2, :gridfed_fossil_co2), the builder keeps every time slice, builds the LL→CS regridder ONCE, applies it per slice, and returns a TimeVaryingSurfaceFluxSource whose cell_mass_rate_series is an NTuple{6} of (Nc, Nc, ntime) panels plus a times vector (seconds since reference_time). The default (time_varying absent/false) path returns a static SurfaceFluxSource.
AtmosTransport.Models.InitialConditionIO.build_surface_flux_sources Method
build_surface_flux_sources(grid, tracer_specs, ::Type{FT}; reference_time=nothing, run_span=nothing)Build surface-flux source instances for every tracer spec that requests one. Returns a tuple (possibly empty) suitable for the surface_sources = (…,) kwarg on DrivenSimulation.
reference_time (the run start DateTime) is threaded to each per-tracer builder so the time-varying CS path can align its slice times to the simulation clock. run_span = (t_start, t_end) (seconds since the run start) lets the time-varying CS path read only the slices the run can use. Both are ignored by static sources.
AtmosTransport.Models.InitialConditionIO.initial_condition_spec Method
Parse an initial-condition kind once at the runtime configuration boundary.
sourceAtmosTransport.Models.InitialConditionIO.pack_initial_tracer_mass Method
pack_initial_tracer_mass(grid, air_mass, vmr_dry; mass_basis::AbstractMassBasis,
qv = nothing)Convert dry volume mixing ratio vmr_dry to conservative model storage matching the binary's mass-basis contract. On DryBasis that storage is vmr_dry × dry_air_mass; it is not physical kg species. Returns new storage with the same shape as air_mass. On cubed-sphere grids this is a six-tuple of halo-padded panels, with the halos set to zero.
Dispatch
mass_basis::DryBasis(the default runtime basis) —air_massism_dry. Result:vmr_dry .* air_mass.qvis ignored.mass_basis::MoistBasis—air_massism_moist, so the dry VMR is converted with1 - qv. Result:vmr_dry .* air_mass .* (1 .- qv).qvmust be supplied from the first transport window; missingqverrors.
CS dispatch handles per-panel halo packing.
Arguments
grid—AtmosGridwith a lat-lon, reduced-Gaussian, or cubed-sphere mesh.air_mass— storage-shaped air mass from the transport window; a six-tuple of halo-padded 3D panels on cubed-sphere grids.vmr_dry— dry volume mixing ratio. On cubed-sphere grids, a six-tuple of interior 3D panels; otherwise the same shape asair_mass.mass_basis—DryBasis()orMoistBasis().qv— specific humidity, same shape asair_mass; required iffmass_basis isa MoistBasis.
AtmosTransport.Models.InitialConditionIO.surface_flux_species Method
surface_flux_species(tracer_name::Symbol, cfg) -> SymbolThe species whose kilograms the surface flux cfg of tracer tracer_name counts: cfg["species"] ("co2", "sf6" or "rn222", the entries of Parameters.SPECIES_MOLAR_MASS), or, when the key is absent, the species the tracer name implies (co2, co2_natural, co2_fossil, fossil_co2 → :co2; sf6 → :sf6; rn222 → :rn222). An unknown species, or a tracer with another name and no species, is an ArgumentError that names the known species.
The species sets the conversion of physical kg species s⁻¹ to the model's storage units, DRY_AIR_MOLAR_MASS / SPECIES_MOLAR_MASS[species] (storage is dry VMR × dry-air mass). It also decides how the flux units convert (GriddedIO.kg_per_unit): kg of another named species is an error, and kg of carbon is converted to the species by M(species) / M(C).
AtmosTransport.Models.BinaryPathExpander Module
BinaryPathExpanderExpand an [input] TOML block into transport-binary file paths. Explicit lists retain the supplied order; folder/date selection sorts by date and checks continuity.
Accepted TOML shapes
# Shape A: explicit list, consumed in the supplied order
[input]
binary_paths = ["...a.bin", "...b.bin"]
# Shape B: folder + inclusive date range
[input]
folder = "~/data/.../cs_c48/.../"
start_date = "2021-12-01"
end_date = "2021-12-10"
file_pattern = "era5_transport_{YYYYMMDD}_merged1000Pa_float32.bin" # optionalShapes A and B are mutually exclusive.
Folder scanning
When folder is supplied:
readdir(folder)→ candidate filenames.Each filename is parsed for an 8-digit
YYYYMMDDtoken. Iffile_patternis supplied, the{YYYYMMDD}placeholder marks where the date lives, and the surrounding text is matched literally (as a regex). Withoutfile_pattern, any\d{8}run anywhere in the filename is used.Files parseable as dates within
[start_date, end_date]are kept; files outside the range or not matching the pattern are ignored.The resulting sorted list is verified for continuity: every date in the closed interval must be present. Missing days list in the error message.
Returns
Vector{String} with home/environment-variable expansion. Explicit paths retain their order and may remain relative to the working directory; their dates are not checked here. Folder selection sorts chronologically by parsed date and rejects missing or duplicate dates.
AtmosTransport.Models.BinaryPathExpander.expand_binary_paths Method
expand_binary_paths(input_cfg::AbstractDict) -> Vector{String}See module docstring for the accepted TOML shapes. Throws ArgumentError on mutual exclusion, missing folder, date parse failure, or gaps in the date range.
AtmosTransport.Models.InputStaging.InputStager Type
InputStagerRolling local-disk stager for the daily transport binary list. Construct from the resolved NAS binary_paths and the [input.staging] config sub-table; call staged_path_for! at each day's driver-open site and cleanup_staging! at run end.
All mutable bookkeeping (staged, tasks, failed) is touched only by the main loop thread — background tasks are pure (copy + return the path or throw), so there is no shared-state race.
AtmosTransport.Models.InputStaging.InputStager Method
InputStager(binary_paths, staging_cfg)Parse the [input.staging] sub-table and build the stager. Keys (all optional):
enabled: Bool (defaultfalse⇒ no staging, NAS paths used)dir: String (REQUIRED when enabled) — local NVMe directorylookahead_days: Int (default2)keep_behind_days: Int (default0)cleanup_on_exit: Bool (defaulttrue)
One run owns the staging directory at a time. If ownership or directory creation fails, use the original source paths. Retained copies are reused only when their recorded source metadata agrees; forcing files must stay immutable during a run.
sourceAtmosTransport.Models.InputStaging.cleanup_staging! Method
cleanup_staging!(mgr)Wait for any in-flight copies and remove all remaining staged files (when cleanup_on_exit). Safe to call unconditionally at run end.
AtmosTransport.Models.InputStaging.staged_path_for! Method
staged_path_for!(mgr, idx) -> StringReturn the path the day-idx driver should open. With staging enabled this ensures day idx is on local NVMe (blocking only if its copy is still in flight), kicks off async copies for the look-ahead window, and evicts processed days. Falls back to the NAS path if the copy failed. With staging disabled it returns the original NAS path unchanged.
AtmosTransport.Models.DrivenRunner Module
DrivenRunnerLibrary-level entry point for the driven transport runtime.
The canonical CLI, scripts/run_transport.jl, is a thin wrapper over run_driven_simulation(cfg). The library function handles LL/RG and CS runtime flows with dispatch driven by the first binary's header (inspect_binary(first_path).grid_type).
Ownership boundary
Binary header (
grid_type,mass_basis,payload_sections, panel convention) — authoritative for topology and capability. Accessed here viabinary_capabilities(driver.reader)andair_mass_basis(driver).TOML
[input]— either an explicitbinary_paths = [...]list (Shape A) or afolder + start_date + end_date (+ file_pattern)block (Shape B).expand_binary_pathspreserves explicit list order and sorts folder selections by date, checking gaps and duplicates.TOML physics (
[advection]/[diffusion]/[convection]) — validated against binary capabilities byvalidate_runtime_physics_recipe/build_runtime_physics_recipebefore the loop.TOML
[tracers.*]— tracer specs consumed viabuild_initial_mixing_ratio+pack_initial_tracer_mass(basis-aware perfeedback_vmr_to_mass_basis_aware) andbuild_surface_flux_sources.
Where the physics actually runs
DrivenRunner is the orchestration layer, not the kernel layer. It opens transport binaries, builds initial state, chooses output cadence, and installs the TOML-selected operators on a TransportModel. The live physics call chain is:
build_runtime_physics_recipereads[advection],[diffusion],[convection],[chemistry], and tracer surface-flux settings and returns the operator objects._make_structured_modelor the CS model constructor installs those operators onTransportModel(state, fluxes, grid, recipe.advection; ...).DrivenSimulation(model, driver; ...)loads the current met window and wraps chemistry and surface sources into the model withwith_chemistryandwith_emissions.The runtime loop below calls
run_window!(sim)for LL/RG orstep!(sim)for CS. Those functions live indriven_stepping.jl.DrivenSimulation.step!refreshes time-varying forcing from the driver, then callsTransportModel.step!or, for binary-scheduled substeps,transport_step!bracketed by exponential-decay half-steps, plus end-of-window convection (and unsplit non-decay chemistry).TransportModel.jlis where advection, surface emissions, diffusion, convection, and chemistry are applied to the state.
So, when following a run as a scientist: start here to understand data and configuration flow, then jump to DrivenSimulation.step! and TransportModel.step! to see the actual physics ordering.
GPU residency (feedback_verify_gpu_runs_on_gpu)
When [architecture].use_gpu = true or backend selects a GPU, the runner asserts that state.air_mass lives on the selected backend after model construction and prints a [gpu verified] … line. A silent CPU fallback aborts the run with a precise error message.
AtmosTransport.Models.DrivenRunner.TransportTracerSpec Type
TransportTracerSpecValidated runtime tracer configuration: tracer name, initial-condition configuration, and optional surface-flux configuration.
sourceAtmosTransport.Models.DrivenRunner.run_driven_simulation Method
run_driven_simulation(cfg::AbstractDict) -> TransportModelCanonical high-level entry point for scientists running an AtmosTransport TOML. Use this unless you are writing a custom time loop. It resolves [input] to a sorted binary list, prints a one-line summary for each binary, dispatches on the first binary's grid_type, validates physics-vs-capability, runs the loop, optionally writes topology-native snapshots, and returns the terminal TransportModel.
This function does not call the advection/diffusion/convection kernels directly. The handoff to physics happens inside the structured loop at run_window!(sim) and inside the CS loop at step!(sim). Both routes enter DrivenSimulation.step!, which refreshes forcing and then calls TransportModel.step! / transport_step! / convection_chemistry_step!.
AtmosTransport.Models.DrivenRunner.validate_config Method
validate_config(cfg::AbstractDict) -> (ok::Bool, errors::Vector{String})Check a driven runtime config and return errors without opening binary readers or allocating model state. Checks cover runtime table shapes (including input.staging, tracer init and surface_flux subtables, and output.observations with its sources array), resolved binary paths, numeric type, backend/float compatibility, the [output.observations] contract, and integer run-window bounds. The [advection], [diffusion], [convection], [chemistry], [logging] and [checks] sections, air_mass_reset_mode, physics_cadence, prefetch_windows and [output] are parsed as the run parses them; decay half-lives must name a tracer, and an enabled [output] needs both a path and snapshot times (an explicit hours = [] means none). Shape errors are reported before value checks. Window indices accept integers, not Booleans or floating-point values.
Unknown keys and tables are returned as errors (with a suggestion when a known key is close, e.g. order → ppm_order); known keys that the chosen kind or another setting leaves unread are logged as warnings (e.g. value unless [diffusion] kind = "constant").
This is not a complete physics or binary validation. Topology and payload capability checks run when run_driven_simulation inspects the first binary, which also checks that snapshot hours fall on met-window ends. GPU backend auto-detection can load and probe optional runtime packages.