Skip to content

Commit 2c28678

Browse files
Add tests, docs, and monorepo wiring for GlobalDiffEq
Adds GLEE and companion-estimator test suites (convergence orders, estimate fidelity, adaptive stepping, integrator interface, function evaluation counts, argument validation), a Global Error Control docs section with all public names rendered, and the docs env/make wiring. The GLEE __init override extends plain problems to the partitioned state via an exact-arity invoke into OrdinaryDiffEqCore so the integrator interface works directly. QA's rendered-docs check points at the monorepo docs and excludes reexported DiffEqBase names, which are documented at their owners. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
1 parent 135320f commit 2c28678

15 files changed

Lines changed: 362 additions & 22 deletions

File tree

docs/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
33
DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d"
44
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
5+
GlobalDiffEq = "1d72d19b-84cc-4cb7-a099-7cbdb9ccc67c"
56
ImplicitDiscreteSolve = "3263718b-31ed-49cf-8a0f-35a466e8af96"
67
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
78
OrdinaryDiffEqAMF = "08082164-f6a1-4363-b3df-3aa6fcf571ad"
@@ -39,6 +40,7 @@ OrdinaryDiffEqVerner = "79d7bb75-1356-48c1-b8c0-6832512096c2"
3940
[sources]
4041
DiffEqBase = {path = "../lib/DiffEqBase"}
4142
DiffEqDevTools = {path = "../lib/DiffEqDevTools"}
43+
GlobalDiffEq = {path = "../lib/GlobalDiffEq"}
4244
ImplicitDiscreteSolve = {path = "../lib/ImplicitDiscreteSolve"}
4345
OrdinaryDiffEqAMF = {path = "../lib/OrdinaryDiffEqAMF"}
4446
OrdinaryDiffEqAdamsBashforthMoulton = {path = "../lib/OrdinaryDiffEqAdamsBashforthMoulton"}
@@ -76,6 +78,7 @@ OrdinaryDiffEqVerner = {path = "../lib/OrdinaryDiffEqVerner"}
7678
DiffEqBase = "7.6.1"
7779
DiffEqDevTools = "3, 4.0"
7880
Documenter = "0.27, 1"
81+
GlobalDiffEq = "1.3"
7982
ImplicitDiscreteSolve = "2"
8083
OrdinaryDiffEq = "7"
8184
OrdinaryDiffEqAMF = "2"

docs/make.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ using OrdinaryDiffEqStabilizedRK
3636
using OrdinaryDiffEqSymplecticRK
3737
using OrdinaryDiffEqTsit5
3838
using OrdinaryDiffEqVerner
39+
using GlobalDiffEq
3940

4041
cp(joinpath(@__DIR__, "Manifest.toml"), joinpath(@__DIR__, "src", "assets", "Manifest.toml"), force = true)
4142
cp(joinpath(@__DIR__, "Project.toml"), joinpath(@__DIR__, "src", "assets", "Project.toml"), force = true)
@@ -83,6 +84,7 @@ makedocs(
8384
OrdinaryDiffEqAMF,
8485
ImplicitDiscreteSolve,
8586
DiffEqDevTools,
87+
GlobalDiffEq,
8688
],
8789
linkcheck_ignore = [r"https://github.com/JuliaDiff/ForwardDiff.jl"],
8890
warnonly = [:docs_block, :missing_docs, :eval_block],

docs/pages.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ pages = [
5353
"Misc Solvers" => [
5454
"misc.md",
5555
],
56+
"Global Error Control" => [
57+
"globalerrorcontrol/GlobalDiffEq.md",
58+
],
5659
"APIs" => [
5760
"api/diffeqbase.md",
5861
"api/ordinarydiffeqcore.md",
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# GlobalDiffEq: Global Error Estimation and Control
2+
3+
Standard adaptive ODE solvers control the *local* error of each step. The
4+
local tolerances only indirectly control the *global* (accumulated) error of
5+
the solution, which can grow arbitrarily large over long integrations or on
6+
unstable problems even when every step satisfies its local tolerance. The
7+
GlobalDiffEq sublibrary provides solvers and solver wrappers that estimate
8+
the global error, and in several cases control it to a requested global
9+
tolerance.
10+
11+
To use these methods:
12+
13+
```julia
14+
using GlobalDiffEq
15+
```
16+
17+
## Choosing a method
18+
19+
- For a solution accompanied by a running, asymptotically correct estimate of
20+
its global error at every time point, use the global-error-estimating
21+
solvers [`GLEE24`](@ref), [`GLEE35`](@ref) (Constantinescu 2016), or the
22+
Dormand-Prince-based [`MM5GEE`](@ref) (Makazaga and Murua 2003). These cost
23+
only a few extra stages per step over a plain method of the same order and
24+
require nothing beyond the right-hand side `f`.
25+
- To *control* the endpoint global error to a tolerance `gtol`, wrap any
26+
adaptive solver in [`GlobalErrorTransport`](@ref) (linearized
27+
error-transport equation, Jacobian-vector products via automatic
28+
differentiation), [`GlobalDefectCorrection`](@ref) (solving for the
29+
correction; no Jacobian needed), or [`GlobalAdjoint`](@ref) (adjoint-based,
30+
for endpoint functionals; requires SciMLSensitivity and QuadGK to be
31+
loaded). Each solves the problem, estimates the endpoint global error, and
32+
tightens the local tolerances until the requested global tolerance is met.
33+
- [`GlobalRichardson`](@ref) wraps any fixed-step method in global Richardson
34+
extrapolation over whole solves, interpreting `abstol` and `reltol` as
35+
global tolerances. It is the most robust and most expensive option.
36+
37+
For example, solving with a controlled endpoint global error of `1e-8`:
38+
39+
```julia
40+
using GlobalDiffEq, OrdinaryDiffEqTsit5
41+
42+
function lorenz!(du, u, p, t)
43+
du[1] = 10.0(u[2] - u[1])
44+
du[2] = u[1] * (28.0 - u[3]) - u[2]
45+
du[3] = u[1] * u[2] - (8 / 3) * u[3]
46+
end
47+
prob = ODEProblem(lorenz!, [1.0; 0.0; 0.0], (0.0, 10.0))
48+
sol = solve(prob, GlobalDefectCorrection(Tsit5(); gtol = 1.0e-8))
49+
```
50+
51+
or solving while tracking the global error along the trajectory:
52+
53+
```julia
54+
sol = solve(prob, GLEE35(); abstol = 1.0e-8, reltol = 1.0e-8)
55+
errs = global_error_estimate(sol) # global error estimate at every sol.t
56+
```
57+
58+
## Global-error-estimating solvers
59+
60+
```@docs
61+
GLEE23
62+
GLEE24
63+
GLEE35
64+
MM5GEE
65+
global_error_estimate
66+
```
67+
68+
## Global error controlling wrappers
69+
70+
```@docs
71+
GlobalRichardson
72+
GlobalErrorTransport
73+
GlobalDefectCorrection
74+
GlobalAdjoint
75+
adjoint_error_estimate
76+
```

lib/GlobalDiffEq/Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,11 @@ OrdinaryDiffEqSSPRK = "669c94d9-1f4b-4b64-b377-1aa079aa2388"
6464
OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a"
6565
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
6666
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
67+
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
6768
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
6869
SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1"
6970
SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283"
7071
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
7172

7273
[targets]
73-
test = ["LinearAlgebra", "OrdinaryDiffEqSSPRK", "OrdinaryDiffEqTsit5", "QuadGK", "Random", "SafeTestsets", "SciMLSensitivity", "SciMLTesting", "Test"]
74+
test = ["LinearAlgebra", "OrdinaryDiffEqSSPRK", "OrdinaryDiffEqTsit5", "QuadGK", "Random", "RecursiveArrayTools", "SafeTestsets", "SciMLSensitivity", "SciMLTesting", "Test"]

lib/GlobalDiffEq/README.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
# GlobalDiffEq
22

3-
[![Build Status](https://github.com/SciML/GlobalDiffEq.jl/workflows/CI/badge.svg)](https://github.com/SciML/GlobalDiffEq.jl/actions?query=workflow%3ACI)
3+
Differential equation solvers and solver wrappers with global (accumulated)
4+
error estimates and global error control for the
5+
[DifferentialEquations.jl common solver interface](https://docs.sciml.ai/DiffEqDocs/stable/).
6+
Formerly the standalone [GlobalDiffEq.jl](https://github.com/SciML/GlobalDiffEq.jl)
7+
repository, now maintained as a sublibrary of OrdinaryDiffEq.jl.
48

5-
This is a repository for differential equation solvers with global error estimates
6-
for the [DifferentialEquations.jl common solver interface](diffeq.sciml.ai).
9+
Standard adaptive ODE solvers control the *local* error of each step, which
10+
does not bound the error accumulated over the whole integration. This package
11+
provides:
12+
13+
- `GlobalRichardson`: global Richardson extrapolation of whole solves of any
14+
fixed-step method, interpreting `abstol`/`reltol` as global tolerances.
15+
- `GLEE23`, `GLEE24`, `GLEE35`: explicit general linear methods with built-in
16+
global error estimation (Constantinescu 2016), which propagate the solution
17+
together with an asymptotically correct estimate of its global error.
18+
- `MM5GEE`: the Makazaga-Murua (2003) Dormand-Prince-based order-5 scheme
19+
with a cheap built-in global error estimate.
20+
- `GlobalErrorTransport`: global error estimation and `gtol`-based control by
21+
integrating the linearized error-transport equation driven by the
22+
dense-output defect (Shampine 1986, Berzins 1988, Lang-Verwer 2007).
23+
- `GlobalDefectCorrection`: global error estimation and control by solving
24+
for the correction (Zadunaisky 1976, Dormand-Duckers-Prince 1984/1989),
25+
requiring no Jacobian.
26+
- `GlobalAdjoint`: adjoint-based a posteriori endpoint error estimation and
27+
control (Cao and Petzold 2004), available as a package extension when
28+
SciMLSensitivity and QuadGK are loaded.
29+
30+
See the [OrdinaryDiffEq.jl documentation](https://docs.sciml.ai/OrdinaryDiffEq/stable/)
31+
for the full API reference.

lib/GlobalDiffEq/src/correction.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,4 @@ function SciMLBase.__solve(
126126
estimator, prob, alg.alg, alg.gtol, alg.options, args...;
127127
abstol, reltol, kwargs...
128128
)
129-
end
129+
end

lib/GlobalDiffEq/src/glee/algorithms.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Explicit general linear methods with built-in global error estimation
22
# (GLEE methods) from Constantinescu (2016), doi:10.1137/15M1014633.
33
abstract type AbstractGLEEAlgorithm <:
4-
OrdinaryDiffEqCore.OrdinaryDiffEqAdaptiveAlgorithm end
4+
OrdinaryDiffEqCore.OrdinaryDiffEqAdaptiveAlgorithm end
55

66
const _GLEE_DOCS_SHARED = """
77
GLEE methods propagate the solution `y` together with an asymptotically

lib/GlobalDiffEq/src/glee/caches.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@cache struct GLEECache{uType, yType, rateType, yRateType, uNoUnitsType, TabType} <:
2-
OrdinaryDiffEqCore.OrdinaryDiffEqMutableCache
2+
OrdinaryDiffEqCore.OrdinaryDiffEqMutableCache
33
u::uType
44
uprev::uType
55
tmp::uType

lib/GlobalDiffEq/src/glee/solve.jl

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,34 @@ function _glee_extended_problem(prob)
4646
return SciMLBase.remake(prob; f = extended_f, u0 = u0)
4747
end
4848

49+
_is_glee_extended(prob) = prob.f.f isa GLEEExtendedRHS
50+
51+
# init/solve on a plain ODEProblem transparently extend it to the partitioned
52+
# (y, ε) state; the invoke dispatches into OrdinaryDiffEqCore's generic __init
53+
# (its exact five-argument form, so the DiffEqBase default-algorithm catch-all
54+
# cannot be selected).
55+
function SciMLBase.__init(
56+
prob::SciMLBase.AbstractODEProblem, alg::AbstractGLEEAlgorithm,
57+
timeseries_init = (), ts_init = (), ks_init = ();
58+
kwargs...
59+
)
60+
extended_prob = _is_glee_extended(prob) ? prob : _glee_extended_problem(prob)
61+
return invoke(
62+
SciMLBase.__init,
63+
Tuple{
64+
SciMLBase.AbstractODEProblem,
65+
OrdinaryDiffEqCore.OrdinaryDiffEqAlgorithm,
66+
Any, Any, Any,
67+
},
68+
extended_prob, alg, timeseries_init, ts_init, ks_init; kwargs...
69+
)
70+
end
71+
4972
function SciMLBase.__solve(
5073
prob::SciMLBase.AbstractODEProblem, alg::AbstractGLEEAlgorithm, args...;
5174
kwargs...
5275
)
53-
extended_prob = _glee_extended_problem(prob)
54-
integrator = SciMLBase.__init(extended_prob, alg, args...; kwargs...)
76+
integrator = SciMLBase.__init(prob, alg, args...; kwargs...)
5577
SciMLBase.solve!(integrator)
5678
return integrator.sol
5779
end

0 commit comments

Comments
 (0)