Skip to content

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.DrivenSimulation Type
julia
DrivenSimulation

Low-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:

julia
using TOML, AtmosTransport
using AtmosTransport.MetDrivers: air_mass_basis, driver_grid

cfg = TOML.parsefile("config/runs/quickstart/ll72x37_advonly.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 (previously defined here) was migrated to src/Operators/SurfaceFlux/ in plan 17 Commit 2; it is still re-exported below for backward compatibility with external callers that imported it via AtmosTransport.SurfaceFluxSource.

source
AtmosTransport.Models.DrivenSimulation Method
julia
DrivenSimulation(model, driver; kwargs...)

Construct a window-driven src runtime.

Keyword arguments:

  • start_window=1

  • stop_window=total_windows(driver)

  • initialize_air_mass=true

  • use_midpoint_forcing=true

  • interpolate_fluxes_within_window=nothing (derive from driver)

  • reset_air_mass_each_window=false — when true, each newly loaded window replaces prognostic air mass while preserving tracer VMR. For binary-scheduled runs, the same endpoint reset is applied before the once-per-window convection/chemistry block so physics sees the binary's authoritative window-end mass.

  • surface_sources=()

  • chemistry=NoChemistry() — applied after advection + surface sources each step

  • callbacks=NamedTuple()

source
AtmosTransport.Models.Simulation Type
julia
Simulation

Low-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.

source
AtmosTransport.Models.build_runtime_chemistry Method
julia
build_runtime_chemistry(cfg, ::Type{FT}) -> AbstractChemistryOperator

Read 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 the half_lives_seconds table:

    [chemistry]
    kind = "decay"
    [chemistry.half_lives_seconds]
    rn222 = 330350.4   # 3.8235 days

    The keyword name must match the corresponding [tracers.<name>] symbol that the run is carrying (case-insensitive — the builder symbolizes the key as-is and ExponentialDecay.apply! resolves it against state.tracer_names at call time).

source
AtmosTransport.Models.convection_chemistry_step! Method
julia
convection_chemistry_step!(model::TransportModel, dt; meteo = nothing)

Advance the non-transport physics blocks once: convection block → chemistry block.

source
AtmosTransport.Models.step! Method
julia
step!(model::TransportModel, dt; meteo = nothing)

Advance model.state by one full runtime step: transport block (advection with vertical diffusion at the palindrome center, surface emissions wrapped by the two V half-steps when active) → convection block → chemistry block.

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 pre-refactor advection.

meteo is optional and defaults to nothing; pass a real meteorology object (AbstractMetDriver) or a DrivenSimulation (plan 18 A3) to thread current_time(meteo) through operators that consume time-varying fields.

source
AtmosTransport.Models.transport_step! Method
julia
transport_step!(model::TransportModel, dt; meteo = nothing)

Advance model.state by one transport step: advection with vertical diffusion at the palindrome center, surface emissions wrapped by the two V half-steps when active.

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!.

source
AtmosTransport.Models.with_chemistry Method
julia
with_chemistry(model::TransportModel, chemistry)

Return a copy of model with its chemistry operator replaced. All other fields share storage with the original. Used by DrivenSimulation up through plan 15 to install the sim-level chemistry operator into the model; plan 17 Commit 6 removed the sim-level workaround so this helper is now primarily useful for tests that want to swap chemistry on a constructed model.

source
AtmosTransport.Models.with_convection Method
julia
with_convection(model::TransportModel, convection)

Return a copy of model with its convection operator replaced (plan 18 Commit 2). 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 (plan 18 Commit 8) is responsible for allocating real buffers via allocate_convection_forcing_like after the first window loads (plan 18 v5.1 §2.20 Decision 26). 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.

source
AtmosTransport.Models.with_convection_forcing Method
julia
with_convection_forcing(model::TransportModel, forcing::ConvectionForcing)

Return a copy of model with its per-step convection-forcing container replaced (plan 18 Commit 2). All other fields — including the convection operator — share storage with the original.

Used by DrivenSimulation construction (plan 18 Commit 8) 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.

source
AtmosTransport.Models.with_diffusion Method
julia
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().

source
AtmosTransport.Models.with_emissions Method
julia
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 (plan 17 Commit 6) 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.

source