Numerical Integration Callbacks
Sometimes one may want to solve an integral simultaneously to the solution of a differential equation. For example, assume we want to solve:
\[u^\prime = f(u,p,t) h = \int_{t_0}^{t_f} g(u,p,t) dt\]
While one can use the ODE solver's dense solution to call an integration scheme on sol(t)
after the solve, this can be memory intensive. Another way one can solve this problem is by extending the system, i.e.:
\[u^\prime = f(u,p,t) h^\prime = g(u,p,t)\]
with $h(t_0) = 0$, so then $h(t_f)$ would be the solution to the integral. However, many differential equation solvers scale superlinearly with the equation size and thus this could add an extra cost to the solver process.
The IntegratingCallback
allows one to be able to solve such definite integrals in a way that is both memory and compute efficient. It uses the free local interpolation of a given step in order to approximate the Gaussian quadrature for a given step to the order of the numerical differential equation solve, thus achieving accuracy while not requiring the post-solution dense interpolation to be saved. By doing this via a callback, this method is able to easily integrate with functionality that introduces discontinuities, like other callbacks, in a way that is more accurate than a direct integration post solve.
The IntegratingSumCallback
is the same, but instead of returning the timeseries of the interval results of the integration, it simply returns the final integral value.
The IntegratingGKCallback
uses Gauss-Kronrod quadrature method in order to allow for error control.
The IntegratingGKSumCallback
is the same, but instead of returning the timeseries of the interval results of the integration, it simply returns the final integral value.
DiffEqCallbacks.IntegratingCallback
— FunctionIntegratingCallback(integrand_func,
integrand_values::IntegrandValues, integrand_prototype)
Let one define a function integrand_func(u, t, integrator)::typeof(integrand_prototype)
which returns Integral(integrand_func(u(t),t)dt over the problem tspan.
Arguments
integrand_func(out, u, t, integrator)
for in-place problems andout = integrand_func(u, t, integrator)
for out-of-place problems. Returns the quantity in the integral for computing dG/dp. Note that for out-of-place problems, this should allocate the output (not as a view tou
).integrand_values::IntegrandValues
is the types thatintegrand_func
will return, i.e.integrand_func(t, u, integrator)::integrandType
. It's specified viaIntegrandValues(integrandType)
, i.e. give the type thatintegrand_func
will output (or higher compatible type).integrand_prototype
is a prototype of the output from the integrand.
The outputted values are saved into integrand_values
. The values are found via integrand_values.integrand
.
DiffEqCallbacks.IntegrandValues
— TypeIntegrandValues{integrandType}
A struct used to save values of the integrand values in integrand::Vector{integrandType}
.
DiffEqCallbacks.IntegratingSumCallback
— FunctionIntegratingCallback(integrand_func,
integrand_values::IntegrandValues,
cache = nothing)
Lets one define a function integrand_func(u, t, integrator)
which returns Integral(integrand_func(u(t),t)dt over the problem tspan.
Arguments
integrand_func(out, u, t, integrator)
for in-place problems andout = integrand_func(u, t, integrator)
for out-of-place problems. Returns the quantity in the integral for computing dG/dp. Note that for out-of-place problems, this should allocate the output (not as a view tou
).integrand_values::IntegrandValues
is the types thatintegrand_func
will return, i.e.integrand_func(t, u, integrator)::integrandType
. It's specified viaIntegrandValues(integrandType)
, i.e. give the type thatintegrand_func
will output (or higher compatible type).integrand_prototype
is a prototype of the output from the integrand.
The outputted values are saved into integrand_values
. The values are found via integrand_values.integrand
.
DiffEqCallbacks.IntegrandValuesSum
— TypeIntegrandValuesSum{integrandType}
A struct used to save values of the integrand values in integrand::Vector{integrandType}
.
DiffEqCallbacks.IntegratingGKCallback
— FunctionIntegratingGKCallback(integrand_func,
integrand_values::IntegrandValues, integrand_prototype)
Let one define a function integrand_func(u, t, integrator)::typeof(integrand_prototype)
which returns Integral(integrand_func(u(t),t)dt) over the problem tspan.
Arguments
integrand_func(out, u, t, integrator)
for in-place problems andout = integrand_func(u, t, integrator)
for out-of-place problems. Returns the quantity in the integral for computing dG/dp. Note that for out-of-place problems, this should allocate the output (not as a view tou
).integrand_values::IntegrandValues
is the types thatintegrand_func
will return, i.e.integrand_func(t, u, integrator)::integrandType
. It's specified viaIntegrandValues(integrandType)
, i.e. give the type thatintegrand_func
will output (or higher compatible type).integrand_prototype
is a prototype of the output from the integrand.
The outputted values are saved into integrand_values
. The values are found via integrand_values.integrand
.
DiffEqCallbacks.IntegratingGKSumCallback
— FunctionIntegratingCallback(integrand_func,
integrand_values::IntegrandValues,
cache = nothing)
Lets one define a function integrand_func(u, t, integrator)
which returns Integral(integrand_func(u(t),t)dt over the problem tspan.
Arguments
integrand_func(out, u, t, integrator)
for in-place problems andout = integrand_func(u, t, integrator)
for out-of-place problems. Returns the quantity in the integral for computing dG/dp. Note that for out-of-place problems, this should allocate the output (not as a view tou
).integrand_values::IntegrandValues
is the types thatintegrand_func
will return, i.e.integrand_func(t, u, integrator)::integrandType
. It's specified viaIntegrandValues(integrandType)
, i.e. give the type thatintegrand_func
will output (or higher compatible type).integrand_prototype
is a prototype of the output from the integrand.
The outputted values are saved into integrand_values
. The values are found via integrand_values.integrand
.
Example
using OrdinaryDiffEq, DiffEqCallbacks, Test
prob = ODEProblem((u, p, t) -> [1.0], [0.0], (0.0, 1.0))
integrated = IntegrandValues(Float64, Vector{Float64})
sol = solve(prob, Euler(),
callback = IntegratingCallback(
(u, t, integrator) -> [1.0], integrated, Float64[0.0]),
dt = 0.1)
@test all(integrated.integrand .≈ [[0.1] for i in 1:10])
integrated = IntegrandValues(Float64, Vector{Float64})
sol = solve(prob, Euler(),
callback = IntegratingCallback(
(u, t, integrator) -> [u[1]], integrated, Float64[0.0]),
dt = 0.1)
@test all(integrated.integrand .≈ [[((n * 0.1)^2 - ((n - 1) * (0.1))^2) / 2] for n in 1:10])
@test sum(integrated.integrand)[1] ≈ 0.5
integrated = IntegrandValuesSum(zeros(1))
sol = solve(prob, Euler(),
callback = IntegratingSumCallback(
(u, t, integrator) -> [1.0], integrated, Float64[0.0]),
dt = 0.1)
@test integrated.integrand[1] == 1
integrated = IntegrandValuesSum(zeros(1))
sol = solve(prob, Euler(),
callback = IntegratingSumCallback(
(u, t, integrator) -> [u[1]], integrated, Float64[0.0]),
dt = 0.1)
@test integrated.integrand[1] == 0.5
integrated = IntegrandValues(Float64, Vector{Float64})
sol = solve(prob, Euler(),
callback = IntegratingGKCallback(
(u, t, integrator) -> [cos.(1000*u[1])], integrated, Float64[0.0], 1e-7),
dt = 0.1)
@test sum(integrated.integrand)[1] .≈ sin(1000)/1000
integrated = IntegrandValuesSum(zeros(1))
sol = solve(prob, Euler(),
callback = IntegratingGKSumCallback(
(u, t, integrator) -> [cos.(1000*u[1])], integrated, Float64[0.0], 1e-7),
dt = 0.1)
@test integrated.integrand[1] ≈ sin(1000)/1000
Test Passed