GeoStack · somax · box 3 and box 6 of the six-step cycle

Hybrid model taxonomy: what can be learned in an ocean model

The physical model is not one object. It is a stack of pieces: parameters, discretisation, terms, a tendency, an integrator, a step map, a rollout. Every hybrid or emulator learns one of those pieces and glues it back in one of a few ways. This page lists the ten that matter, on the same Gulf Stream QG example as the state-estimation page.

TL;DR

One signature, ten insides

Chip colour gives each type's usual job: teal mostly emulation, amber mostly correction. Several types do either; the card says which target each one takes.

Your 2 × 2, as a slice of the list

Classical integratorLearned integrator
Physical tendency
the physical modelno learning
T6 solver in the loopbig step + learned error
Hybrid tendency
T3, T4 closuresphysics + learned term or field
T3 or T4 inside T6both at once
Learned tendency
T5 neural ODEnetwork RHS, RK4
T8 pure step mapno tendency at all

The 2 × 2 misses three things: learning below the tendency (T1 parameters, T2 discretisation), correcting after the step (T7), and learning above it (T9 rollouts, T10 representations).

Notation

As on the state-estimation page, plus the pieces of the model.

SymbolMeaningKind, shape
\(\vect{x}_k\)state at day k; here the model's prognostic PV \(q\) on the grid (SSH \(\eta = f_0 \psi / g\) is a linear diagnostic of it)\(\mathbb{R}^{D_x}\) (variable, height, width)
\(\vect{u}^\text{cov}\), \(\vect{u}^\text{wind}_k\)exogenous inputs \(\vect{u}_k = (\vect{u}^\text{cov}, \vect{u}^\text{wind}_k)\): covariates (\(\beta s_2\), coordinates) and forcing (wind-stress curl)(variable, height, width), (height, width)
\(\gvect{\theta}\), \(\gvect{\alpha}\)physical parameters \((L_d, \nu)\), hyperparameters\(\mathbb{R}^{D_\theta}\) (param,), \(\mathbb{R}^{D_\alpha}\) (hyper,)
\(\mathcal{F}\)tendency, \(\partial_t q = \mathcal{F}[q; \vect{u}_k, \gvect{\theta}]\)operator, output (variable, height, width)
\(\mathrm{RK4}_{\Delta t}[\mathcal{F}]\)one integrator step of size Δt applied to the tendency \(\mathcal{F}\)operator
\(f\)step map: one day, \(\vect{x}_{k+1} = f(\vect{x}_k, \vect{u}_k; \gvect{\theta}) = \mathrm{RK4}_{\Delta t}[\mathcal{F}]^{48}(\vect{x}_k)\)\(\mathbb{R}^{D_x} \to \mathbb{R}^{D_x}\), field → field
\(f_{k \leftarrow 0}\), \(f_{1:T \leftarrow 0}\)k days of \(f\) composed; the rollout over a window, \((f_{1 \leftarrow 0}, \ldots, f_{T \leftarrow 0})\)\(\mathbb{R}^{D_x} \to \mathbb{R}^{T \times D_x}\), field → cube
\(\gvect{\theta}_\text{emu}\)weights of whatever is learned, emulator or correctionweights
\(g_\text{label}\), \(\mathcal{G}\)a learned map on vectors, a learned operator on fields, where the piece has no role letter of its own; the label names the piecemap, operator
\(\mathcal{C}\)coarse-graining from a finer grid to ours(height_hr, width_hr) → (height, width)

Note the clash: \(\nu\) in the equations below is the viscosity, as is standard in QG. The variational parameters \(\gvect{\nu}\) of the last page never appear on this page.

The physical model, taken apart

The 1.5-layer QG model of the last page, written layer by layer. Let \(\nabla := \nabla_{\vect{s}}\) act on the horizontal coordinates \(\vect{s} = (s_1, s_2)\), \(\mathcal{A} = \nabla^2 - L_d^{-2}\) be the Helmholtz operator and \(J(a, b) = \partial_{s_1} a \partial_{s_2} b - \partial_{s_2} a \partial_{s_1} b\) the Jacobian.

\[ \begin{aligned} \text{diagnostic solve: } & \psi = \mathcal{A}^{-1}[q - \beta s_2]\\ \text{terms: } & \mathcal{F}[q] = \underbrace{-J(\psi, q)}_{\text{advection}} \underbrace{- \nu \nabla^6 \psi}_{\text{dissipation}} + \underbrace{u^\text{wind}}_{\text{wind forcing}}\\ \text{integrator: } & q \leftarrow \mathrm{RK4}_{\Delta t}[\mathcal{F}](q) = q + \frac{\Delta t}{6} \left(\mathcal{F}^{(1)} + 2 \mathcal{F}^{(2)} + 2 \mathcal{F}^{(3)} + \mathcal{F}^{(4)}\right)\\ \text{step map: } & \vect{x}_{k+1} = f(\vect{x}_k, \vect{u}_k; \gvect{\theta}) = \mathrm{RK4}_{\Delta t}[\mathcal{F}]^{48}(\vect{x}_k)\\ \text{rollout: } & \vect{x}_{1:T} = f_{1:T \leftarrow 0}(\vect{x}_0, \vect{u}_{1:T}; \gvect{\theta}) \end{aligned} \]

Below the terms sits the discretisation: \(J\) is the Arakawa stencil and \(\nabla^2\) the five-point stencil on the C-grid, and \(\mathcal{A}^{-1}\) is a spectral or sine-transform solve. Each layer is a function with a signature, and each can be swapped.

The sketch below is the model in code. Every type in this page is an edit to one or two of its lines.

import equinox as eqx
import finitevolx as fvx
import jax
from jaxtyping import Array, Float

# Shapes: T = 10 days (time); H, W = 130 × 130 grid with ghost ring
#         (height, width); V covariates, β s_2 first (variable);
#         48 RK4 substeps per day


class QGParams(eqx.Module):
    """Physical parameters θ = (L_d, ν) of the 1.5-layer QG model."""

    deformation_radius: Float[Array, ""]
    viscosity: Float[Array, ""]


def qg_tendency(
    pv: Float[Array, "height width"],
    covariates: Float[Array, "variable height width"],
    forcing: Float[Array, "height width"],
    params: QGParams,
    grid: fvx.CartesianGrid2D,
) -> Float[Array, "height width"]:
    # today (finitevolx)
    difference = fvx.Difference2D(grid)
    # today (finitevolx)
    jacobian = fvx.ArakawaJacobian2D(grid)
    beta_y = covariates[0]                            # (H, W)

    # --- diagnostic solve ---
    # q − β s_2
    relative_pv = pv - beta_y                         # (H, W) → (H, W)
    # L_d²
    radius_squared = params.deformation_radius**2     # → ()
    # L_d⁻²
    helmholtz_shift = 1.0 / radius_squared            # → ()
    # today* (finitevolx)
    # ψ = 𝒜⁻¹[q − β s_2],  𝒜 = ∇² − L_d⁻²
    streamfunction = fvx.pv_inversion(                # (H, W) → (H, W)
        relative_pv, grid.dx, grid.dy, lambda_=helmholtz_shift, bc="dst"
    )

    # --- terms ---
    # today (finitevolx)
    # J(ψ, q)
    jacobian_term = jacobian(streamfunction, pv)      # (H, W)
    # advection = −J(ψ, q)
    advection = -jacobian_term                        # (H, W)
    # today (finitevolx)
    # ∇²ψ
    laplacian_1 = difference.laplacian(streamfunction)  # (H, W)
    # today (finitevolx)
    # ∇⁴ψ = ∇²(∇²ψ)
    laplacian_2 = difference.laplacian(laplacian_1)   # (H, W)
    # today (finitevolx)
    # ∇⁶ψ = ∇²(∇⁴ψ)
    laplacian_3 = difference.laplacian(laplacian_2)   # (H, W)
    # dissipation = −ν ∇⁶ψ
    dissipation = -params.viscosity * laplacian_3     # (H, W)

    # ℱ[q] = −J(ψ, q) − ν∇⁶ψ + u^wind
    tendency = advection + dissipation + forcing      # (H, W)
    return tendency


def qg_day(
    pv: Float[Array, "height width"],
    covariates: Float[Array, "variable height width"],
    forcing: Float[Array, "height width"],
    params: QGParams,
    grid: fvx.CartesianGrid2D,
    *,
    dt: float = 1800.0,
    n_substeps: int = 48,
) -> Float[Array, "height width"]:
    def rhs(state):
        # user
        # ℱ[q; u_k, θ]
        return qg_tendency(state, covariates, forcing, params, grid)

    def substep(state, _):
        # today (finitevolx)
        # q ← RK4_Δt[ℱ](q), classic RK4
        state = fvx.rk4_step(state, rhs, dt)          # (H, W) → (H, W)
        return state, None

    # f(x_k, u_k; θ) = RK4_Δt[ℱ]⁴⁸(x_k)
    next_pv, _ = jax.lax.scan(                        # (H, W) → (H, W)
        substep, pv, None, length=n_substeps
    )
    return next_pv


def qg_rollout(
    initial_pv: Float[Array, "height width"],
    covariates: Float[Array, "variable height width"],
    forcing: Float[Array, "time height width"],
    params: QGParams,
    grid: fvx.CartesianGrid2D,
) -> Float[Array, "time height width"]:
    def day(state, daily_forcing):
        # user
        # x_k = f(x_{k−1}, u_k; θ)
        state = qg_day(                               # (H, W) → (H, W)
            state, covariates, daily_forcing, params, grid
        )
        return state, state

    # f_{1:T←0}(x₀, u_{1:T}; θ) = (x₁, …, x_T)
    _, trajectory = jax.lax.scan(                     # (H, W) → (T, H, W)
        day, initial_pv, forcing
    )
    return trajectory

Markers: today ships on main (checked 24 Oct 2026: finitevolX 831c69f, gaussx eeda235, somax 9cb19b3); today* ships, arguments sketched; user is defined on these pages; not pinned is a library whose API was not checked. Shape column letters are defined in the header.

somax ships barotropic and baroclinic QG models but no 1.5-layer one, so the tendency here is page-defined. BaroclinicQG.create(n_layers=1) may be the shipped route, but it is untested.

TypeThe edit
T1params.viscosity → viscosity_net(phi, covariates)
T2jacobian and difference.laplacian use learned stencil weights
T3tendency = … - div(flux_net(phi, pv, streamfunction))
T4add div(kappa_net(phi, pv, streamfunction) * grad(pv)), with kappa ≥ 0
T5qg_tendency body → tendency_net(phi, pv, covariates, forcing, params)
T6qg_day: 6 substeps of 8 * dt, each followed by + correction_net(phi, state)
T7qg_day returns next_pv + residual_net(phi, pv, covariates, forcing)
T8qg_day body → step_net(phi, pv, covariates, forcing, params)
T9qg_rollout body → rollout_net(phi, initial_pv, forcing, covariates, params)
T10wrap qg_day in encoder and decoder, run it on the embedding

Three axes

Which layer is learned

The diagram above. Lower layers (T1, T2) keep the most physics and fix the least; upper layers (T8, T9) keep the least and are fastest.

How the learned piece is glued in

GlueFormGuaranteeTypes
Additive\(\mathcal{F}_\text{phys} + \mathcal{G}\), \(f + g_\text{res}\)none; the network can undo the physicsT3, T7
Parametricthe network outputs a coefficient the physics uses: \(g_\nu(\vect{u}^\text{cov}; \gvect{\theta}_\text{emu})\), \(g_\kappa\)the operator bounds what it can doT1, T4
Structuraloutput forced into a form: \(-\nabla \cdot \mathcal{G}_\text{flux}\), \(\kappa \ge 0\), flux-form stencilsconservation or dissipation by constructionT2, T3 (flux), T4
Splittingphysics step, then correction stepphysics untouched; error tied to the step sizeT6
Replacementthe network is the pieceonly the signatureT5, T8, T9

What it is trained against

Two jobs, three sources of reference, four loss levels. Let \(\vect{x}^\text{ref}\) be the reference trajectory and \(\mathcal{F}_\text{ref}\) its tendency.

JobReferenceGround truth box
emulation\(\vect{x}^\text{ref}\) from the physical model itselfstep 1
correction\(\vect{x}^\text{ref} = \mathcal{C}[\vect{x}^\text{hr}]\), a finer or richer run coarse-grained to our grida better step 1
correctionobservations \(\vect{y}_{1:T}\) through \(\mat{H}_k\)the ocean

The loss can sit at four levels, from cheapest to most faithful to how the model will be used:

\[ \begin{aligned} \mathcal{L}_\text{tendency}(\gvect{\theta}_\text{emu}) &= \mathbb{E} \Vert \mathcal{F}_\text{ref}[q] - \mathcal{F}[q; \gvect{\theta}_\text{emu}] \Vert^2\\ \mathcal{L}_\text{step}(\gvect{\theta}_\text{emu}) &= \mathbb{E} \Vert \vect{x}^\text{ref}_{k+1} - f(\vect{x}^\text{ref}_k; \gvect{\theta}_\text{emu}) \Vert^2\\ \mathcal{L}_\text{rollout}(\gvect{\theta}_\text{emu}) &= \mathbb{E} \sum_{k=1}^{T} \Vert \vect{x}^\text{ref}_k - f_{k \leftarrow 0}(\vect{x}^\text{ref}_0; \gvect{\theta}_\text{emu}) \Vert^2\\ \mathcal{L}_\text{obs}(\gvect{\theta}_\text{emu}) &= \mathbb{E} \sum_{k=1}^{T} \Vert \vect{y}_k - \mat{H}_k f_{k \leftarrow 0}(\vect{x}_0; \gvect{\theta}_\text{emu}) \Vert^2_{\gmat{\Sigma}_{\varepsilon,k}^{-1}} \end{aligned} \]

For correction against a finer run, the tendency target is the subgrid forcing: what the coarse physics misses when applied to the coarse-grained state,

\[ \mathcal{F}_\text{sub} = \mathcal{C}\left[\mathcal{F}_\text{hr}[q^\text{hr}]\right] - \mathcal{F}_\text{phys}\left[\mathcal{C}[q^\text{hr}]\right]. \]

The tendency and one-step losses are a priori: cheap, but the model never sees its own errors, so long rollouts often go unstable. The rollout and observation losses are a posteriori: they differentiate through the solver, which is what a JAX model makes affordable. The observation loss also needs each window's \(\vect{x}_0\), so learning from data is 4D-Var with \(\gvect{\theta}_\text{emu}\) added to the control vector.

The ten types

Each card gives the equation, the operator signature with what is learned, and the training target as a column. Shapes use the named dimensions of the last page.

T1

Learned parameters

layer: parameters

The equations are fixed. Constants become leaves, or a network maps the covariates to a parameter field:

\[ \gvect{\theta} \to g_\text{param}(\vect{u}^\text{cov}; \gvect{\theta}_\text{emu}) \] \[ \mathcal{F}[q] = -J(\psi, q) - g_\nu(\vect{u}^\text{cov}; \gvect{\theta}_\text{emu}) \nabla^6 \psi + u^\text{wind} . \]
inx_k(variable, height, width)u^cov(variable, height, width)u^wind_k(height, width)outx_k+1(variable, height, width)learnedθ(param,)g_param(u^cov)(param, height, width)
Learned
constants, or a parameter field from a small network on u^cov
Glue
parametric: the physics consumes the learned numbers
Target
correctiondata or a richer model; emulationstep 1 only to test identifiability
Regime
a posteriori, through the rollout (the parameters have no local target)
Keeps
everything: conservation, Δt freedom, a correct adjoint
Six-step
box 2's inner loop when θ is constant, box 6 when it becomes a field

The cheapest hybrid and the most honest one. It can only fix errors the equations already have room for: a missing process stays missing.

T2

Learned discretisation

layer: discretisation

The PDE is fixed; the stencil weights are learned. With offsets \((p, r) \in \mathcal{O}\) and weights \(w\), a derivative or face value is a weighted sum of shifted copies. In flux form the update telescopes, so the domain integral of \(q\) is conserved whatever the weights are:

\[ (\mathcal{D}[q])_{j,i} = \sum_{(p, r) \in \mathcal{O}} w_{pr}(q; \gvect{\theta}_\text{emu}) q_{j+p, i+r} \] \[ \mathcal{F}[q] = -\nabla \cdot \left(\vect{v} q^\text{face}(q; \gvect{\theta}_\text{emu})\right) . \]
inx_k(variable, height, width)u^cov(variable, height, width)u^wind_k(height, width)θ(param,)outx_k+1(variable, height, width)learnedw(offset,)w(q; θ_emu)(offset, height, width)
Learned
stencil coefficients, constant or predicted per point from the local state
Glue
structural: the learned numbers sit inside the stencil, the PDE is untouched
Target
correctiona finer-grid run coarse-grained to this grid (numerics error, not physics error)
Regime
a posteriori; a priori on face values is possible but drifts
Keeps
conservation (flux form), Δt freedom, the C-grid staggering
Six-step
box 6: it changes how box 1 is computed

Lets a coarse grid behave like a finer one. With constant weights it still compiles to one slice fusion, and its Fourier symbol is inspectable, so you can see what it learned.

T3

Physics plus a learned term

layer: terms (additive)

A network adds the missing term. In flux form the term moves PV around without creating it:

\[ \mathcal{F}[q] = \underbrace{-J(\psi, q) - \nu \nabla^6 \psi + u^\text{wind}}_{\mathcal{F}_\text{phys}[q]} + \mathcal{G}[q, \psi; \vect{u}_k, \gvect{\theta}_\text{emu}] \] \[ \mathcal{G} = -\nabla \cdot \mathcal{G}_\text{flux}[q, \psi; \gvect{\theta}_\text{emu}] . \]
inx_k(variable, height, width)u^cov(variable, height, width)u^wind_k(height, width)θ(param,)outx_k+1(variable, height, width)learnedθ_emunetwork𝒢_flux(2, height, width) flux
Learned
one tendency term: a subgrid PV flux, a missing forcing
Glue
additive, or structural when written as a flux divergence
Target
correctionsubgrid forcing from a coarse-grained high-resolution run, or observations
Regime
a priori on the subgrid forcing, then a posteriori; a priori alone is often unstable
Keeps
Δt freedom; conservation only in flux form; an adjoint as good as 𝒢's Jacobian
Six-step
box 6: a structural change to box 1

The workhorse of hybrid modelling. Its danger is identifiability: trained jointly with θ, an additive term can absorb part of the physics.

T4

Learned fields in a known form

layer: terms (parametric)

The network never writes a tendency. It supplies a field that a physics operator then uses. An eddy diffusivity kept non-negative is dissipative by construction:

\[ \mathcal{F}[q] = -J(\psi, q) + \nabla \cdot \left(\kappa \nabla q\right) - \nu \nabla^6 \psi + u^\text{wind} \] \[ \kappa = \mathrm{softplus}\left(g_\kappa(q, \psi, \vect{u}^\text{cov}; \gvect{\theta}_\text{emu})\right) \ge 0 . \]
inx_k(variable, height, width)u^cov(variable, height, width)u^wind_k(height, width)θ(param,)outx_k+1(variable, height, width)learnedθ_emunetworkκ = softplus(g_κ)(height, width)
Learned
a coefficient field that depends on the state: diffusivity, an advecting velocity, a source
Glue
parametric and structural: the operator fixes what the field can do
Target
correctioncoarse-grained high-resolution run, or observations
Regime
a posteriori
Keeps
the sign of energy or enstrophy change, conservation, Δt freedom
Six-step
box 6

Sits between T1 and T3. Less expressive than a free term, much harder to break, and the learned field is a physical quantity you can map and inspect.

T5

Neural tendency, classical integrator

layer: tendency

The whole right-hand side is a network; the time stepping stays classical:

\[ \partial_t q = \mathcal{F}[q; \vect{u}_k, \gvect{\theta}, \gvect{\theta}_\text{emu}] \] \[ \vect{x}_{k+1} = \mathrm{RK4}_{\Delta t}[\mathcal{F}]^{48}(\vect{x}_k) . \]
inx_k(variable, height, width)u^cov(variable, height, width)u^wind_k(height, width)θ(param,)outx_k+1(variable, height, width)learnedθ_emunetworkℱ[q; θ_emu](variable, height, width)
Learned
the tendency
Glue
replacement
Target
emulationtendencies of step 1, exact and free; correctiona richer model or data
Regime
a priori on tendencies, then a posteriori
Keeps
Δt freedom and the integrator's stability region; loses conservation
Six-step
box 3 when emulating, box 6 when correcting

For QG this is no faster than the physics it replaces, because the network is called 192 times a day (48 steps × 4 RK4 stages). It earns its place when the true tendency is unknown, not when it is expensive.

T6

Physical tendency, learned integrator

layer: integrator

Take a large step \(\Delta t_\text{big} = 8 \Delta t\) that the classical integrator gets wrong, and learn the time-discretisation error. Six corrected big steps replace 48 small ones:

\[ \vect{x} \leftarrow \mathrm{RK4}_{\Delta t_\text{big}}[\mathcal{F}_\text{phys}](\vect{x}) + g_\text{corr}(\vect{x}; \gvect{\theta}_\text{emu}) \text{, repeated 6 times per day.} \]
inx_k(variable, height, width)u^cov(variable, height, width)u^wind_k(height, width)θ(param,)outx_k+1(variable, height, width)learnedθ_emunetworkg_corr(x)(variable, height, width)
Learned
the error of a large time step
Glue
splitting: physics step, then correction
Target
emulationthe same physics at the small Δt (step 1)
Regime
a posteriori through the solver
Keeps
the physical tendency; loses Δt freedom (Δt_big is baked in)
Six-step
box 3

Speed with the physics still visible. The correction is trained for one Δt_big and one grid; change either and it must be retrained.

T7

Residual on the step map

layer: step map (additive)

Run the physical model for a day, then add a learned correction:

\[ \vect{x}_{k+1} = f(\vect{x}_k, \vect{u}_k; \gvect{\theta}) + g_\text{res}(\vect{x}_k, \vect{u}_k; \gvect{\theta}_\text{emu}) . \]
inx_k(variable, height, width)u^cov(variable, height, width)u^wind_k(height, width)θ(param,)outx_k+1(variable, height, width)learnedθ_emunetworkg_res(x)(variable, height, width)
Learned
the one-day error of the physical model
Glue
additive, after the step
Target
correctionreanalysis states or a richer model sampled daily
Regime
one-step, then rollout
Keeps
nothing beyond what f(·; θ) gives; loses Δt freedom
Six-step
box 6

Easy to bolt onto an existing model, and g_res itself is a map of where the model is wrong. It mixes physics error and numerics error, so it does not tell you which one to fix.

T8

Pure step map

layer: step map (replacement)

One network evaluation replaces the whole day:

\[ \vect{x}_{k+1} = f(\vect{x}_k, \vect{u}_k; \gvect{\theta}, \gvect{\theta}_\text{emu}) . \]
inx_k(variable, height, width)u^cov(variable, height, width)u^wind_k(height, width)θ(param,)outx_k+1(variable, height, width)learnedθ_emunetwork
Learned
the one-day map
Glue
replacement
Target
emulationstep 1 trajectories; correctionreanalysis
Regime
one-step, then rollout fine-tuning for stability
Keeps
nothing by construction; same signature as f, so it drops into any cycle
Six-step
box 3 (the emulator of the last page)

The largest speed-up per day, and the one whose Jacobian is least trustworthy for 4D-Var. Long rollouts drift toward smooth or unstable states unless trained on them.

T9

Rollout operator

layer: rollout

The network maps an initial state and a forcing sequence to a whole window at once:

\[ \vect{x}_{1:T} = f_{1:T \leftarrow 0}(\vect{x}_0, \vect{u}_{1:T}; \gvect{\theta}, \gvect{\theta}_\text{emu}) . \]
inx_0(variable, height, width)u_1:T(time, height, width)θ(param,)outx_1:T(time, variable, height, width)learnedθ_emunetwork
Learned
the trajectory
Glue
replacement
Target
emulationstep 1 windows; correctionreanalysis windows
Regime
trajectory loss
Keeps
nothing; the horizon T is fixed
Six-step
box 3

The signature changes from field → field to field → cube, so it cannot be stepped, cycled or restarted mid-window. It fits a fixed 10-day 4D-Var window and little else.

T10

Learned representation

layer: representation (orthogonal)

An encoder maps the state to an embedding \(\vect{z}_k \in \mathbb{R}^{D_z}\), any of T5, T8 or T9 runs there, and a decoder maps back. The embedding is the latent \(\vect{z}\) because \(\vect{x}\) is already the state of the probabilistic model:

\[ \vect{z}_k = g_\text{enc}(\vect{x}_k; \gvect{\theta}_\text{emu}) \] \[ \vect{z}_{k+1} = f_\text{latent}(\vect{z}_k, \vect{u}_k; \gvect{\theta}_\text{emu}) \] \[ \hat{\vect{x}}_{k+1} = g_\text{dec}(\vect{z}_{k+1}; \gvect{\theta}_\text{emu}) . \]
inx_k(variable, height, width)outx_k+1(variable, height, width)learnedθ_emug_enc, g_dec and dynamicsz_k(channel, height′, width′)
Learned
the space the dynamics run in, plus the dynamics
Glue
composition g_enc → dynamics → g_dec
Target
emulationor correction, plus reconstruction ‖x − g_dec(g_enc(x))‖²
Regime
reconstruction and latent rollout, jointly or in stages
Keeps
whatever the inner type keeps, in the embedding only
Six-step
box 3

Cheap dynamics in a small space. For assimilation, the observation operator becomes H_k ∘ g_dec, so every 4D-Var gradient goes through the decoder, and the decoder's errors become observation errors no Σ_ε accounts for.

Selection matrix

● yes   ◐ partly or with care   ○ no

TypeFaster than fCan fix model errorFree ΔtUsable adjointConserves by constructionInspectable
T1 parameters○◐●●●●
T2 discretisation●◐●●●●
T3 learned term◐●●◐◐◐
T4 learned field◐●●◐●●
T5 neural tendency○●●◐○○
T6 learned integrator●○○◐○◐
T7 step residual○●○◐○◐
T8 step map●◐○○○○
T9 rollout●◐○○○○
T10 representation●◐◐◐○○

"Faster" is for this QG model, where the physics is already cheap. T2 is faster because a learned stencil lets a coarser grid match a finer one, not because a stencil is quicker. "Can fix model error ◐" for T8–T10 means only when trained on reanalysis or data instead of step 1.

What the structure implies

Emulation and correction are different boxes

An emulator answers "same model, faster" and is scored against step 1. A correction answers "better model" and is scored against a richer model or the ocean. Benchmarking a closure against the QG model, or an emulator against observations, measures the wrong thing. For this QG example emulation buys little; correction is where the value is.

Additive terms are not identifiable

Fit θ and an additive \(\mathcal{G}\) together and the network can learn part of \(\nu \nabla^6 \psi\) or of \(\beta s_2\), leaving θ meaningless. The fixes, in order of preference: a parametric or structural form (T4, flux-form T3); fit θ first and freeze it; penalise \(\Vert \mathcal{G} \Vert^2\).

For 4D-Var, learn low in the stack

T1–T5 keep a classical integrator, so stability, Δt and the meaning of the adjoint come from the physics. T6–T9 trade that for speed, and a step map that matches values can still have the wrong Jacobian. If a type above T5 is meant for step 4, its training should include Jacobian–vector products, not only values.

A priori training is a trap for T3–T5

A term that fits the subgrid forcing perfectly on reference states meets its own errors in a rollout and can blow up within days. Use the a priori loss to initialise, then train a posteriori through the solver.

Where each type lives

TypeHook in the stack
T1differentiable constants in somax term kernels; a JaxOperator for the parameter field
T2the stencil-as-data layer in finitevolX, with a learnable stencil module in somax
T3, T4OperatorTerm wrapping a network, added to the somax term tree
T5a term tree with one OperatorTerm and no physics terms
T6, T7, T8a JaxOperator with the step-map signature, used as the cycle's step
T9, T10a JaxOperator outside the step interface; T10 also changes the DA observation operator
all, a posteriorirollout_loss on a scanned cycle with checkpointing