Time-stepping
Available time steppers
The TimeSteppers module provides three generic, explicit time-stepping schemes. Importantly, the module handles only the time integration of prognostic fields, the tendency computation is the responsibility of each model's implementation.
Time stepper availability in Oceananigans models
NonhydrostaticModel:QuasiAdamsBashforth2,RungeKutta3TimeStepper(default)ShallowWaterModel:QuasiAdamsBashforth2TimeStepper,RungeKutta3(default)HydrostaticFreeSurfaceModel:QuasiAdamsBashforth2TimeStepper(default),SplitRungeKuttaTimeStepper
Quasi-Adams-Bashforth second order
The QuasiAdamsBashforth2TimeStepper approximates the time integral of tendencies via
where
The scheme requires storing tendencies from the previous time step, but only requires one tendency evaluation, making it computationally-efficient compared to multi-stage methods. The first time step automatically uses forward Euler (
Runge-Kutta third order
The RungeKutta3TimeStepper implements a low-storage, third-order Runge-Kutta scheme following Le and Moin (1991). The scheme advances the state through three substeps per time step:
with default coefficients
This scheme requires three tendency evaluations per time step but provides higher accuracy and better stability for problems with oscillatory dynamics.
Split Runge-Kutta
The SplitRungeKuttaTimeStepper implements a Runge-Kutta scheme suitable for split-explicit computations that follows the implementation detailed by Wicker and Skamarock (2002). At the beginning of each time step the prognostic fields are cached, and subsequent substeps compute:
where HydrostaticFreeSurfaceModel for split-explicit treatment of the barotropic and baroclinic modes.
Usage
NonhydrostaticModel and ShallowWaterModel
The NonhydrostaticModel and ShallowWaterModel support two time steppers: :QuasiAdamsBashforth2 and :RungeKutta3 (default). The timestepper can be specified through a keyword argument in the model constructor using the symbols shown in the examples below or by building the timestepper manually.
using Oceananigans
# Use QuasiAdamsBashforth2 time stepper
grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1))
model = NonhydrostaticModel(grid; timestepper=:QuasiAdamsBashforth2)
model.timestepper
# output
QuasiAdamsBashforth2TimeStepper{Float64}
├── χ: 0.1
└── implicit_solver: nothingusing Oceananigans
# Use RungeKutta3 time stepper (default)
grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1))
model = NonhydrostaticModel(grid; timestepper=:RungeKutta3)
model.timestepper
# output
RungeKutta3TimeStepper{Float64}
├── γ: (0.5333333333333333, 0.4166666666666667, 0.75)
├── ζ: (-0.2833333333333333, -0.4166666666666667)
└── implicit_solver: nothingHydrostaticFreeSurfaceModel
The HydrostaticFreeSurfaceModel supports :QuasiAdamsBashforth2 (default) and SplitRungeKuttaTimeStepper variants. The QuasiAdamsBashforth2 timestepper can be constructed as for the above or by manually building the timestepper and passing it to the model:
using Oceananigans
using Oceananigans.TimeSteppers: QuasiAdamsBashforth2TimeStepper
# Use QuasiAdamsBashforth2 time stepper (default)
grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1))
timestepper = QuasiAdamsBashforth2TimeStepper(χ = 0.125)
model = HydrostaticFreeSurfaceModel(grid; timestepper)
model.timestepper
# output
QuasiAdamsBashforth2TimeStepper{Float64}
├── χ: 0.125
└── implicit_solver: nothingThe HydrostaticFreeSurfaceModel supports a SplitRungeKutta time stepper with 2 to 5 stages using convenience constructors timestepper = :SplitRungeKuttaN where N is the desired number of stages:
using Oceananigans
# Construct models with convenience split Runge-Kutta constructor
grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1))
model = HydrostaticFreeSurfaceModel(grid; timestepper=:SplitRungeKutta2)
model = HydrostaticFreeSurfaceModel(grid; timestepper=:SplitRungeKutta3)
model = HydrostaticFreeSurfaceModel(grid; timestepper=:SplitRungeKutta4)
model = HydrostaticFreeSurfaceModel(grid; timestepper=:SplitRungeKutta5)
model.timestepper
# output
SplitRungeKuttaTimeStepper
├── stages: 5
├── β: (5, 4, 3, 2, 1)
└── implicit_solver: nothingFor custom coefficients, it is also possible to construct a SplitRungeKuttaTimeStepper directly:
using Oceananigans
using Oceananigans.TimeSteppers: SplitRungeKuttaTimeStepper
grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1))
timestepper = SplitRungeKuttaTimeStepper(coefficients=(4.3, 3.2, 2.1, 1.0))
model = HydrostaticFreeSurfaceModel(grid; timestepper)
model.timestepper
# output
SplitRungeKuttaTimeStepper
├── stages: 4
├── β: (4.3, 3.2, 2.1, 1.0)
└── implicit_solver: nothingExtending time steppers for custom models
To use the existing time steppers with a new model type, implement the following methods. For QuasiAdamsBashforth2TimeStepper:
ab2_step!(model::MyModel, Δt, callbacks) # advance fields one AB2 step
cache_previous_tendencies!(model::MyModel) # store Gⁿ → G⁻For RungeKutta3TimeStepper:
rk3_substep!(model::MyModel, Δt, γⁿ, ζⁿ, callbacks) # advance one RK3 substep
cache_previous_tendencies!(model::MyModel) # store Gⁿ → G⁻For SplitRungeKuttaTimeStepper:
cache_current_fields!(model::MyModel) # store U → Ψ⁻ at step start
rk_substep!(model::MyModel, Δτ, callbacks) # advance one substepAll models must also implement update_state!(model, callbacks) to fill halo regions and compute any diagnostic quantities after each step or substep.
The fractional step method
With the pressure decomposition as discussed, the momentum evolves via:
where, e.g., for the non-hydrostatic model (ignoring background velocities and surface-wave effects)
collects all terms on the right side of the momentum equation
The time-integral of the momentum equation
where the superscript
while treating the rest of the terms on the right hand side of
To effect such a fractional step method, we define an intermediate velocity field
The integral on the right of the equation for
Combining the equation
Taking the divergence of fractional step equation
With
Tracers are stepped forward explicitly via
where
and the same time-stepping scheme used for the momentum tendencies is applied to evaluate the integral of