Skip to content

Commit 8dadb64

Browse files
Merge pull request #150 from SciML/hr/stability_region_for_methods
allow computing the stability function directly from an algorithm
2 parents 76afa67 + 6f6cb42 commit 8dadb64

File tree

5 files changed

+73
-6
lines changed

5 files changed

+73
-6
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DiffEqDevTools"
22
uuid = "f3b72e0c-5b89-59e1-b016-84e28bfd966d"
33
authors = ["Chris Rackauckas <[email protected]>"]
4-
version = "2.46.0"
4+
version = "2.47.0"
55

66
[deps]
77
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"

src/DiffEqDevTools.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module DiffEqDevTools
22

3+
using DiffEqBase: AbstractODEAlgorithm
34
using DiffEqBase, RecipesBase, RecursiveArrayTools, DiffEqNoiseProcess, StructArrays
45
using NLsolve, LinearAlgebra, RootedTrees
56

src/ode_tableaus.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
deduce_Butcher_tableau(erk, T=Float64)
2+
deduce_Butcher_tableau(erk, T = Float64)
33

44
Deduce and return the Butcher coefficients `A, b, c` by solving some
55
specific ordinary differential equations using the explicit Runge-Kutta

src/tableau_info.jl

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ Defines the length of a Runge-Kutta method to be the number of stages.
66
Base.length(tab::ODERKTableau) = tab.stages
77

88
"""
9-
`stability_region(z,tab::ODERKTableau)`
9+
`stability_region(z, tab::ODERKTableau; embedded = false)`
1010
1111
Calculates the stability function from the tableau at `z`. Stable if <1.
12+
If `embedded = true`, the stability function is calculated for the embedded
13+
method. Otherwise, the stability function is calculated for the main method
14+
(default).
1215
1316
```math
1417
r(z) = 1 + z bᵀ(I - zA)⁻¹ e
@@ -24,14 +27,49 @@ function stability_region(z, tab::ODERKTableau; embedded = false)
2427
end
2528

2629
"""
27-
`stability_region(tab::ODERKTableau; initial_guess=-3.0)`
30+
stability_region(z, alg::AbstractODEAlgorithm)
31+
32+
Calculates the stability function from the algorithm `alg` at `z`.
33+
The stability region of a possible embedded method cannot be calculated
34+
using this method.
35+
36+
If you use an implicit method, you may run into convergence issues when
37+
the value of `z` is outside of the stability region, e.g.,
38+
39+
```julia-repl
40+
julia> typemin(Float64)
41+
-Inf
42+
43+
julia> stability_region(typemin(Float64), ImplicitEuler())
44+
┌ Warning: Newton steps could not converge and algorithm is not adaptive. Use a lower dt.
45+
46+
julia> nextfloat(typemin(Float64))
47+
-1.7976931348623157e308
48+
49+
julia> stability_region(nextfloat(typemin(Float64)), ImplicitEuler())
50+
0.0
51+
```
52+
"""
53+
function stability_region(z, alg::AbstractODEAlgorithm)
54+
u0 = one(z)
55+
tspan = (zero(real(z)), one(real(z)))
56+
ode = ODEProblem{false}((u, p, t) -> z * u, u0, tspan)
57+
integrator = init(ode, alg; adaptive = false, dt = 1)
58+
step!(integrator)
59+
return integrator.u[end]
60+
end
61+
62+
"""
63+
`stability_region(tab_or_alg::Union{ODERKTableau, AbstractODEAlgorithm};
64+
initial_guess=-3.0)`
2865
2966
Calculates the length of the stability region in the real axis.
3067
See also [`imaginary_stability_interval`](@ref).
3168
"""
32-
function stability_region(tab::ODERKTableau; initial_guess = -3.0, kw...)
69+
function stability_region(tab_or_alg::Union{ODERKTableau, AbstractODEAlgorithm};
70+
initial_guess = -3.0, kw...)
3371
residual! = function (resid, x)
34-
resid[1] = abs(stability_region(x[1], tab)) - 1
72+
resid[1] = abs(stability_region(x[1], tab_or_alg)) - 1
3573
end
3674
sol = nlsolve(residual!, [initial_guess]; kw...)
3775
sol.zero[1]
@@ -55,6 +93,24 @@ function imaginary_stability_interval(tab::ODERKTableau;
5593
sol.zero[1]
5694
end
5795

96+
"""
97+
imaginary_stability_interval(alg::ODERKTableau;
98+
initial_guess = 20.0)
99+
100+
Calculates the length of the imaginary stability interval, i.e.,
101+
the size of the stability region on the imaginary axis.
102+
See also [`stability_region`](@ref).
103+
"""
104+
function imaginary_stability_interval(alg::AbstractODEAlgorithm;
105+
initial_guess = 20.0,
106+
kw...)
107+
residual! = function (resid, x)
108+
resid[1] = abs(stability_region(im * x[1], alg)) - 1
109+
end
110+
sol = nlsolve(residual!, [initial_guess]; kw...)
111+
sol.zero[1]
112+
end
113+
58114
function RootedTrees.residual_order_condition(tab::ODERKTableau, order::Int,
59115
reducer = nothing, mapper = x -> x^2;
60116
embedded = false)

test/stability_region_test.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
using DiffEqDevTools, Test
2+
using OrdinaryDiffEq
23

34
@test stability_region(constructDormandPrince6(), initial_guess = -3.5)-3.95413 rtol=1e-3
45
@test stability_region(constructTsitourasPapakostas6(), initial_guess = -3.5)-3.95413 rtol=1e-3
56
@test stability_region(constructRadauIIA5(), initial_guess = 12.0)11.84 rtol=1e-2
67

8+
@test @inferred(stability_region(constructTsitouras5()))@inferred(stability_region(Tsit5()))
9+
@test @inferred(stability_region(constructSSPRK104()))@inferred(stability_region(SSPRK104()))
10+
@test @inferred(stability_region(constructImplicitEuler()))@inferred(stability_region(ImplicitEuler()))
11+
@test @inferred(abs(stability_region(nextfloat(typemin(Float64)), ImplicitEuler()))) < eps(Float64)
12+
713
@test @inferred(imaginary_stability_interval(constructSSPRK33()))sqrt(3)
814
@test @inferred(imaginary_stability_interval(constructSSPRK33(Float32)))sqrt(3.0f0)
915
@test @inferred(imaginary_stability_interval(constructKutta3()))sqrt(3)
1016
@test @inferred(imaginary_stability_interval(constructKutta3(Float32)))sqrt(3.0f0)
1117
@test @inferred(imaginary_stability_interval(constructRK4()))2.8284271247
18+
19+
@test @inferred(imaginary_stability_interval(SSPRK33()))sqrt(3)
20+
@test @inferred(imaginary_stability_interval(SSPRK33(), initial_guess = 5.0f0))sqrt(3.0f0)
21+
@test @inferred(imaginary_stability_interval(RK4()))2.8284271247

0 commit comments

Comments
 (0)