Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/src/output_saving.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
These callbacks extend the output and saving controls available during time stepping.

```@docs
SavedValues
SavingCallback
FunctionCallingCallback
IndependentlyLinearizedSolution
LinearizingSavingCallback
```

### Saving Example
Expand Down
20 changes: 20 additions & 0 deletions src/independentlylinearizedutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ end

"""
IndependentlyLinearizedSolution
IndependentlyLinearizedSolution(prob::SciMLBase.AbstractDEProblem, num_derivatives = 0)

Efficient datastructure that holds a set of independently linearized solutions
(obtained via the `LinearizingSavingCallback`) with related, but slightly
Expand All @@ -165,6 +166,25 @@ denoting which `u` vectors are sampled at which timepoints. Provides an
efficient `iterate()` method that can be used to reconstruct coherent views
of the state variables at all timepoints, as well as an efficient `sample!()`
method that can sample at arbitrary timesteps.

## Arguments

- `prob`: differential equation problem used to infer the time, state, and storage
dimensions.
- `num_derivatives`: number of derivative rows to store in addition to the primal
state values.

## Returns

An `IndependentlyLinearizedSolution` storage object for use with
[`LinearizingSavingCallback`](@ref).

## Example

```julia
ils = IndependentlyLinearizedSolution(prob)
sol = solve(prob, solver; callback = LinearizingSavingCallback(ils))
```
"""
mutable struct IndependentlyLinearizedSolution{T, S}
# All timepoints, shared by all `us`
Expand Down
62 changes: 42 additions & 20 deletions src/saving.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
"""
SavedValues{tType<:Real, savevalType}

A struct used to save values of the time in `t::Vector{tType}` and
additional values in `saveval::Vector{savevalType}`.
Container used by [`SavingCallback`](@ref) 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.
"""
struct SavedValues{tType, savevalType}
t::Vector{tType}
saveval::Vector{savevalType}
end

"""
SavedValues(tType::DataType, savevalType::DataType)
SavedValues(tType::Type, savevalType::Type)

Return `SavedValues{tType, savevalType}` with empty storage vectors.

## Arguments

- `tType`: the element type of saved time points.
- `savevalType`: the element type of saved values returned by the saving function.

## Example

```julia
saved_values = SavedValues(Float64, Tuple{Float64, Float64})
cb = SavingCallback((u, t, integrator) -> (sum(u), maximum(u)), saved_values)
```
"""
function SavedValues(::Type{tType}, ::Type{savevalType}) where {tType, savevalType}
return SavedValues{tType, savevalType}(Vector{tType}(), Vector{savevalType}())
Expand Down Expand Up @@ -360,34 +377,39 @@ end
LinearizingSavingCallback(ils::IndependentlyLinearizedSolution)
LinearizingSavingCallback(ilss::Vector{IndependentlyLinearizedSolution})

Provides a saving callback that inserts interpolation points into your signal such that
a naive linear interpolation of the resultant saved values will be within `abstol`/`reltol`
of the higher-order interpolation of your solution. This essentially makes a time/space
tradeoff, where more points in time are saved, costing more memory, but interpolation is
incredibly cheap and downstream algorithm complexity is reduced by not needing to bother
with multiple interpolation types.
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.

This callback generator takes in an `IndependentlyLinearizedSolution` object to store
output into. An `IndependentlyLinearizedSolution` object itself controls how many
derivatives (if any) to linearize along with the primal states themselves.
## Arguments

- `ils`: the [`IndependentlyLinearizedSolution`](@ref) storage object to fill.
- `ilss`: storage objects for multiple independently linearized solutions.

## Keyword Arguments

Example usage:
- `interpolate_mask`: a `BitVector` selecting the `u` indices for which the
integrator interpolant can be queried. False indices are linearly interpolated
from the solution time points without subdivision.
- `abstol`: absolute tolerance for comparing linearized and integrator interpolation.
Defaults to the integrator absolute tolerance.
- `reltol`: relative tolerance for comparing linearized and integrator interpolation.
Defaults to the integrator relative tolerance.

## Returns

A `DiscreteCallback` that stores independently linearized output in `ils`.

## Example

```julia
ils = IndependentlyLinearizedSolution(prob)
solve(prob, solver; callback=LinearizingSavingCallback(ils))
sol = solve(prob, solver; callback = LinearizingSavingCallback(ils))
```

# Keyword Arguments
- `interpolate_mask::BitVector`: a set of `u` indices for which the integrator
interpolant can be queried. Any false indices will be linearly-interpolated
based on the `sol.t` points instead (no subdivision). This is useful for when
a solution needs to ignore certain indices due to badly-behaved interpolation.
"""
function LinearizingSavingCallback(
ils::IndependentlyLinearizedSolution{T, S};
Expand Down
Loading