Time Integration
The generic time steppers live in integrix (integrix.march); finitevolX keeps the semi-Lagrangian scheme and the solve_ocean_pde wrapper.
Deprecated finitevolX names
finitevolx.RK3SSP, finitevolx.rk3_ssp_step and the other generic steppers below still work for one release and emit a DeprecationWarning. The diffrax solvers and the pure step functions are the integrix objects themselves, so import them from integrix.march. finitevolx.AB2Solver, finitevolx.LeapfrogRAFSolver and finitevolx.SplitExplicitRKSolver are adapters that keep the old init / step call shapes; their integrix replacements are integrix.march.AbstractSteppers run by integrix.march.run(stepper, rhs, t0, y0, dt, n_steps) with rhs(t, y, args).
Explicit Runge-Kutta (Pure Functional)
integrix.march.euler_step(state, rhs_fn, dt)
One forward-Euler step: yₙ₊₁ = yₙ + h f(yₙ).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
PyTree
|
The state yₙ, any pytree. |
required |
rhs_fn
|
Callable[[PyTree], PyTree]
|
The autonomous right-hand side f(y). |
required |
dt
|
ScalarLike
|
The step h. |
required |
Returns:
| Type | Description |
|---|---|
PyTree
|
The state yₙ₊₁. |
Examples:
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.heun_step(state, rhs_fn, dt)
One Heun (RK2) step, order 2, SSP with C = 1.
k₁ = f(yₙ), k₂ = f(yₙ + h k₁), yₙ₊₁ = yₙ + (h/2)(k₁ + k₂).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
PyTree
|
The state yₙ, any pytree. |
required |
rhs_fn
|
Callable[[PyTree], PyTree]
|
The autonomous right-hand side f(y). |
required |
dt
|
ScalarLike
|
The step h. |
required |
Returns:
| Type | Description |
|---|---|
PyTree
|
The state yₙ₊₁. |
Examples:
>>> import jax.numpy as jnp
>>> bool(jnp.allclose(heun_step(jnp.array(1.0), lambda y: -y, 0.1), 0.905))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.rk3_ssp_step(state, rhs_fn, dt)
One third-order SSP Runge–Kutta step in Shu–Osher form, C = 1.
y⁽¹⁾ = yₙ + h f(yₙ); y⁽²⁾ = ¾ yₙ + ¼ y⁽¹⁾ + ¼ h f(y⁽¹⁾); yₙ₊₁ = ⅓ yₙ + ⅔ y⁽²⁾ + ⅔ h f(y⁽²⁾).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
PyTree
|
The state yₙ, any pytree. |
required |
rhs_fn
|
Callable[[PyTree], PyTree]
|
The autonomous right-hand side f(y). |
required |
dt
|
ScalarLike
|
The step h. |
required |
Returns:
| Type | Description |
|---|---|
PyTree
|
The state yₙ₊₁. |
Examples:
>>> import jax.numpy as jnp
>>> y = rk3_ssp_step(jnp.array(1.0), lambda y: -y, 0.1)
>>> bool(jnp.allclose(y, 1.0 - 0.1 + 0.1**2 / 2 - 0.1**3 / 6))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.rk4_step(state, rhs_fn, dt)
One classical fourth-order Runge–Kutta step.
k₁ = f(yₙ), k₂ = f(yₙ + (h/2) k₁), k₃ = f(yₙ + (h/2) k₂), k₄ = f(yₙ + h k₃), yₙ₊₁ = yₙ + (h/6)(k₁ + 2k₂ + 2k₃ + k₄).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
PyTree
|
The state yₙ, any pytree. |
required |
rhs_fn
|
Callable[[PyTree], PyTree]
|
The autonomous right-hand side f(y). |
required |
dt
|
ScalarLike
|
The step h. |
required |
Returns:
| Type | Description |
|---|---|
PyTree
|
The state yₙ₊₁. |
Examples:
>>> import jax.numpy as jnp
>>> y = rk4_step({"a": jnp.array(1.0)}, lambda y: {"a": -y["a"]}, 0.1)
>>> bool(jnp.allclose(y["a"], jnp.exp(-0.1), rtol=1e-6))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
Multistep Methods (Pure Functional)
integrix.march.ab2_step(state, rhs_fn, dt, rhs_nm1)
One second-order Adams–Bashforth step.
yₙ₊₁ = yₙ + (h/2)(3 fₙ − fₙ₋₁), one evaluation of f per step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
PyTree
|
The state yₙ, any pytree. |
required |
rhs_fn
|
Callable[[PyTree], PyTree]
|
The autonomous right-hand side f(y). |
required |
dt
|
ScalarLike
|
The step h. |
required |
rhs_nm1
|
PyTree
|
fₙ₋₁, the right-hand side one step back. |
required |
Returns:
| Type | Description |
|---|---|
tuple[PyTree, PyTree, PyTree]
|
|
Examples:
>>> import jax.numpy as jnp
>>> y, f_n, _ = ab2_step(jnp.array(1.0), lambda y: -y, 0.1, -1.0)
>>> bool(jnp.allclose(y, 0.9)), bool(f_n == -1.0)
(True, True)
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_multistep.py
integrix.march.ab3_step(state, rhs_fn, dt, rhs_nm1, rhs_nm2)
One third-order Adams–Bashforth step.
yₙ₊₁ = yₙ + (h/12)(23 fₙ − 16 fₙ₋₁ + 5 fₙ₋₂), one evaluation of f per
step. The start must be at least second-order accurate to keep the
global order 3: bootstrap y₁ and y₂ with rk3_ssp_step, as AB3Solver
does; the crude start fₙ₋₂ = fₙ₋₁ = f(y₀) degrades AB3 to second order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
PyTree
|
The state yₙ, any pytree. |
required |
rhs_fn
|
Callable[[PyTree], PyTree]
|
The autonomous right-hand side f(y). |
required |
dt
|
ScalarLike
|
The step h. |
required |
rhs_nm1
|
PyTree
|
fₙ₋₁, the right-hand side one step back. |
required |
rhs_nm2
|
PyTree
|
fₙ₋₂, two steps back. |
required |
Returns:
| Type | Description |
|---|---|
PyTree
|
|
PyTree
|
the next |
Examples:
>>> import jax.numpy as jnp
>>> f = lambda y: -y
>>> y, f_n, f_nm1 = ab3_step(jnp.array(1.0), f, 0.1, f(1.0), f(1.0))
>>> bool(jnp.allclose(y, 0.9)), bool(f_n == -1.0)
(True, True)
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_multistep.py
integrix.march.leapfrog_raf_step(state, state_nm1, rhs_fn, dt, alpha=0.05)
One leapfrog step with a Robert–Asselin filter on the middle level.
ỹₙ₊₁ = yₙ₋₁ + 2h f(yₙ) and ȳₙ = yₙ + α (yₙ₋₁ − 2yₙ + ỹₙ₊₁). The filtered ȳₙ damps the computational mode of the three-level scheme. Second order for α = 0 and first order for α > 0 (the filter's O(αh) damping; the measured rate is about 1.1 at the default 0.05).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
PyTree
|
The state yₙ, any pytree. |
required |
state_nm1
|
PyTree
|
The (filtered) state yₙ₋₁. |
required |
rhs_fn
|
Callable[[PyTree], PyTree]
|
The autonomous right-hand side f(y). |
required |
dt
|
ScalarLike
|
The step h. |
required |
alpha
|
float
|
The filter coefficient α, typically 0.01 to 0.1. |
0.05
|
Returns:
| Type | Description |
|---|---|
tuple[PyTree, PyTree]
|
|
Examples:
>>> import jax.numpy as jnp
>>> y_next, filtered = leapfrog_raf_step(1.0, 1.0, lambda y: 0.0 * y, 0.1)
>>> bool(y_next == 1.0), bool(filtered == 1.0)
(True, True)
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_multistep.py
IMEX (Pure Functional)
integrix.march.imex_ssp2_step(state, rhs_explicit, rhs_implicit, implicit_solve, dt)
One IMEX-SSP2(2,2,2) step, with the caller's implicit solve.
Second order in the explicit part, the implicit part and the coupled split (the implicit row (1 − 2γ, γ) gives ∑ᵢ bᵢ cᵢ = ½).
Algorithm
- Y₁ = yₙ + γh f_I(Y₁):
implicit_solve(yₙ, γh). - Y₂ = yₙ + h f_E(Y₁) + (1 − 2γ) h f_I(Y₁), then
Y₂ = Y₂ + γh f_I(Y₂):
implicit_solve(Y₂*, γh). - yₙ₊₁ = yₙ + (h/2)(f_E(Y₁) + f_E(Y₂)) + (h/2)(f_I(Y₁) + f_I(Y₂)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
PyTree
|
The state yₙ, any pytree. |
required |
rhs_explicit
|
Callable[[PyTree], PyTree]
|
The non-stiff right-hand side f_E(y). |
required |
rhs_implicit
|
Callable[[PyTree], PyTree]
|
The stiff right-hand side f_I(y). |
required |
implicit_solve
|
Callable[[PyTree, ScalarLike], PyTree]
|
Solves Y − γh f_I(Y) = r for Y, given (r, γh); for vertical diffusion, a tridiagonal solve along columns. |
required |
dt
|
ScalarLike
|
The step h. |
required |
Returns:
| Type | Description |
|---|---|
PyTree
|
The state yₙ₊₁. |
Examples:
>>> import jax.numpy as jnp
>>> y = imex_ssp2_step(
... jnp.array(1.0),
... lambda y: -y,
... lambda y: -10.0 * y,
... lambda r, gamma_dt: r / (1.0 + 10.0 * gamma_dt),
... 0.01,
... )
>>> bool(jnp.allclose(y, jnp.exp(-0.11), rtol=1e-4))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_imex.py
Split-Explicit (Pure Functional)
integrix.march.split_explicit_step(state_3d, state_2d, rhs_3d, rhs_2d, couple_2d_to_3d, dt_slow, n_substeps, slow_forcing=None, t0=0.0)
One split-explicit barotropic/baroclinic step.
The fast subcycle is neutrally stable for the oscillatory gravity-wave
mode (RK4 amplification 1 − (ωh)⁶/144 per substep); the slow step is first
order in h. The returned 2-D state is the end-of-cycle y_M, which stays on
time; the slow step used the mean ⟨y⟩, and making the 3-D state consistent
with it (replacing the depth mean of the 3-D velocity by the averaged
transport, say) is couple_2d_to_3d's job.
Algorithm
- F =
slow_forcing(state_3d), if given, held fixed over the cycle. - Subcycle the 2-D state with M RK4 steps of h/M on
y′ =
rhs_2d(t, y, state_3d)+ F, and form its trapezoidal mean ⟨y⟩ over the cycle. - One forward-Euler step of the 3-D state:
y₃ + h
rhs_3d(state_3d, ⟨y⟩). couple_2d_to_3d(new state_3d, ⟨y⟩).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_3d
|
PyTree
|
The slow (baroclinic) state. |
required |
state_2d
|
PyTree
|
The fast (barotropic) state. |
required |
rhs_3d
|
Callable[[PyTree, PyTree], PyTree]
|
The slow tendency |
required |
rhs_2d
|
Callable[[ScalarLike, PyTree, PyTree], PyTree]
|
The fast tendency |
required |
couple_2d_to_3d
|
Callable[[PyTree, PyTree], PyTree]
|
|
required |
dt_slow
|
ScalarLike
|
The slow step h. |
required |
n_substeps
|
int
|
The number of fast substeps M ≥ 1 per slow step. |
required |
slow_forcing
|
Callable[[PyTree], PyTree] | None
|
|
None
|
t0
|
ScalarLike
|
The time at the start of the slow step. |
0.0
|
Returns:
| Type | Description |
|---|---|
tuple[PyTree, PyTree]
|
|
Raises:
| Type | Description |
|---|---|
ValidationError
|
If |
Examples:
>>> import jax.numpy as jnp
>>> slow, fast = split_explicit_step(
... jnp.array(1.0),
... jnp.array(1.0),
... lambda y3, mean_2d: -mean_2d,
... lambda t, y2, y3: 0.0 * y2,
... lambda y3, mean_2d: y3,
... 0.1,
... 5,
... )
>>> bool(jnp.allclose(slow, 0.9)), bool(fast == 1.0)
(True, True)
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_split_explicit.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
Semi-Lagrangian (Pure Functional)
finitevolx.semi_lagrangian_step(field, u, v, dx, dy, dt, interp_order=1, bc='periodic')
Advect a 2D scalar field using semi-Lagrangian backtracking.
Algorithm::
1. Compute departure points: x_dep = x_i - u * dt,
y_dep = y_j - v * dt
2. Interpolate ``field`` at the departure points.
3. Return interpolated values as the new field.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field
|
Array[Ny, Nx]
|
Scalar field to advect. |
required |
u
|
Array[Ny, Nx]
|
Velocity components at the same grid points as |
required |
v
|
Array[Ny, Nx]
|
Velocity components at the same grid points as |
required |
dx
|
float
|
Grid spacing in x and y (m). |
required |
dy
|
float
|
Grid spacing in x and y (m). |
required |
dt
|
float
|
Timestep (s). |
required |
interp_order
|
int
|
Interpolation order passed to :func: |
1
|
bc
|
str
|
Boundary handling: |
'periodic'
|
Returns:
| Type | Description |
|---|---|
Array[Ny, Nx]
|
Advected field. |
Source code in finitevolx/_src/timestepping/semi_lagrangian.py
Diffrax Solvers
integrix.march.ForwardEulerDfx
Bases: AbstractSolver
Forward Euler as a diffrax solver: yₙ₊₁ = yₙ + h F(tₙ, yₙ).
Order 1, one stage, no error estimate (fixed steps only). Included for
completeness and parity with finitevolX; diffrax.Euler is the same
scheme.
Examples:
>>> import diffrax as dfx
>>> import jax.numpy as jnp
>>> solution = dfx.diffeqsolve(
... dfx.ODETerm(lambda t, y, args: -y),
... ForwardEulerDfx(),
... t0=0.0,
... t1=1.0,
... dt0=0.1,
... y0=jnp.array(1.0),
... )
>>> bool(jnp.allclose(solution.ys[0], 0.9**10))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.RK2Heun
Bases: AbstractERK
Heun's method (RK2), with an embedded forward-Euler error estimate.
Order 2, two stages, SSP with coefficient C = 1. The error weights are
b̂ − b = [½, −½] (as diffrax.Heun), so adaptive controllers work.
Butcher tableau: c = (0, 1), a₂₁ = 1, b = (½, ½).
Examples:
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.RK3SSP
Bases: AbstractERK
Third-order strong-stability-preserving Runge–Kutta (Shu–Osher).
Order 3, three stages, SSP coefficient C = 1 (optimal): it keeps the monotonicity, positivity and TVD properties of forward Euler within the same CFL limit. The embedded second-order solution is Heun on the first two stages (b̂ = [½, ½, 0]), so adaptive controllers work.
Butcher tableau: c = (0, 1, ½), a₂₁ = 1, a₃₁ = a₃₂ = ¼, b = (⅙, ⅙, ⅔).
Examples:
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.RK4Classic
Bases: _FixedStepERK
The classical fourth-order Runge–Kutta method, fixed steps only.
Order 4, four stages, not SSP. The tableau has no embedded lower-order
solution, so an adaptive controller raises; diffrax.Tsit5 or
diffrax.Dopri5 are the adaptive fourth- and fifth-order choices.
Butcher tableau: c = (0, ½, ½, 1), a₂₁ = a₃₂ = ½, a₄₃ = 1, b = (⅙, ⅓, ⅓, ⅙).
Examples:
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.SSP_RK2
Bases: AbstractERK
Second-order SSP Runge–Kutta, the same scheme as RK2Heun.
Order 2, two stages, SSP coefficient C = 1, with an embedded forward-Euler error estimate.
Examples:
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.SSP_RK104
Bases: _FixedStepERK
Ketcheson's ten-stage, fourth-order SSP Runge–Kutta, fixed steps only.
Order 4, ten stages, SSP coefficient C = 6, the largest achievable at fourth order. There is no embedded error estimate, so an adaptive controller raises.
Examples:
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_erk.py
integrix.march.IMEX_SSP2
Bases: AbstractRungeKutta, AbstractImplicitSolver
IMEX-SSP2(2,2,2) for a diffrax.MultiTerm of an explicit and an implicit term.
The explicit part is SSP with C = 1; the implicit part is A-stable SDIRK with γ = 1 − 1/√2. Order 2. The embedded first-order solution y + h (F_E(Y₁) + F_I(Y₁)) (b̂ = [1, 0] in both tableaux) gives the error estimate for adaptive step-size control. Dense output is linear between steps.
Explicit tableau: c = (0, 1), a₂₁ = 1, b = (½, ½). Implicit tableau: c = (γ, 1 − γ), a₁₁ = a₂₂ = γ, a₂₁ = 1 − 2γ, b = (½, ½).
With fixed steps, give the implicit root finder its own tolerances
(IMEX_SSP2(root_finder=dfx.VeryChord(rtol=1e-8, atol=1e-8))); with a
diffrax.PIDController, the default root finder takes the controller's.
Examples:
>>> import diffrax as dfx
>>> import jax.numpy as jnp
>>> terms = dfx.MultiTerm(
... dfx.ODETerm(lambda t, y, args: -y),
... dfx.ODETerm(lambda t, y, args: -10.0 * y),
... )
>>> solver = IMEX_SSP2(root_finder=dfx.VeryChord(rtol=1e-10, atol=1e-10))
>>> solution = dfx.diffeqsolve(
... terms, solver, 0.0, 1.0, 0.01, jnp.array(1.0)
... )
>>> bool(jnp.allclose(solution.ys[0], jnp.exp(-11.0), rtol=1e-2))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_imex.py
integrix.march.AB3Solver
Bases: AbstractSolver
Third-order Adams–Bashforth as a diffrax solver, with an SSP-RK3 start.
yₙ₊₁ = yₙ + (h/12)(23 fₙ − 16 fₙ₋₁ + 5 fₙ₋₂), one evaluation of F per step. The two-level history lives in the diffrax solver state, and the first two steps are SSP-RK3 steps (a third-order bootstrap).
Fixed, uniform steps only (dt0=, or StepTo with equal spacing):
the weights assume a constant step and there is no error estimate, so an
adaptive controller raises. Order 3; dense output is linear.
Examples:
>>> import diffrax as dfx
>>> import jax.numpy as jnp
>>> solution = dfx.diffeqsolve(
... dfx.ODETerm(lambda t, y, args: -y),
... AB3Solver(),
... t0=0.0,
... t1=1.0,
... dt0=0.01,
... y0=jnp.array(1.0),
... )
>>> bool(jnp.allclose(solution.ys[0], jnp.exp(-1.0), rtol=1e-5))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_multistep.py
Steppers Run by integrix.march.run
integrix.march.run(stepper, rhs, t0, y0, dt, n_steps, *, args=None, saveat=None, adjoint=None)
March y₀ through n_steps constant steps of h from t₀.
yₙ₊₁ = Φ(yₙ, tₙ, h) for n = 0, …, N − 1, with tₙ = t₀ + n h.
Algorithm
- A diffrax solver goes to
diffrax.diffeqsolveon the step times t₀ + n h (diffrax.StepTo), so it takes exactly N steps. - An
AbstractSteppertakes its first step withinitand the other N − 1 withstepinsidejax.lax.scan, carrying the state and its history.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stepper
|
AbstractStepper | AbstractSolver
|
An |
required |
rhs
|
Callable[..., PyTree] | Any
|
The vector field |
required |
t0
|
ScalarLike
|
The start time t₀. |
required |
y0
|
PyTree
|
The initial state y₀, any pytree. |
required |
dt
|
ScalarLike
|
The step h. |
required |
n_steps
|
int
|
The number of steps N ≥ 1, a Python integer. |
required |
args
|
Any
|
Passed to the vector field. |
None
|
saveat
|
SaveAt | None
|
|
None
|
adjoint
|
Any
|
How reverse mode runs back through the march. |
None
|
Returns:
| Type | Description |
|---|---|
PyTree
|
y_N, or the stacked y₁, …, y_N. |
Raises:
| Type | Description |
|---|---|
ValidationError
|
If |
Examples:
>>> import jax.numpy as jnp
>>> from integrix.march import RK4Classic
>>> y = run(
... RK4Classic(), lambda t, y, args: -y, 0.0, jnp.array(1.0), 0.1, 10
... )
>>> bool(jnp.allclose(y, jnp.exp(-1.0), rtol=1e-6))
True
>>> from integrix.march import AB2Solver
>>> every = run(
... AB2Solver(),
... lambda t, y, args: -y,
... 0.0,
... jnp.ones(3),
... 0.1,
... 5,
... saveat=dfx.SaveAt(steps=True),
... )
>>> every.shape
(5, 3)
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_loop.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | |
integrix.march.AbstractStepper
Bases: Module
A fixed-step scheme whose history lives in the loop carry.
A stepper takes the diffrax vector field rhs(t, y, args), so a model
writes its tendencies once for both kinds of stepper. init takes the
first step, bootstrapping whatever history the scheme needs, and step
takes every later one; neither mutates the module.
Examples:
>>> import jax.numpy as jnp
>>> class Euler(AbstractStepper):
... @property
... def order(self):
... return 1
...
... def init(self, rhs, t0, y0, dt, args):
... return None, y0 + dt * rhs(t0, y0, args)
...
... def step(self, rhs, t, y, dt, state, args):
... return y + dt * rhs(t, y, args), state
>>> y = run(Euler(), lambda t, y, args: -y, 0.0, jnp.array(1.0), 0.1, 10)
>>> bool(jnp.allclose(y, 0.9**10))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_loop.py
order
abstractmethod
property
The global order of accuracy p, error O(hᵖ).
init(rhs, t0, y0, dt, args)
abstractmethod
Take the first step from (t₀, y₀): returns (state, y₁).
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_loop.py
step(rhs, t, y, dt, state, args)
abstractmethod
Take one step from (tₙ, yₙ) with the history: returns (yₙ₊₁, state).
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_loop.py
integrix.march.AB2Solver
Bases: AbstractStepper
Second-order Adams–Bashforth, run by integrix.march.run.
yₙ₊₁ = yₙ + (h/2)(3 fₙ − fₙ₋₁), one evaluation of the vector field per
step. init bootstraps with one Heun step and keeps f(t₀, y₀) as the
history; fixed, uniform steps only (variable steps break the weights).
Examples:
>>> import jax.numpy as jnp
>>> from integrix.march import run
>>> y = run(
... AB2Solver(), lambda t, y, args: -y, 0.0, jnp.array(1.0), 0.01, 100
... )
>>> bool(jnp.allclose(y, jnp.exp(-1.0), rtol=1e-4))
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_multistep.py
order
property
Second order.
init(rhs, t0, y0, dt, args)
step(rhs, t, y, dt, state, args)
yₙ₊₁ = yₙ + (h/2)(3 fₙ − fₙ₋₁); the history becomes fₙ.
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_multistep.py
integrix.march.LeapfrogRAFSolver
Bases: AbstractStepper
Leapfrog with a Robert–Asselin filter, run by integrix.march.run.
ỹₙ₊₁ = ȳₙ₋₁ + 2h F(tₙ, yₙ) and ȳₙ = yₙ + α (ȳₙ₋₁ − 2yₙ + ỹₙ₊₁): the filter
damps the computational mode, which grows without it. Second order for
α = 0 and first order for α > 0 (the Robert–Asselin degradation).
init bootstraps with one Heun step and keeps y₀ as the history; fixed,
uniform steps only.
Attributes:
| Name | Type | Description |
|---|---|---|
alpha |
float | Float[Array, '']
|
The filter coefficient α ≥ 0, typically 0.01 to 0.1. It is an array leaf, so it can be traced, vmapped over or differentiated; a Python or NumPy value is checked when the stepper is built. |
Raises:
| Type | Description |
|---|---|
ValidationError
|
If a concrete |
Examples:
>>> import jax.numpy as jnp
>>> from integrix.march import run
>>> stepper = LeapfrogRAFSolver(alpha=0.05)
>>> y = run(
... stepper, lambda t, y, args: -y, 0.0, jnp.array(1.0), 0.001, 1000
... )
>>> bool(jnp.allclose(y, jnp.exp(-1.0), rtol=1e-3)), stepper.order
(True, 1)
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_multistep.py
order
property
Second order without the filter, first order with it (or when traced).
init(rhs, t0, y0, dt, args)
step(rhs, t, y, dt, state, args)
Leapfrog from the filtered ȳₙ₋₁, then filter yₙ; the history becomes ȳₙ.
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_multistep.py
integrix.march.SplitExplicitRKSolver
Bases: AbstractStepper
Split-explicit stepping as an AbstractStepper, for integrix.march.run.
The state is the pair y = (y₃, y₂) of the slow and fast modes, and rhs
is a SplitExplicitTerms. Each step is split_explicit_step with the
fast mode in M RK4 substeps; M follows the external-wave CFL limit. The
scheme carries no history, so init is a plain first step. First order
in the slow step.
Attributes:
| Name | Type | Description |
|---|---|---|
n_substeps |
int
|
The number of fast substeps M ≥ 1 per slow step. |
Raises:
| Type | Description |
|---|---|
ValidationError
|
If |
Examples:
>>> import jax.numpy as jnp
>>> from integrix.march import run
>>> terms = SplitExplicitTerms(
... slow=lambda t, y3, mean_2d, args: -mean_2d,
... fast=lambda t, y2, y3, args: 0.0 * y2,
... )
>>> slow, fast = run(
... SplitExplicitRKSolver(n_substeps=5),
... terms,
... 0.0,
... (jnp.array(1.0), jnp.array(1.0)),
... 0.1,
... 10,
... )
>>> bool(jnp.allclose(slow, 0.0, atol=1e-6)), bool(fast == 1.0)
(True, True)
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_split_explicit.py
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | |
order
property
First order in the slow step.
init(rhs, t0, y0, dt, args)
No history to bootstrap: the first step is an ordinary one.
step(rhs, t, y, dt, state, args)
One split_explicit_step of the pair (y₃, y₂).
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_split_explicit.py
integrix.march.SplitExplicitTerms
Bases: Module
The vector fields of a split-explicit model, passed to run as rhs.
Attributes:
| Name | Type | Description |
|---|---|---|
slow |
Callable[..., PyTree]
|
The slow tendency |
fast |
Callable[..., PyTree]
|
The fast tendency |
slow_forcing |
Callable[..., PyTree] | None
|
|
couple |
Callable[..., PyTree] | None
|
|
Examples:
>>> terms = SplitExplicitTerms(
... slow=lambda t, y3, mean_2d, args: -y3,
... fast=lambda t, y2, y3, args: -y2,
... )
>>> terms.couple is None
True
Source code in .venv/lib/python3.12/site-packages/integrix/_src/march/_split_explicit.py
Semi-Lagrangian Solver
finitevolx.SemiLagrangianSolver
Bases: AbstractSolver
Semi-Lagrangian advection solver for diffrax.
Traces characteristic curves backward in time and interpolates the old field at departure points. Unconditionally stable (CFL > 1 allowed).
The term's vf(t, y, args) must return (u, v) velocity components
in grid index units per second (i.e. physical velocity divided by
grid spacing); wrap the velocity function in :class:SemiLagrangianTerm
to use the solver with diffeqsolve. The departure point is found
with one backward Euler step along a velocity frozen at t0, so the
scheme is first order in time (:meth:order is 1); there is no error
estimate, so it runs with fixed steps only. Dense output (SaveAt(ts=...))
is linear between steps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
interpolation_order
|
int
|
0 = nearest-neighbour, 1 = linear (diffusive, monotone).
JAX currently only supports |
required |
Source code in finitevolx/_src/timestepping/semi_lagrangian.py
finitevolx.SemiLagrangianTerm
Bases: AbstractTerm
Velocity term for :class:SemiLagrangianSolver in diffeqsolve.
dfx.ODETerm requires its vector field to return a pytree shaped like
y0, but semi-Lagrangian advection needs the velocity (u, v) of a
scalar field y. This term carries that velocity function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
velocity
|
Callable[[float, Array, PyTree], tuple[Array, Array]]
|
|
required |
Examples:
>>> term = SemiLagrangianTerm(
... lambda t, y, args: (jnp.ones_like(y), jnp.zeros_like(y))
... )
>>> sol = dfx.diffeqsolve(term, SemiLagrangianSolver(), 0.0, 4.0, 1.0, y0)
Source code in finitevolx/_src/timestepping/semi_lagrangian.py
Deprecated Adapters
The objects behind finitevolx.AB2Solver, finitevolx.LeapfrogRAFSolver and finitevolx.SplitExplicitRKSolver for one release.
finitevolx._src.timestepping._deprecated.AB2Solver
Bases: Module
Deprecated: use integrix.march.AB2Solver with integrix.march.run.
Adams-Bashforth 2nd order, y_{n+1} = y_n + (dt/2)(3 f_n - f_{n-1}),
with f_prev held in the module. The arithmetic is
integrix.march.AB2Solver's.
Usage::
solver = AB2Solver()
solver, y = solver.init(rhs_fn, t0, y0, dt)
for n in range(1, n_steps):
y, solver = solver.step(rhs_fn, t0 + n * dt, y, dt)
Source code in finitevolx/_src/timestepping/_deprecated.py
init(rhs_fn, t0, y0, dt)
Bootstrap with a Heun step, returning (updated_solver, y1).
Stores f_prev = f(t0, y0) so that the first AB2 step uses the
correct history.
Source code in finitevolx/_src/timestepping/_deprecated.py
step(rhs_fn, t, y, dt)
AB2 step: y_{n+1} = y_n + (dt/2)(3 f_n - f_{n-1}).
Source code in finitevolx/_src/timestepping/_deprecated.py
finitevolx._src.timestepping._deprecated.LeapfrogRAFSolver
Bases: Module
Deprecated: use integrix.march.LeapfrogRAFSolver with integrix.march.run.
Leapfrog with a Robert-Asselin filter on the middle level, with the
filtered y_prev held in the module. The arithmetic is
integrix.march.LeapfrogRAFSolver's: first order for alpha > 0,
second order for alpha = 0.
Usage::
solver = LeapfrogRAFSolver(alpha=0.05)
solver, y = solver.init(rhs_fn, t0, y0, dt)
for n in range(1, n_steps):
y, solver = solver.step(rhs_fn, t0 + n * dt, y, dt)
Source code in finitevolx/_src/timestepping/_deprecated.py
init(rhs_fn, t0, y0, dt)
Bootstrap with a Heun step, returning (updated_solver, y1).
Source code in finitevolx/_src/timestepping/_deprecated.py
step(rhs_fn, t, y_curr, dt)
Leapfrog + RAF step, returning (y_next, updated_solver).
Source code in finitevolx/_src/timestepping/_deprecated.py
finitevolx._src.timestepping._deprecated.SplitExplicitRKSolver
Bases: Module
Deprecated: use integrix.march.SplitExplicitRKSolver with integrix.march.run.
Split-explicit barotropic/baroclinic step with finitevolX's old call
shape. The arithmetic is integrix.march.SplitExplicitRKSolver's: RK4
substeps of the fast (2D) mode, its trapezoidal time mean over the cycle,
and one Forward-Euler step of the slow (3D) mode with that mean.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_substeps
|
int
|
Number of barotropic substeps per baroclinic step. |
required |
Source code in finitevolx/_src/timestepping/_deprecated.py
step(rhs_slow, rhs_fast, t, y_3d, y_2d, dt_slow, slow_forcing=None, couple_2d_to_3d=None)
Take one split-explicit step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rhs_slow
|
Callable[[float, PyTree, PyTree], PyTree]
|
Slow RHS: |
required |
rhs_fast
|
Callable[[float, PyTree, PyTree], PyTree]
|
Fast RHS: |
required |
t
|
float
|
Current time. |
required |
y_3d
|
PyTree
|
3D (slow) and 2D (fast) states. |
required |
y_2d
|
PyTree
|
3D (slow) and 2D (fast) states. |
required |
dt_slow
|
float
|
Slow timestep. |
required |
slow_forcing
|
Callable[[PyTree], PyTree] or None
|
|
None
|
couple_2d_to_3d
|
Callable[[PyTree, PyTree], PyTree] or None
|
|
None
|
Returns:
| Type | Description |
|---|---|
tuple[PyTree, PyTree]
|
|
Source code in finitevolx/_src/timestepping/_deprecated.py
Convenience Wrapper
finitevolx.solve_ocean_pde(rhs_fn, solver, y0, t0, t1, dt0, saveat=None, bc_fn=None, args=None, **kwargs)
Integrate an ocean PDE using diffrax.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rhs_fn
|
Callable
|
Right-hand side |
required |
solver
|
AbstractSolver
|
Time integration scheme (e.g. |
required |
y0
|
PyTree
|
Initial condition. |
required |
t0
|
float
|
Start and end times. |
required |
t1
|
float
|
Start and end times. |
required |
dt0
|
float
|
Initial (or fixed) timestep. |
required |
saveat
|
SaveAt
|
Output saving specification. Defaults to saving the final state. |
None
|
bc_fn
|
Callable
|
Boundary condition function |
None
|
args
|
PyTree
|
Static arguments forwarded to |
None
|
**kwargs
|
Any
|
Forwarded to :func: |
{}
|
Returns:
| Type | Description |
|---|---|
Solution
|
Solution object containing the saved states. |