Output and Saving Controls

These callbacks extend the output and saving controls available during time stepping.

DiffEqCallbacks.SavedValuesType
SavedValues{tType<:Real, savevalType}

Container used by SavingCallback to store saved time points and user-defined values.

Fields

  • t::Vector{tType}: saved time points.
  • saveval::Vector{savevalType}: values returned by the saving function.

Construct empty storage with SavedValues(tType, savevalType). The callback appends to both vectors in place; do not mutate them while a solve is active.

source
DiffEqCallbacks.SavingCallbackFunction
SavingCallback(save_func, saved_values::SavedValues;
    saveat = Vector{eltype(saved_values.t)}(),
    save_everystep = isempty(saveat),
    save_start = save_everystep || isempty(saveat) || saveat isa Number,
    save_end = save_everystep || isempty(saveat) || saveat isa Number,
    tdir = 1) -> DiscreteCallback

The saving callback lets you define a function save_func(u, t, integrator) which returns quantities of interest that shall be saved.

Arguments

  • save_func: function called as save_func(u, t, integrator). It must return a value compatible with eltype(saved_values.saveval) and must not return a view of u.
  • saved_values::SavedValues: storage whose time and value element types match the integration time and the output of save_func.

Keywords

  • saveat = Vector{eltype(saved_values.t)}(): selected integration times, or a scalar interval at which to evaluate save_func throughout the problem time span.
  • save_everystep::Bool = isempty(saveat): whether to save after every accepted step.
  • save_start::Bool = ...: whether to save at the initial condition.
  • save_end::Bool = ...: whether to save at the final time.
  • tdir = 1: integration direction used to order saveat. Set this to sign(tspan[end] - tspan[1]) for reverse-time problems.

The output values are saved into saved_values. Time points are found via saved_values.t and the values are saved_values.saveval.

Returns

  • DiscreteCallback: a callback that evaluates save_func at the requested times and appends the results to saved_values.

Examples

using DiffEqCallbacks, OrdinaryDiffEq

prob = ODEProblem((u, p, t) -> -u, 1.0, (0.0, 1.0))
saved_values = SavedValues(Float64, Float64)
cb = SavingCallback((u, t, integrator) -> u^2, saved_values; saveat = 0.0:0.25:1.0)

sol = solve(prob, Tsit5(); callback = cb)
source
DiffEqCallbacks.FunctionCallingCallbackFunction
FunctionCallingCallback(func;
    funcat = Vector{Float64}(),
    func_everystep = isempty(funcat),
    func_start = true,
    tdir = 1) -> DiscreteCallback

The function calling callback lets you define a function func(u,t,integrator) which gets called at the time points of interest.

Arguments

  • func: function called as func(u, t, integrator) at each selected time. Its return value is ignored, and it should not modify u or the integrator.

Keywords

  • funcat = Vector{Float64}(): selected integration times, or a scalar interval at which to call func throughout the problem time span.
  • func_everystep::Bool = isempty(funcat): whether to call func after every accepted step.
  • func_start::Bool = true: whether to call func at the initial condition.
  • tdir = 1: integration direction used to order funcat. Set this to sign(tspan[end] - tspan[1]) for reverse-time problems.

Returns

  • DiscreteCallback: a callback that calls func without modifying the integrator state.

Examples

using DiffEqCallbacks, OrdinaryDiffEq

seen = Float64[]
func = (u, t, integrator) -> push!(seen, t)
cb = FunctionCallingCallback(func; funcat = 0.0:0.25:1.0)

prob = ODEProblem((u, p, t) -> -u, 1.0, (0.0, 1.0))
sol = solve(prob, Tsit5(); callback = cb)
source
DiffEqCallbacks.IndependentlyLinearizedSolutionType
IndependentlyLinearizedSolution{T, S}
IndependentlyLinearizedSolution(prob::SciMLBase.AbstractDEProblem,
    num_derivatives = 0) -> IndependentlyLinearizedSolution

Efficient storage for independently linearized state components obtained from LinearizingSavingCallback. It stores a single time vector with a packed BitMatrix denoting which state components were sampled at each time and implements Julia's iteration interface to reconstruct a coherent state at every stored time.

Fields

  • ts::Vector{T}: sorted union of the stored time points.
  • us::Vector{Matrix{S}}: state and derivative samples. Each matrix corresponds to one state component, uses rows for the primal and requested derivatives, and stores its available time samples in columns.
  • time_mask::BitMatrix: maps rows of us to entries in ts; use iteration rather than interpreting this packed representation directly.

The vectors and mask are storage owned by the callback. Iterate the result after the solve; do not mutate its fields while solving.

Arguments

  • prob: differential equation problem used to infer the time, state, and storage dimensions.
  • num_derivatives::Int = 0: nonnegative number of derivative rows to store in addition to the primal state values.

Iteration

Iteration yields (t, values) for each stored time, where values is a matrix with one row per state component and columns containing the primal followed by the requested derivatives.

Returns

Examples

using DiffEqCallbacks, OrdinaryDiffEq

prob = ODEProblem((du, u, p, t) -> (du .= -u), [1.0, 2.0], (0.0, 1.0))
ils = IndependentlyLinearizedSolution(prob)
sol = solve(prob, Tsit5(); callback = LinearizingSavingCallback(ils))
first_time, first_values = first(ils)
source
DiffEqCallbacks.LinearizingSavingCallbackFunction
LinearizingSavingCallback(ils::IndependentlyLinearizedSolution;
    kwargs...) -> DiscreteCallback

Return a saving callback that inserts interpolation points so that linear interpolation of the saved values is within abstol/reltol of the integrator interpolation.

The algorithm internally checks 3 equidistant points between each time point to determine goodness of fit versus the linearly interpolated function; this should be sufficient for interpolations up to the 4th order, higher orders may need more points to ensure good fit. This has not been implemented yet.

Arguments

Keywords

  • interpolate_mask::BitVector: select the state indices for which the integrator interpolant can be queried. False indices are linearly interpolated from the solution time points without subdivision. By default, all indices are selected.
  • abstol = nothing: absolute tolerance for comparing linearized and integrator interpolation. Defaults to the integrator absolute tolerance.
  • reltol = nothing: relative tolerance for comparing linearized and integrator interpolation. Defaults to the integrator relative tolerance.

Returns

  • DiscreteCallback: a callback that stores independently linearized output in ils.

Examples

using DiffEqCallbacks, OrdinaryDiffEq

prob = ODEProblem((du, u, p, t) -> (du .= -u), [1.0, 2.0], (0.0, 1.0))
ils = IndependentlyLinearizedSolution(prob)
sol = solve(prob, Tsit5(); callback = LinearizingSavingCallback(ils))
source

Saving Example

In this example, we will solve a matrix equation and at each step save a tuple of values which contains the current trace and the norm of the matrix. We build the SavedValues cache to use Float64 for time and Tuple{Float64,Float64} for the saved values, and then call the solver with the callback.

using DiffEqCallbacks, OrdinaryDiffEq, LinearAlgebra
prob = ODEProblem((du, u, p, t) -> du .= u, rand(4, 4), (0.0, 1.0))
saved_values = SavedValues(Float64, Tuple{Float64, Float64})
cb = SavingCallback((u, t, integrator) -> (tr(u), norm(u)), saved_values)
sol = solve(prob, Tsit5(), callback = cb)

print(saved_values.saveval)
[(2.2093641623702687, 2.070610270796817), (2.4419144260041548, 2.288555765051708), (3.1296422512690105, 2.9330924705708394), (4.375892248991175, 4.101074684284124), (6.0056741908513125, 5.628502025349696)]

Note that the values are retrieved from the cache as .saveval, and the time points are found as .t. If we want to control the saved times, we use saveat in the callback. The save controls like saveat act analogously to how they act in the solve function.

saved_values = SavedValues(Float64, Tuple{Float64, Float64})
cb = SavingCallback((u, t, integrator) -> (tr(u), norm(u)), saved_values,
    saveat = 0.0:0.1:1.0)
sol = solve(prob, Tsit5(), callback = cb)
print(saved_values.saveval)
print(saved_values.t)
[(2.2093641623702687, 2.070610270796817), (2.4417250197416585, 2.288378254001625), (2.6985235775186687, 2.529049185627687), (2.9823294951679387, 2.7950313437555008), (3.2959846237721333, 3.088988103730817), (3.6426254969601297, 3.413859016605139), (4.025722420994646, 3.772896444262843), (4.449113067450856, 4.169697042391042), (4.917030758514874, 4.6082282693867995), (5.4341572116601595, 5.0928778185288515), (6.0056741908513125, 5.628502025349696)][0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]