Skip to content

Commit f9b86e9

Browse files
authored
rosenbrock methods (#89)
* RosenbrockMethod * RosenbrockMethod tests * add tutorial * format * test show and add new tutorial to docs/make.jl * set version to 2.13.0 * mention ROW in RosenbrockMethod docstring * fix typos in new tutorial
1 parent f7c3ce5 commit f9b86e9

File tree

7 files changed

+281
-3
lines changed

7 files changed

+281
-3
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "RootedTrees"
22
uuid = "47965b36-3f3e-11e9-0dcf-4570dfd42a8c"
33
authors = ["Hendrik Ranocha <mail@ranocha.de> and contributors"]
4-
version = "2.12.2"
4+
version = "2.13.0"
55

66
[deps]
77
Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ makedocs(modules = [RootedTrees],
5353
"Tutorials" => [
5454
"tutorials/RK_order_conditions.md",
5555
"tutorials/ARK_order_conditions.md",
56+
"tutorials/Rosenbrock_order_conditions.md",
5657
],
5758
# "Benchmarks" => "benchmarks.md",
5859
"API reference" => "api_reference.md",

docs/src/tutorials/ARK_order_conditions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ ARK methods are represented as
3737
The order conditions of RK methods can be derived using colored rooted trees.
3838
In [RootedTrees.jl](https://github.com/SciML/RootedTrees.jl), this
3939
functionality is implemented in [`residual_order_condition`](@ref).
40-
Thus, a [`RungeKuttaMethod`](@ref) is of order ``p`` if the
40+
Thus, an [`AdditiveRungeKuttaMethod`](@ref) is of order ``p`` if the
4141
[`residual_order_condition`](@ref) vanishes for all colored rooted trees
4242
with [`order`](@ref) up to ``p`` and ``N`` colors. The most important case
4343
is ``N = 2``, i.e., [`BicoloredRootedTree`](@ref)s as special case of
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Rosenbrock methods
2+
3+
Consider an ordinary differential equation (ODE) of the form
4+
```math
5+
u'(t) = f(t, u(t)).
6+
```
7+
8+
A Rosenbrock (or Rosenbrock-Wanner, ROW) method with ``s`` stages is given
9+
by its coefficients
10+
```math
11+
\gamma = (\gamma_{i,j})_{i,j} \in \mathbb{R}^{s \times s}, \quad
12+
A = (a_{i,j})_{i,j} \in \mathbb{R}^{s \times s}, \quad
13+
b = (b_i)_i \in \mathbb{R}^{s}, \quad
14+
c = (c_i)_i \in \mathbb{R}^{s}.
15+
```
16+
Usually, the consistency condition
17+
```math
18+
\forall i\colon \quad c_i = \sum_j a_{i,j}
19+
```
20+
is assumed, which reduces all analysis to autonomous problems.
21+
22+
The step from ``u^{n}`` to ``u^{n+1}`` is given by
23+
```math
24+
\begin{aligned}
25+
k^i &= \Delta t f\bigbl(u^n + \sum_j a_{i,j} k^j \bibgr) + \Delta t J \sum_j \gamma_{ij} k_j, \\
26+
u^{n+1} &= u^n + \sum_i b_{i} k^i.
27+
\end{aligned}
28+
```
29+
30+
In [RootedTrees.jl](https://github.com/SciML/RootedTrees.jl),
31+
ROW methods are represented as
32+
[`RosenbrockMethod`](@ref)s.
33+
34+
35+
## Order conditions
36+
37+
The order conditions of ROW methods can be derived using rooted trees.
38+
In [RootedTrees.jl](https://github.com/SciML/RootedTrees.jl), this
39+
functionality is again implemented in [`residual_order_condition`](@ref).
40+
Thus, a [`RosenbrockMethod`](@ref) is of order ``p`` if the
41+
[`residual_order_condition`](@ref) vanishes for all rooted trees
42+
with [`order`](@ref) up to ``p``.
43+
44+
For example, the method GRK4A of
45+
[Kaps and Rentrop (1979)](https://doi.org/10.1007/BF01396495) can be
46+
written as follows.
47+
48+
```@example GRK4A
49+
using RootedTrees
50+
51+
γ = [0.395 0 0 0;
52+
-0.767672395484 0.395 0 0;
53+
-0.851675323742 0.522967289188 0.395 0;
54+
0.288463109545 0.880214273381e-1 -.337389840627 0.395]
55+
A = [0 0 0 0;
56+
0.438 0 0 0;
57+
0.796920457938 0.730795420615e-1 0 0;
58+
0.796920457938 0.730795420615e-1 0 0]
59+
b = [0.199293275701, 0.482645235674, 0.680614886256e-1, 0.25]
60+
ros = RosenbrockMethod(γ, A, b)
61+
```
62+
63+
To verify that this method is at least fourth-order accurate, we can
64+
check the [`residual_order_condition`](@ref)s up to this order.
65+
66+
```@example GRK4A
67+
using Test
68+
69+
@testset "GRK4A, order 4" begin
70+
for o in 0:4
71+
for t in RootedTreeIterator(o)
72+
val = residual_order_condition(t, ros)
73+
@test abs(val) < 3000 * eps()
74+
end
75+
end
76+
end
77+
nothing # hide
78+
```
79+
80+
To verify that this method does not satisfy the order conditions
81+
for an order of accuracy of five, we can use the following code.
82+
83+
```@example GRK4A
84+
85+
@testset "GRK4A, not order 5" begin
86+
s = 0.0
87+
for t in RootedTreeIterator(5)
88+
s += abs(residual_order_condition(t, ros))
89+
end
90+
@test s > 0.06
91+
end
92+
nothing # hide
93+
```

src/RootedTrees.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export partition_forest, PartitionForestIterator,
2727

2828
export all_splittings, SplittingIterator
2929

30-
export RungeKuttaMethod, AdditiveRungeKuttaMethod
30+
export RungeKuttaMethod, AdditiveRungeKuttaMethod, RosenbrockMethod
3131

3232
abstract type AbstractRootedTree end
3333

src/time_integration_methods.jl

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,131 @@ function residual_order_condition(t::ColoredRootedTree, ark::AdditiveRungeKuttaM
287287

288288
(ew - one(T) / γ(t)) / σ(t)
289289
end
290+
291+
"""
292+
RosenbrockMethod(γ, A, b, c=vec(sum(A, dims=2)))
293+
294+
Represent a Rosenbrock (or Rosenbrock-Wanner, ROW) method with
295+
coefficients `γ`, `A`, `b`, and `c`.
296+
If `c` is not provided, the usual "row sum" requirement of consistency with
297+
autonomous problems is applied.
298+
299+
# Reference
300+
301+
- Ernst Hairer, Gerhard Wanner.
302+
Solving ordinary differential equations II: Stiff and differential-algebraic problems.
303+
Springer, 2010.
304+
Section IV.7
305+
"""
306+
struct RosenbrockMethod{T, MatT <: AbstractMatrix{T}, VecT <: AbstractVector{T}} <:
307+
AbstractTimeIntegrationMethod
308+
γ::MatT
309+
A::MatT
310+
b::VecT
311+
c::VecT
312+
end
313+
314+
function RosenbrockMethod::AbstractMatrix, A::AbstractMatrix,
315+
b::AbstractVector,
316+
c::AbstractVector = vec(sum(A, dims = 2)))
317+
T = promote_type(eltype(γ), eltype(A), eltype(b), eltype(c))
318+
= T.(γ)
319+
_A = T.(A)
320+
_b = T.(b)
321+
_c = T.(c)
322+
return RosenbrockMethod(_γ, _A, _b, _c)
323+
end
324+
325+
Base.eltype(ros::RosenbrockMethod{T}) where {T} = T
326+
327+
function Base.show(io::IO, ros::RosenbrockMethod)
328+
print(io, "RosenbrockMethod{", eltype(ros), "}")
329+
if get(io, :compact, false)
330+
print(io, "(")
331+
show(io, ros.γ)
332+
print(io, ", ")
333+
show(io, ros.A)
334+
print(io, ", ")
335+
show(io, ros.b)
336+
print(io, ", ")
337+
show(io, ros.c)
338+
print(io, ")")
339+
else
340+
print(io, " with\nγ: ")
341+
show(io, MIME"text/plain"(), ros.γ)
342+
print(io, "\nA: ")
343+
show(io, MIME"text/plain"(), ros.A)
344+
print(io, "\nb: ")
345+
show(io, MIME"text/plain"(), ros.b)
346+
print(io, "\nc: ")
347+
show(io, MIME"text/plain"(), ros.c)
348+
print(io, "\n")
349+
end
350+
end
351+
352+
"""
353+
elementary_weight(t::RootedTree, ros::RosenbrockMethod)
354+
355+
Compute the elementary weight Φ(`t`) of the [`RosenbrockMethod`](@ref) `ros`
356+
for a rooted tree `t``.
357+
"""
358+
function elementary_weight(t::RootedTree, ros::RosenbrockMethod)
359+
dot(ros.b, derivative_weight(t, ros))
360+
end
361+
362+
"""
363+
derivative_weight(t::RootedTree, ros::RosenbrockMethod)
364+
365+
Compute the derivative weight (ΦᵢD)(`t`) of the [`RosenbrockMethod`](@ref) `ros`
366+
for the rooted tree `t`.
367+
"""
368+
function derivative_weight(t::RootedTree, ros::RosenbrockMethod)
369+
γ = ros.γ
370+
A = ros.A
371+
c = ros.c
372+
373+
# Initialize `result` with the identity element of pointwise multiplication `.*`
374+
result = zero(c) .+ one(eltype(c))
375+
376+
# Count the number of subtrees to decide which matrix to use for multiplications
377+
num_subtrees = 0
378+
for subtree in SubtreeIterator(t)
379+
num_subtrees += 1
380+
end
381+
if num_subtrees == 1
382+
matrix = A + γ
383+
else
384+
matrix = A
385+
end
386+
387+
# Iterate over all subtrees and update the `result` using recursion
388+
for subtree in SubtreeIterator(t)
389+
tmp = matrix * derivative_weight(subtree, ros)
390+
result = result .* tmp
391+
end
392+
393+
return result
394+
end
395+
396+
"""
397+
residual_order_condition(t::RootedTree, ros::RosenbrockMethod)
398+
399+
The residual of the order condition
400+
`(Φ(t) - 1/γ(t)) / σ(t)`
401+
with [`elementary_weight`](@ref) `Φ(t)`, [`density`](@ref) `γ(t)`, and
402+
[`symmetry`](@ref) `σ(t)` of the [`RosenbrockMethod`](@ref) `ros`
403+
for the rooted tree `t`.
404+
405+
# Reference
406+
407+
- Ernst Hairer, Gerhard Wanner.
408+
Solving ordinary differential equations II: Stiff and differential-algebraic problems.
409+
Springer, 2010.
410+
Section IV.7
411+
"""
412+
function residual_order_condition(t::RootedTree, ros::RosenbrockMethod)
413+
ew = elementary_weight(t, ros)
414+
T = typeof(ew)
415+
416+
(ew - one(T) / γ(t)) / σ(t)
417+
end

test/runtests.jl

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,62 @@ using Aqua: Aqua
12751275
@test abs(residual_order_condition(t, ark)) > 10 * eps()
12761276
end
12771277
end
1278+
1279+
@testset "RosenbrockMethod, original Rosenbrock" begin
1280+
γ = [1-sqrt(2) / 2 0;
1281+
0 1-sqrt(2) / 2]
1282+
A = [0 0;
1283+
(sqrt(2) - 1)/2 0]
1284+
b = [0, 1]
1285+
ros = @inferred RosenbrockMethod(γ, A, b)
1286+
1287+
# second-order accurate
1288+
@test abs(@inferred(residual_order_condition(rootedtree(Int[]), ros))) <
1289+
10 * eps()
1290+
@test abs(@inferred(residual_order_condition(rootedtree(Int[1]), ros))) <
1291+
10 * eps()
1292+
@test abs(@inferred(residual_order_condition(rootedtree(Int[1, 2]), ros))) <
1293+
10 * eps()
1294+
@test abs(@inferred(residual_order_condition(rootedtree(Int[1, 2, 3]), ros))) >
1295+
0.04
1296+
@test abs(@inferred(residual_order_condition(rootedtree(Int[1, 2, 2]), ros))) >
1297+
0.14
1298+
end
1299+
1300+
@testset "RosenbrockMethod, GRK4A (Kaps and Rentrop, 1979)" begin
1301+
# Kaps, Rentrop (1979)
1302+
# Generalized Runge-Kutta methods of order four with stepsize control
1303+
# for stiff ordinary differential equations
1304+
# https://doi.org/10.1007/BF01396495
1305+
γ = [0.395 0 0 0;
1306+
-0.767672395484 0.395 0 0;
1307+
-0.851675323742 0.522967289188 0.395 0;
1308+
0.288463109545 0.880214273381e-1 -0.337389840627 0.395]
1309+
A = [0 0 0 0;
1310+
0.438 0 0 0;
1311+
0.796920457938 0.730795420615e-1 0 0;
1312+
0.796920457938 0.730795420615e-1 0 0]
1313+
b = [0.199293275701, 0.482645235674, 0.680614886256e-1, 0.25]
1314+
ros = @inferred RosenbrockMethod(γ, A, b)
1315+
1316+
@test_nowarn show(ros)
1317+
@test_nowarn show(IOContext(stdout, :compact => true), ros)
1318+
1319+
# fourth-order accurate
1320+
for o in 0:4
1321+
for t in RootedTreeIterator(o)
1322+
val = @inferred residual_order_condition(t, ros)
1323+
@test abs(val) < 3000 * eps()
1324+
end
1325+
end
1326+
1327+
# not fifth-order accurate
1328+
s = 0.0
1329+
for t in RootedTreeIterator(5)
1330+
s += abs(residual_order_condition(t, ros))
1331+
end
1332+
@test s > 0.06
1333+
end
12781334
end # @testset "Order conditions"
12791335

12801336
@testset "plots" begin

0 commit comments

Comments
 (0)