Skip to content

TimeIntegrators

The time integration module implemented in Mantis is based on the framework developed by (Vos et al., 2011). This framework allows for an easy implementation of a variety of explicit, implicit, and implicit-explicit (IMEX) time stepping schemes, and is based on the concept of general linear methods (GLMs). See, for example, (Butcher, 2006), for more details. These methods are applicable to both ODEs and PDEs, so that both are available in Mantis.

GLMs: Notation and Theory

General linear methods can be characterised as follows (see (Butcher, 2006), (Vos et al., 2011)). Consider the initial value problem defined as the ODE

dydt=f(y),y(t0)=y0,

where f:RNRN. The n-th (time) step of the GLM comprised of r (integrator) steps and s stages is then formulated as

Yi=Δtj=1saijFj+j=1ruijyjn1,i=1,,s,yin=Δtj=1sbijFj+j=1rvijyjn1,i=1,,r,

where Yi are called the stage values and Fi are called the stage derivatives. These two quantities are related by the differential equation

Fi=f(Yi).

The above formulation can be cast into the following matrix form

[Yyn]=[AIUIBIVI][ΔtYyn1],

which is often simplified (with some abuse of notation) to

[Yyn]=[AUBV][ΔtYyn1].

Either way, the vectors y (in/output approximations), Y (stage values), and F (stage derivatives) are defined as

yn1=[y1n1y2n1yrn1],yn=[y1ny2nyrn],Y=[Y1Y2Ys],F=[F1F2Fs].

It is important to note that the in/output vectors can contain more than just the solution. The exact content depends on the specific method, but often includes previously computed stage derivatives. This is particularly important when creating and/or initialising new methods.

GLMs: Characterising a GLM

Any time integrator that fits in the above framework can thus be characterised by the four matrices A, B, U, V, and the layout of the in- and output vectors y. In addition, every time integrator will also need a vector C, which keeps track of the time at which the stages are evaluated. In Mantis, A, B, U, V, and C are stored in the time integrator structs (see this section below). The matrices have the following sizes:

MatrixSize
Anum_stages x num_stages
Bnum_steps x num_stages
Unum_stages x num_steps
Vnum_steps x num_steps
Cnum_stages x 1

GLMs: Extension to IMEX Schemes

The framework introduced by (Vos et al., 2011) extends the GLM idea to IMEX integrators. The ODE from the previous section is now split into

dydt=f(y)+g(y),y(t0)=y0,

where f:RNRN and g:RNRN. The f-part represent the part of the ODE that is treated explicitly, while the g-part is treated implicitly. The n-th (time) step of the IMEX-GLM comprised of r (integrator) steps and s stages is then formulated as

Yi=Δtj=1saijIMGj+Δtj=1saijEXFj+j=1ruijyjn1,i=1,,s,yin=Δtj=1sbijIMGj+Δtj=1sbijEXFj+j=1rvijyjn1,i=1,,r,

where Yi are called the stage values and Fi and Gi are called the (explicit and implicit) stage derivatives. These quantities are related by the differential equation

Fi=f(Yi),Gi=g(Yi).

The matrix form is obtained in the same way as in the previous section.

IMEX GLMs are characterised in the same way as described in GLMs: Characterising a GLM. The only difference is that an IMEX GLM will have two A and B matrices, and two C vectors : one for the explicit part and one for the implicit part.

What are time integrators in Mantis?

The top-level type within the TimeIntegrators module is the AbstractTimeIntegrator{num_stages, num_steps} type.

Mantis.TimeIntegrators.AbstractTimeIntegrator Type
julia
AbstractTimeIntegrator{num_stages, num_steps}

Supertype for all time integrators.

Type parameters

  • num_stages: The number of stages for a multi-step scheme (such as the Runge-Kutta family). Since every scheme is at least a single-stage scheme, num_stages >= 1.

  • num_steps: The number of steps for a multi-step scheme (such as the Adams-Bashforth family). Since every scheme is at least a single-step scheme, num_steps >= 1.

source

Note that you can always obtain the number of stages and steps using the following functions.

Mantis.TimeIntegrators.get_num_stages Function
julia
get_num_stages(
    ::AbstractTimeIntegrator{num_stages, num_steps}
) where {num_stages, num_steps}

Return the number of stages of the given AbstractTimeIntegrator.

source
Mantis.TimeIntegrators.get_num_steps Function
julia
get_num_steps(
    ::AbstractTimeIntegrator{num_stages, num_steps}
) where {num_stages, num_steps}

Return the number of steps of the given AbstractTimeIntegrator.

source

The AbstractTimeIntegrator{num_stages, num_steps} type has four concrete subtypes, each representing a specific class of time integrators.

Mantis.TimeIntegrators.Explicit Type
julia
Explicit{num_stages, num_steps, NT, AA, AE, EE} <:
    AbstractTimeIntegrator{num_stages, num_steps}

Explicit time integration scheme.

Explicit time integrators are explicit in the ODE sense

Following (Vos et al., 2011), the explicit time integrators in this framework are considered explicit integrators when applied to ODEs. When applied to PDEs using a Galerkin method, one still has to solve a linear system. This can be referred to as an indirect explicit method in this case.

Fields

  • A, B, U, V, C: See the GLM characterisation for more details, including the matrix sizes. All matrices (and vector) are of type SMatrix (or SVector, respectively) with the appropriate size and NT as eltype.

  • time_levels::TimeLevels: Required information from previous steps. See TimeLevels for the details.

  • order::Int: Order of the scheme.

Type parameters

  • num_stages, num_steps: See AbstractTimeIntegrator for the details.

  • NT: Element type of the A, B, U, V matrices, and C vector.

  • AA, AE, EE: Number of entries in A, (B and U), and V, respectively.

source
Mantis.TimeIntegrators.DiagonallyImplicit Type
julia
DiagonallyImplicit{num_stages, num_steps, NT, AA, AE, EE} <:
    AbstractTimeIntegrator{num_stages, num_steps}

Diagonally implicit time integration scheme.

Fields

  • A, B, U, V, C: See the GLM characterisation for more details, including the matrix sizes. All matrices (and vector) are of type SMatrix (or SVector, respectively) with the appropriate size and NT as eltype.

  • time_levels::TimeLevels: Required information from previous steps. See TimeLevels for the details.

  • order::Int: Order of the scheme.

Type parameters

  • num_stages, num_steps: See AbstractTimeIntegrator for the details.

  • NT: Element type of the A, B, U, V matrices, and C vector.

  • AA, AE, EE: Number of entries in A, (B and U), and V, respectively.

source
Mantis.TimeIntegrators.Implicit Type
julia
Implicit{num_stages, num_steps, NT, AA, AE, EE} <:
    AbstractTimeIntegrator{num_stages, num_steps}

Implicit time integration scheme

Fields

  • A, B, U, V, C: See the GLM characterisation for more details, including the matrix sizes. All matrices (and vector) are of type SMatrix (or SVector, respectively) with the appropriate size and NT as eltype.

  • time_levels::TimeLevels: Required information from previous steps. See TimeLevels for the details.

  • order::Int: Order of the scheme.

Type parameters

  • num_stages, num_steps: See AbstractTimeIntegrator for the details.

  • NT: Element type of the A, B, U, V matrices, and C vector.

  • AA, AE, EE: Number of entries in A, (B and U), and V, respectively.

source
Mantis.TimeIntegrators.IMEX Type
julia
IMEX{num_stages, num_steps, NT, AA, AE, EE} <:
    AbstractTimeIntegrator{num_stages, num_steps}

Implicit-Explicit (IMEX) time integration scheme. Currently only supportes implicit parts which are diagonally implicit.

Fields

  • A_IM, A_EX, B_IM B_EX, U, V, C_IM, C_EX: See the GLM characterisation for more details, including the matrix sizes. All matrices (and vector) are of type SMatrix (or SVector, respectively) with the appropriate size and NT as eltype.

  • time_levels::TimeLevels: Required information from previous steps. See TimeLevels for the details.

  • order::Int: Order of the scheme.

Type parameters

  • num_stages, num_steps: See AbstractTimeIntegrator for the details.

  • NT: Element type of the A_IM, A_EX, B_IM B_EX, U, and V matrices, and C_IM, and C_EX vectors.

  • AA, AE, EE: Number of entries in (A_IM and A_EX), (B_IM, B_EX and U), and V, respectively.

source

The A-matrix (see above) in the GLM framework dictates whether a scheme is implicit or not. When initialising one of the above structs, this is checked using the following function. You can also use this to check what to expect.

Mantis.TimeIntegrators.check_implicit Function
julia
check_implicit(A::AbstractMatrix)

Check if the GLM matrix A belongs to an implicit or diagonally implicit scheme.

Returns

  • is_implicit::Bool: whether the scheme is fully implicit or not

  • is_diagonally_implicit::Bool: whether the scheme is diagonally implicit. Note that both outputs are true for a fully implicit scheme, and that only this argument is true for a diagonally implicit scheme.

source

Since all schemes store the order of the scheme, you can always retrieve that information using the following getter.

Mantis.TimeIntegrators.get_order Function
julia
get_order(scheme::AbstractTimeIntegrator)

Return the order of the time integration scheme

source

The solution objects

The time integrators themselves do not store the solution. This is instead handled by the TimeIntegrationSolution-object.

Mantis.TimeIntegrators.TimeIntegrationSolution Type
julia
TimeIntegrationSolution{T, S, NT, ST}

Solution and current state of the time integration problem.

TimeIntegrationSolution does not store problem-specific information.

While a TimeIntegrationSolution stores most information, it does not store problem- specific information. See TimeIntegrationOperators for the problem-specific information.

Constructors

  • TimeIntegrationSolution( solution::Matrix{NT}, scheme::AbstractTimeIntegrator{num_stages, num_steps}, startup_scheme::Union{Nothing, AbstractTimeIntegrator}, remaining_startup_steps::Int, startup_solution::ST=nothing, ) where {NT, num_stages, num_steps, ST}: General constructor. Note that the eltype of the solution matrix will dictate the number type used in the TimeIntegrationSolution.

Fields

  • N::Int: Number varables in the system.

  • solution::Matrix{NT}: Of size (N, num_steps).

  • scheme::T: The time integration scheme.

  • startup_scheme::S: The startup scheme, if provided. Will be nothing if not provided. Defaults to nothing.

  • remaining_startup_steps::Int: Remaining startup steps.

  • solution_allocated::Matrix{NT}: Pre-allocated memory for calculations.

  • F_alLocated::Matrix{NT}: Pre-allocated memory for calculations.

  • G_alLocated::Matrix{NT}: Pre-allocated memory for calculations.

  • startup_solution::ST: The TimeIntegrationSolution object used for the startup procedure. This will contain information specific to the startup scheme and its current state. Will be nothing if not provided. Defaults to nothing.

  • stage_values::Vector{NT}: Pre-allocated memory for calculations.

  • temp_var::Vector{NT}: Pre-allocated memory for calculations.

Type parameters

  • T: Type of the scheme.

  • S: Type of the startup scheme. Nothing if no startup scheme is provided.

  • NT: eltype of the solution and pre-allocated arrays.

  • ST: Type of the startup solution object, Nothing if no startup solution is provided.

source

The TimeIntegrationSolution stores the information about the state of the time integration problem. You can inspect such objects through the following getters.

Mantis.TimeIntegrators.get_num_variables Function
julia
get_num_variables(sol::TimeIntegrationSolution)

Return the number of variables in the system.

source
Mantis.TimeIntegrators.get_solution Function
julia
get_solution(sol::TimeIntegrationSolution{T, S, NT}) where {T, S, NT}

Return the current solution values. This is a Matrix{NT} of size (N, num_steps), where N is the number of variables in the system.

source
Mantis.TimeIntegrators.get_scheme Function
julia
get_scheme(sol::TimeIntegrationSolution)

Return the time integration scheme used to obtain the solution.

source
Mantis.TimeIntegrators.get_startup_scheme Function
julia
get_startup_scheme(sol::TimeIntegrationSolution)

Return the startup scheme used to initialise the solution. Will be nothing if no startup scheme is present.

source
Mantis.TimeIntegrators.get_remaining_startup_steps Function
julia
get_remaining_startup_steps(sol::TimeIntegrationSolution)

Return the number of startup steps that still have to be performed. These are the steps that will be computed using the startup scheme instead of the main scheme.

source
Internals: solution objects

We explain some internals related to the solution objects. However, this is considered an implementation detail.

Next to the information just mentioned, a TimeIntegrationSolution also stores the pre-allocated arrays and the solution object for the startup scheme (if present), which can be obtained with the following getters.

Mantis.TimeIntegrators.get_solution_allocated Function
julia
get_solution_allocated(sol::TimeIntegrationSolution)

Return the pre-allocated matrix for the solution. To obtain the solution itself, use get_solution.

source
Mantis.TimeIntegrators.get_F_allocated Function
julia
get_F_allocated(sol::TimeIntegrationSolution)

Return the pre-allocated matrix for the explicit stage derivatives.

source
Mantis.TimeIntegrators.get_G_allocated Function
julia
get_G_allocated(sol::TimeIntegrationSolution)

Return the pre-allocated matrix for the implicit stage derivatives

source
Mantis.TimeIntegrators.get_startup_solution Function
julia
get_startup_solution(sol::TimeIntegrationSolution)

Return the solution object corresponding to the startup scheme.

source
Mantis.TimeIntegrators.get_stage_allocated Function
julia
get_stage_allocated(sol::TimeIntegrationSolution)

Return the pre-allocated vector for the stage values.

source
Mantis.TimeIntegrators.get_temp_var Function
julia
get_temp_var(sol::TimeIntegrationSolution)

Return the pre-allocated vector for the temporary variable for the implicit schemes.

source

Do not manually modify the pre-allocated arrays.

Modifying the pre-allocated arrays may lead to incorrect results or unexpected behaviour.

The above getters are internally used to access the pre-allocated arrays and to overwrite their values. You should not need these functions unless you are extending the integrate functionality to new types.

Adding problem-specific information

To use the TimeIntegrators-module, you have to specify which problem you want to solve. Information about the problem is collected in TimeIntegrationOperators. See GLMs: Notation and Theory and GLMs: Extension to IMEX Schemes for the notation.

Mantis.TimeIntegrators.TimeIntegrationOperators Type
julia
TimeIntegrationOperators{EE, IS, IE}

Defines the ODE-specific operators used in the time integration.

Makes a distiction between the explicit and implicit operators. EE, IS, and IE are the types of the explicit and implicit functions, which are Nothing if not defined. Note that at least EE or IS must be a function.

Constructors

  • define_explicit_ode(explicit_evaluate::Function): For fully explicit ODEs.

  • define_diagonally_implicit_ode(implicit_solve::Function): For diagonally implicit ODEs.

  • define_implicit_ode(implicit_solve::Function, implicit_evaluate::Function): For fully implicit ODEs.

  • define_imex_ode(explicit_evaluate::Function, implicit_solve::Function): For IMEX ODEs.

  • TimeIntegrationOperators( explicit_evaluate::Union{Nothing, Function}, implicit_solve::Union{Nothing, Function}, implicit_evaluate::Union{Nothing, Function}, ): Generic constructor.

Fields

  • explicitEvaluate!::EE: A function that evaluates the explicit part of the ODE, that is, the function that evaluates F=f(y,t). See the manual section on TimeIntegrators for the terminology. This function must have the following inputs: (output, yn, t). It must also overwrite the output argument. The output argument will be a vector-like object of length N (the number of variables), as will yn. The argument t will be a number indicating the current time.

  • implicitSolve!::IS: A function that solves the implicit part of the ODE, that is, the function that solves the equation Yhg(Y)=x, with h=aiiIMΔt. See the manual section on TimeIntegrators for the terminology. In case g is a linear operator, a direct solution method can be through the inverse operator (Ihg)Y=x, where I is the identity function. This function must have the following inputs: (output, x, h, t). It must also overwrite the output argument. The output argument will be a matrix-like object of size (N, num_stages), as will yn. The arguments h will be either a number (for DiagonallyImplicit integrators) or a SMatrix of size (num_stages, num_stages) (for Implicit integrators) and t will be a number (for DiagonallyImplicit integrators) or an SVector of length num_stages (for Implicit integrators) indicating the current time(s).

  • implicitEvaluate!::IE: A function that evaluates the implicit part of the ODE, that is, the function that evaluates G=g(y,t). See the manual section on TimeIntegrators for the terminology. This function must have the following inputs: (output, yn, t). It must also overwrite the output argument. The output argument will be a vector-like object of length N (the number of variables), as will yn, and t will be a number indicating the current time.

Type parameters

  • EE: typeof(explicitEvaluate!) if initialised, Nothing otherwise.

  • IS: typeof(implicitSolve!) if initialised, Nothing otherwise.

  • IE: typeof(implicitEvaluate!) if initialised, Nothing otherwise.

source

There are various ways in which you can construct the above object, using the following functions.

The input functions are generic

This means that within these functions, you can choose how to treat your problem. For example, you can solve a non-linear system via the Newton-Rhapson method, as done in the Lorenz Attractor

Mantis.TimeIntegrators.define_explicit_ode Function
julia
define_explicit_ode(explicit_evaluate::Function)

Creates a TimeIntegrationOperators object for an explicit ODE.

source
Mantis.TimeIntegrators.define_diagonally_implicit_ode Function
julia
define_diagonally_implicit_ode(
    implicit_solve::Function, implicit_evaluate::Union{Nothing, Function}=nothing
)

Creates a TimeIntegrationOperators object for a diagonally implicit ODE. The implicit_evaluate argument is needed when:

  • using a multi-step scheme (for the initialisation process).

  • using a diagonally implicit scheme that has at least one zero on the diagonal.

source
Mantis.TimeIntegrators.define_implicit_ode Function
julia
define_implicit_ode(implicit_solve::Function, implicit_evaluate::Function)

Creates a TimeIntegrationOperators object for an implicit ODE.

source
Mantis.TimeIntegrators.define_imex_ode Function
julia
define_imex_ode(
    explicit_evaluate::Function,
    implicit_solve::Function,
    implicit_evaluate::Union{Nothing, Function}=nothing
)

Creates a TimeIntegrationOperators object for an IMEX ODE. The implicit_evaluate argument is needed when:

  • using a multi-step scheme (for the initialisation process).

  • using a diagonally implicit scheme that has at least one zero on the diagonal.

source

Time Integration

Now that the scheme, solution object, and the problem are all defined, we can perform the actual time integration.

Time Integrate

The integration happens by calling one of the following two methods.

Mantis.TimeIntegrators.time_integrate Function
julia
time_integrate(
    y_n::TimeIntegrationSolution,
    ode::TimeIntegrationOperators,
    t::Float64,
    dt::Float64;
    kwargs...,
)

Perform a single time integration step using the given time integration scheme and ODE system operators. Creates a copy of the solution when called.

See also

time_integrate!

Arguments

  • y_n::TimeIntegrationSolution: The current solution vector.

  • ode::TimeIntegrationOperators: The ODE system operators.

  • t::Float64: The current time.

  • dt::Float64: The time step.

  • kwargs...: Additional arguments passed to the ODE system.

Returns

  • TimeIntegrationSolution: The updated solution vector after one time step.
source
Mantis.TimeIntegrators.time_integrate! Function
julia
time_integrate!(
    y_n::TimeIntegrationSolution,
    ode::TimeIntegrationOperators,
    t::Float64,
    dt::Float64;
    kwargs...,
)

Perform a single, in-place time integration step using the given time integration scheme and ODE system operators.

See also

time_integrate

Arguments

  • y_n::TimeIntegrationSolution: The current solution vector.

  • ode::TimeIntegrationOperators: The ODE system operators.

  • t::Float64: The current time.

  • dt::Float64: The time step.

  • kwargs...: Additional arguments passed to the ODE system.

Returns (in-place)

  • TimeIntegrationSolution: The updated solution vector after one time step.
source
Internals: time integration

We explain how the time integration is performed. However, this is considered an implementation detail.

The integration functions from Time Integrate end up calling the following internal integrator. This integrator function is specialised for different integrators and encodes how the time stepping is actually performed.

Mantis.TimeIntegrators._time_integrate! Function
julia
_time_integrate!(
    y_n::TimeIntegrationSolution,
    scheme::Explicit{num_stages, num_steps},
    ode::TimeIntegrationOperators,
    t::Float64,
    dt::Float64;
    kwargs...,
) where {num_steps, num_stages}

Perform a single time integration step using the given Explicit time integration scheme and ODE system operators. Implements algorithm 1 of (Vos et al., 2011) specifically for when the integrator is explicit.

Arguments

  • y_n::TimeIntegrationSolution: The current solution vector.

  • scheme::Explicit{num_stages, num_steps}: The Explicit time integration scheme.

  • ode::TimeIntegrationOperators: The ODE system operators.

  • t::Float64: The current time.

  • dt::Float64: The time step.

  • kwargs...: Additional arguments passed to the ODE system.

Returns (in-place)

  • TimeIntegrationSolution{num_steps}: The updated solution vector after one time step.
source
julia
_time_integrate!(
    y_n::TimeIntegrationSolution,
    scheme::DiagonallyImplicit{num_stages, num_steps},
    ode::TimeIntegrationOperators,
    t::Float64,
    dt::Float64;
    kwargs...,
) where {num_steps, num_stages}

Perform a single time integration step using the given DiagonallyImplicit time integration scheme and ODE system operators. Implements algorithm 1 of (Vos et al., 2011) specifically for when the integrator in diagonally implicit.

Arguments

  • y_n::TimeIntegrationSolution{num_steps}: The current solution vector.

  • scheme::DiagonallyImplicit{num_stages,num_steps}: The DiagonallyImplicit time integration scheme.

  • ode::TimeIntegrationOperators: The ODE system operators.

  • t::Float64: The current time.

  • dt::Float64: The time step.

  • kwargs...: Additional arguments passed to the ODE system.

Returns (in-place)

  • TimeIntegrationSolution{num_steps}: The updated solution vector after one time step.
source
julia
_time_integrate!(
    y_n::TimeIntegrationSolution,
    scheme::Implicit{num_stages, num_steps},
    ode::TimeIntegrationOperators,
    t::Float64,
    dt::Float64;
    kwargs...,
) where {num_steps, num_stages}

Perform a single time integration step using the given Implicit time integration scheme and ODE system operators.

Arguments

  • y_n::TimeIntegrationSolution{num_steps}: The current solution vector.

  • scheme::Implicit{num_stages,num_steps}: The Implicit time integration scheme.

  • ode::TimeIntegrationOperators: The ODE system operators.

  • t::Float64: The current time.

  • dt::Float64: The time step.

  • kwargs...: Additional arguments passed to the ODE system.

Returns (in-place)

  • TimeIntegrationSolution{num_steps}: The updated solution vector after one time step.
source
julia
_time_integrate!(
    y_n::TimeIntegrationSolution,
    scheme::IMEX{num_stages, num_steps},
    ode::TimeIntegrationOperators,
    t::Float64,
    dt::Float64;
    kwargs...,
) where {num_steps, num_stages}

Perform a single time integration step using the given IMEX time integration scheme and ODE system operators. Implements algorithm 1 of (Vos et al., 2011) in full.

Arguments

  • y_n::TimeIntegrationSolution{num_steps}: The current solution vector.

  • scheme::IMEX{num_stages,num_steps}: The IMEX time integration scheme.

  • ode::TimeIntegrationOperators: The ODE system operators.

  • t::Float64: The current time.

  • dt::Float64: The time step.

  • kwargs...: Additional arguments passed to the ODE system.

Returns (in-place)

  • TimeIntegrationSolution{num_steps}: The updated solution vector after one time step.
source

Initialisation

All integrators must be initialised. For multi-stage schemes, this is often just a matter of adding the initial condition. For multi-step schemes, this requires a startup scheme and more computation. To handle these different initialisation requirements, Mantis has a TimeLevels struct, as introduced in (Vos et al., 2011), to keep track of what needs to be initialised. Every time integrator has a TimeLevels struct to define what information is needed from previous steps.

Mantis.TimeIntegrators.TimeLevels Type
julia
TimeLevels

The length of the fields defines the structure of the input and output vectors for an integrator. The initialisation procedure uses this information to determine what needs to be initialised. Note that the vectors may be empty.

Constructors

  • TimeLevels( step_values::Vector{Int} step_derivatives_implicit::Vector{Int} step_derivatives_explicit::Vector{Int} ): Generic constructor.

Examples

Consider the CNAB2 scheme, which defines the input vector as

[ynΔtGnΔtFnΔtFn1],

so, the TimeLevels input for this scheme is TimeLevels([0], [0], [0, 1]).

Consider the BDF3 scheme, which defines the input vector as

[ynyn1yn2],

so, the TimeLevels input for this scheme is TimeLevels([0, 1, 2], Int[], Int[]).

Fields

  • step_values::Vector{Int}: The length of this vector determines how many previous solutions are needed. For multi-stage methods, this is often just [0]. For multi-step methods, this may include a longer history.

  • step_derivatives_implicit::Vector{Int}: Required implicit step derivatives from previous steps. Note that this is ΔtG. This is, for example, used in the Adams-Moulton schemes.

  • step_derivatives_explicit::Vector{Int}: Required explicit step derivatives from previous steps. Note that this is ΔtF. This is, for example, used in the Adams-Bashford schemes.

source

In general, the length of the arrays in the TimeLevels object is required. These lengths can be easily obtained using the following getters.

Mantis.TimeIntegrators.get_num_step_values Function
julia
get_num_step_values(tl::TimeLevels)

Return the number of step values that tl encodes.

source
Mantis.TimeIntegrators.get_num_implicit_derivatives Function
julia
get_num_implicit_derivatives(tl::TimeLevels)

Return the number of implicit derivatives that tl encodes.

source
Mantis.TimeIntegrators.get_num_explicit_derivatives Function
julia
get_num_explicit_derivatives(tl::TimeLevels)

Return the number of explicit derivatives that tl encodes.

source

The actual initialisation step(s) can be performed by calling the following function.

Mantis.TimeIntegrators.initialise_scheme Function
julia
initialise_scheme(
    y0::Matrix{T}, scheme::AbstractTimeIntegrator{num_stages, num_steps}
) where {T, num_stages, num_steps}

Creates the TimeIntegrationSolution object with an initialised y0 vector.

Arguments

  • y0::Matrix{T}: The initial value of the solution matrix.

  • scheme::AbstractTimeIntegrator{num_stages, num_steps}: The time integration scheme.

Returns

  • TimeIntegrationSolution{num_steps}: The initialised solution vector.
source
julia
initialise_scheme(
    y0::Vector{T}, scheme::AbstractTimeIntegrator{num_stages, num_steps}
) where {T, num_stages, num_steps}

Initialise single-step schemes. The given input vector will be turned into a matrix of the appropriate size.

Arguments

  • y0::Vector{T}: The initial value of the solution vector.

  • scheme::AbstractTimeIntegrator{num_stages, num_steps}: The time integration scheme.

source
julia
initialise_scheme(
    y0::Vector{T},
    scheme::AbstractTimeIntegrator{num_stages_scheme, num_steps},
    startup_scheme::AbstractTimeIntegrator{num_stages_startup, 1},
) where {T, num_stages_scheme, num_steps, num_stages_startup}

Initialise multi-step schemes.

Arguments

  • y0::Vector{T}: The initial value of the solution vector.

  • scheme::AbstractTimeIntegrator{num_stages_scheme, num_steps}: The time integration scheme.

  • startup_scheme::AbstractTimeIntegrator{num_stages_startup, 1}: The startup scheme.

Returns

  • TimeIntegrationSolution{num_steps}: The initialised solution vector.
source

Other initialisation procedures

Mantis does not provide an exhaustive set of initialisation procedures. Some GLM schemes may require a different quantity to be initialised than what the initialise_scheme-method provides. If this is the case, you can always perform the initialisation manually. See Adding your own scheme for the details.

Pre-implemented schemes

Mantis provides a few pre-implemented schemes for convenience.

Pre-implemented explicit integrators in the Runge-Kutta family.
Mantis.TimeIntegrators.FORWARD_EULER Constant
julia
FORWARD_EULER

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.EXPLICIT_MIDPOINT Constant
julia
EXPLICIT_MIDPOINT

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.HEUN2 Constant
julia
HEUN2

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.RALSTON2 Constant
julia
RALSTON2

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.HEUN3 Constant
julia
HEUN3

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.RK3 Constant
julia
RK3

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.RALSTON3 Constant
julia
RALSTON3

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.VDHW3 Constant
julia
VDHW3

Van der Houwen's/Wray's third-order method

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.SSPRK3 Constant
julia
SSPRK3

Third-order Strong Stability Preserving Runge-Kutta.

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.RK4 Constant
julia
RK4

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.RK4_3_8 Constant
julia
RK4_3_8

3/8-rule fourth-order method.

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.RALSTON4 Constant
julia
RALSTON4

From Wikipedia's list of Runge-Kutta methods.

source
Pre-implemented (diagonally) implicit integrators in the Runge-Kutta family.
Mantis.TimeIntegrators.BACKWARD_EULER Constant
julia
BACKWARD_EULER

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.RADAU_IA_1 Constant
julia
RADAU_IA_1

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.IMPLICIT_MIDPOINT Constant
julia
IMPLICIT_MIDPOINT

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.CRANK_NICOLSON Constant
julia
CRANK_NICOLSON

Crank-Nicolson method of order 2. From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.DIRK2 Constant
julia
DIRK2

Strongly S-stable, two-stage, DIRK scheme of order 2. See (Alexander, 1977) page 1012. For this scheme, α=1sqrt(2)/2.

source
Mantis.TimeIntegrators.DIRK3 Constant
julia
DIRK3

Crouzeix's two-stage, 3rd order, A-stable DIRK scheme. See (Alexander, 1977) page 1008.

source
Mantis.TimeIntegrators.ESDIRK32 Constant
julia
ESDIRK32

Four-stage, 3rd order, stiffly accurate, A- and L-stable ESDIRK scheme. See (Kværnø, 2004) page 497.

source
Mantis.TimeIntegrators.RADAU_IA_3 Constant
julia
RADAU_IA_3

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.DIRK4 Constant
julia
DIRK4

Crouzeix's three-stage, 4th order, A-stable DIRK scheme. See (Alexander, 1977) page 2.

source
Mantis.TimeIntegrators.GAUSS_LEGENDRE_4 Constant
julia
GAUSS_LEGENDRE_4

From Wikipedia's list of Runge-Kutta methods.

source
Mantis.TimeIntegrators.GAUSS_LEGENDRE_6 Constant
julia
GAUSS_LEGENDRE_6

From Wikipedia's list of Runge-Kutta methods.

source
Pre-implemented explicit multi-step integrators.
Mantis.TimeIntegrators.AB1 Constant
julia
AB1

Adams-Bashforth 1:

yn+1=yn+Δtf(yn).source
Mantis.TimeIntegrators.AB2 Constant
julia
AB2

Adams-Bashforth 2:

yn+1=yn+Δt(32f(yn)12f(yn1)).source
Mantis.TimeIntegrators.AB3 Constant
julia
AB3

Adams-Bashforth 3:

yn+1=yn+Δt(2312f(yn)43f(yn1)+512f(yn2)).source
Mantis.TimeIntegrators.AB4 Constant
julia
AB4

Adams-Bashforth 4:

yn+1=yn+Δt(5524f(yn)5924f(yn1)+3724f(yn2)924f(yn3)).source
Pre-implemented (diagonally) implicit multi-step integrators.
Mantis.TimeIntegrators.AM0 Constant
julia
AM0

Adams-Moulton 0:

yn+1=yn+Δtg(yn).source
Mantis.TimeIntegrators.AM1 Constant
julia
AM1

Adams-Moulton 1:

yn+1=yn+Δt(12g(yn+1)+12g(yn)).source
Mantis.TimeIntegrators.AM2 Constant
julia
AM2

Adams-Moulton 2:

yn+1=yn+Δt(512g(yn+1)+812g(yn)112g(yn1).source
Mantis.TimeIntegrators.AM3 Constant
julia
AM3

Adams-Moulton 3:

yn+1=yn+Δt(924g(yn+1)+1924g(yn)524g(yn1)+124g(yn2)).source
Mantis.TimeIntegrators.AM4 Constant
julia
AM4

Adams-Moulton 4:

yn+1=yn+Δt(251720g(yn+1)+646720g(yn)264720g(yn1)+106720g(yn2)19720g(yn3)).source
Mantis.TimeIntegrators.BDF1 Constant
julia
BDF1

Backward differentiation formula 1:

yn+1=yn+Δtf(yn).source
Mantis.TimeIntegrators.BDF2 Constant
julia
BDF2

Backward differentiation formula 2:

yn+1=43yn13yn1+23Δtf(yn).source
Mantis.TimeIntegrators.BDF3 Constant
julia
BDF3

Backward differentiation formula 3:

yn+1=1811yn911yn1+211yn2+611Δtf(yn).source
Mantis.TimeIntegrators.BDF4 Constant
julia
BDF4

Backward differentiation formula 4:

yn+1=4825yn3625yn1+1625yn2325yn3+1225Δtf(yn).source
Pre-implemented IMEX integrators.
Mantis.TimeIntegrators.BACKWARD_FORWARD_EULER Constant
julia
BACKWARD_FORWARD_EULER

See equation A10 in (Vos et al., 2011).

source
Mantis.TimeIntegrators.MIDPOINT_IMEX Constant
julia
MIDPOINT_IMEX

IMEX combination of the implicit and explicit midpoint rules. See equation 4.14 in (Ern and Guermond, 2023).

source
Mantis.TimeIntegrators.RK3_IMEX Constant
julia
RK3_IMEX

See equation A13 in (Vos et al., 2011).

source
Mantis.TimeIntegrators.IMEX331 Constant
julia
IMEX331

3-stage, 3rd order, A-stable IMEX scheme with optimal efficiency. See equation 4.18 in (Ern and Guermond, 2023).

source
Mantis.TimeIntegrators.CNAB2 Constant
julia
CNAB2

Second-order Crank-Nicolson/Adams-Bashforth linear multistep scheme. See equation A12 in (Vos et al., 2011).

source
Mantis.TimeIntegrators.SSSS2 Constant
julia
SSSS2

Stiffly stable splitting scheme of order 2. See (Karniadakis et al., 1991) table IV and (Vos et al., 2011) equation 59.

source

You can, of course, always initialise a new scheme yourself (see the concrete types in this section or Adding your own scheme).

Next to the pre-implemented schemes, Mantis also provides the following convenience function to take a Butcher-Tableau and turn it into a GLM-based time integrator.

Mantis.TimeIntegrators.butcher_tableau_to_glm Function
julia
butcher_tableau_to_glm(
    A::SMatrix{num_stages, num_stages, T},
    B::SVector{num_stages, T},
    C::SVector{num_stages, T},
    order::Int,
) where {num_stages, T}

Convert the given A, B, and C matrices from a Butcher Tableau into the GLM framework. Will automatically choose the correct type for the integrator.

source

Adding your own scheme

As an example of how to add your own time integrator, we look at how to implement an Almost Runge-Kutta (ARK) scheme. We use a specific scheme introduced in (Rattenbury, 2005).

This scheme requires a specialised initialisation, since it needs an estimate of the second derivative which is not accounted for in the available initialisations. As a result, this scheme is not part of Mantis.

Example: solving a simple ODE with an ARK scheme.

Consider the ODE:

dydt=λy,y(t=0)=1.0

which has exact solution

y(t)=exp(λt).

We can encode this in code as

julia
using Mantis
import StaticArrays

const lambda = -4
const t_final = 1

y_0 = 1.0
function exact_sol(t)
    return exp(lambda * t)
end

function test_ode_explicit_func!(output, yn, t)
    for n in eachindex(output)
        output[n] = lambda * yn[n]
    end
    return nothing
end
test_ode_explicit = TimeIntegrators.define_explicit_ode(test_ode_explicit_func!)
Mantis.TimeIntegrators.TimeIntegrationOperators{typeof(Main.test_ode_explicit_func!), Nothing, Nothing}(Main.test_ode_explicit_func!, nothing, nothing)

The ARK3 scheme that we use here has a known GLM-representation (see (Rattenbury, 2005), page 50). This method, however, requires its own initialisation, see (Rattenbury, 2005), pages 37-38. This initialisation, can be easily implemented as shown below. Note that the TimeLevels struct has more entries for the explicit forcing step. No other changes are required.

julia
const ARK3 = TimeIntegrators.Explicit(
    StaticArrays.SMatrix{3, 3}(0.0, 1/2, 0.0, 0.0, 0.0, 3/4, 0.0, 0.0, 0.0), # A
    StaticArrays.SMatrix{3, 3}(0.0, 0.0, 3.0, 3/4, 0.0, -3.0, 0.0, 1.0, 2.0), # B
    StaticArrays.SMatrix{3, 3}(1.0, 1.0, 1.0, 1/3, 1/6, 1/4, 1/18, 1/18, 0.0), # U
    StaticArrays.SMatrix{3, 3}(1.0, 0.0, 0.0, 1/4, 0, -2.0, 0.0, 0.0, 0.0), # V
    StaticArrays.SVector(1 / 3, 2 / 3, 1.0),
    TimeIntegrators.TimeLevels(
        [0], # y
        Int[], # Δt G
        [0, 1],  # Δt F and Δt^2 F'
    ),
    3,
)

dt = 0.1
yn = zeros(Float64, 1, 3)
yn[:, 1] .= [y_0]
yn[:, 2] .= lambda .* [y_0] .* dt
yn[:, 3] .= lambda^2 .* [y_0] .* dt^2
y_n = TimeIntegrators.TimeIntegrationSolution(yn, ARK3, nothing, 0)

for t in 0.0:dt:(t_final - dt)
    TimeIntegrators.time_integrate!(y_n, test_ode_explicit, t, dt)
end