Skip to content

Commit c9ad882

Browse files
authored
Merge pull request #292 from CliMA/gb/rosenbrock
Revive SSPKnoth
2 parents ff7fb46 + 5eb3518 commit c9ad882

File tree

18 files changed

+777
-83
lines changed

18 files changed

+777
-83
lines changed

docs/Manifest.toml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
julia_version = "1.10.3"
44
manifest_format = "2.0"
5-
project_hash = "6c87fe520a2f2573c1d64ad2168076eae19c6786"
5+
project_hash = "bd8d0393b0badb814c7eec6dee2d48c4c931c7ec"
66

77
[[deps.ADTypes]]
88
git-tree-sha1 = "fc02d55798c1af91123d07915a990fbb9a10d146"
@@ -112,9 +112,9 @@ version = "7.11.0"
112112

113113
[[deps.ArrayLayouts]]
114114
deps = ["FillArrays", "LinearAlgebra"]
115-
git-tree-sha1 = "29649b61e0313db0a7ad5ecf41210e4e85aea234"
115+
git-tree-sha1 = "420e2853770f50e5306b9d96b5a66f26e7c73bc6"
116116
uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
117-
version = "1.9.3"
117+
version = "1.9.4"
118118
weakdeps = ["SparseArrays"]
119119

120120
[deps.ArrayLayouts.extensions]
@@ -1209,6 +1209,12 @@ version = "2.30.1"
12091209
Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2"
12101210
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
12111211

1212+
[[deps.Literate]]
1213+
deps = ["Base64", "IOCapture", "JSON", "REPL"]
1214+
git-tree-sha1 = "596df2daea9c27da81eee63ef2cf101baf10c24c"
1215+
uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
1216+
version = "2.18.0"
1217+
12121218
[[deps.LogExpFunctions]]
12131219
deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
12141220
git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea"
@@ -1648,9 +1654,9 @@ version = "0.6.12"
16481654

16491655
[[deps.RecursiveArrayTools]]
16501656
deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "SparseArrays", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"]
1651-
git-tree-sha1 = "5232d8d580a579ded0fc25d6899c42946566793c"
1657+
git-tree-sha1 = "3400ce27995422fb88ffcd3af9945565aad947f0"
16521658
uuid = "731186ca-8d62-57ce-b412-fbd966d074cd"
1653-
version = "3.23.0"
1659+
version = "3.23.1"
16541660

16551661
[deps.RecursiveArrayTools.extensions]
16561662
RecursiveArrayToolsFastBroadcastExt = "FastBroadcast"
@@ -1740,9 +1746,9 @@ version = "0.6.42"
17401746

17411747
[[deps.SciMLBase]]
17421748
deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"]
1743-
git-tree-sha1 = "1d1d1ff37d2917cad263fa186cbc19ce4b587ccf"
1749+
git-tree-sha1 = "c15e03738d4170f92ba477273ef0528341f79a9a"
17441750
uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1745-
version = "2.40.0"
1751+
version = "2.41.0"
17461752

17471753
[deps.SciMLBase.extensions]
17481754
SciMLBaseChainRulesCoreExt = "ChainRulesCore"

docs/Project.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
1111
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
1212
Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7"
1313
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
14+
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
1415
NVTX = "5da4648a-3479-48b8-97b9-01cb529c0a1f"
1516
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1617
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
1718
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
19+
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1820
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
21+
22+
[compat]
23+
ClimaCore = "0.14.8"

docs/make.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
using Documenter, DocumenterCitations
22
using ClimaTimeSteppers
3+
using Literate
4+
5+
tutorial_basedir = "tutorials"
6+
tutorial_basedir_from_here = joinpath(@__DIR__, "src", tutorial_basedir)
7+
8+
jl_files_in_basedir = filter(endswith(".jl"), readdir(tutorial_basedir_from_here))
9+
10+
println("Building literate tutorials...")
11+
generated_tutorials = String[]
12+
for filename in jl_files_in_basedir
13+
Literate.markdown(
14+
joinpath(tutorial_basedir_from_here, filename),
15+
tutorial_basedir_from_here;
16+
execute = true,
17+
flavor = Literate.CommonMarkFlavor(),
18+
)
19+
push!(generated_tutorials, joinpath(tutorial_basedir, replace(filename, ".jl" => ".md")))
20+
end
321

422
# https://github.com/jheinen/GR.jl/issues/278#issuecomment-587090846
523
ENV["GKSwstype"] = "nul"
@@ -12,12 +30,14 @@ pages = [
1230
"Algorithm Formulations" => [
1331
"ODE Solvers" => "algorithm_formulations/ode_solvers.md",
1432
"Newtons Method" => "algorithm_formulations/newtons_method.md",
33+
"Rosenbrock Method" => "algorithm_formulations/rosenbrock.md",
1534
"Old LSRK Formulations" => "algorithm_formulations/lsrk.md",
1635
"Old MRRK Formulations" => "algorithm_formulations/mrrk.md",
1736
],
1837
"Test problems" => [
1938
"test_problems/index.md",
2039
],
40+
"Tutorials" => generated_tutorials,
2141
"API docs" => [
2242
"ODE Solvers" => "api/ode_solvers.md",
2343
"Newtons Method" => "api/newtons_method.md",
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Rosenbrock methods
2+
3+
In this page, we introduce Rosenbrock-type methods to solve ordinary
4+
differential equations. In doing do, we roughly follow Chapter IV.7 of "Solving
5+
Ordinary Differential Equations II" by Hairer and Wanner. (Beware, notation is
6+
not the same.). In this spirit, let us introduce the need for Rosenbrock-type
7+
methods in the same way as Hairer and Wanner, by quoting Rosenbrock himself:
8+
9+
> When the functions are non-linear, implicit equations can in general be solved
10+
> only by iteration. This is a severe drawback, as it adds to the problem of
11+
> stability, that of convergence of the iterative process. An alternative, which
12+
> avoids this difficulty, is ...
13+
14+
Rosenbrock method!
15+
16+
Before reading this page, we recommend reading the page on ODE solvers first
17+
[TODO: Add link]
18+
19+
## Introduction to the formalism
20+
21+
Let us consider an ordinary differential equation of the form
22+
23+
```math
24+
\frac{d}{dt}u(t) = T(u(t), t)\,,
25+
```
26+
27+
where $u$ is the state, $T$ is the tendency,
28+
and $t$ the time. For the sake of simplicity, let us ignore the explicit time
29+
dependency in the tendency (we will get back to that at the end).
30+
31+
The simplest way to introduce the Rosenbrock method is to start from a
32+
diagonally implicit Runge-Kutta scheme (see page on DIRK). In an implicit
33+
Runge-Kutta method with $s$ stages and tableau $a, b, c$, the updated value
34+
$u_1$ for a step of size $\Delta t$ is obtained starting from the known value $u_0$
35+
with
36+
37+
```math
38+
u_1 = u_0 + \sum_{i=1}^s b_i k_i\,,
39+
```
40+
with
41+
```math
42+
k_i = \Delta t T ( u_0 + \sum_{j=1}^{i-1}\alpha_{ij}k_j + \alpha_{ii} k_i)\,.
43+
```
44+
$\alpha_{ij}$, $b_i$ are carefully chosen coefficients.
45+
46+
Rosenbrock's idea consists in linearizing $T$. This operation can be interpreted
47+
as taking one iteration for Netwon solver and yields
48+
49+
```math
50+
k_i = \Delta t T(g_i) + \Delta t T'(g_i) \alpha_{ii} k_i
51+
```
52+
53+
with $J(g_i)$ Jacobian of the tendency and with
54+
55+
```math
56+
g_i = u_0 + \sum_{j=1}^{i-1}\alpha_{ij} k_j
57+
```
58+
59+
In Rosenbrock-type methods, the Jacobian $T'$ is replaced with linear combinations
60+
of the Jacobian evaluated at $u_0$, $J$:
61+
```math
62+
T'(g_i) \alpha_{ii} k_i \approx J \sum_{j=1}^{i-1}\gamma_{ij} k_j + J \gamma_{ii} k_i\,,
63+
```
64+
with $\gamma_{ij}$ other coefficients that can be chosen.
65+
66+
Now, each stage consists of solving a system of linear equations in $k_i$ with
67+
matrix $I - \Delta t \gamma_{ii}$:
68+
```math
69+
(I - J \Delta t \gamma_{ii}) k_i = \Delta
70+
t T(g_i) + J \sum_{j=1}^{i-1}\gamma_{ij} k_j
71+
```
72+
for each $i$ in $1, \dots, s$. Once $k_i$ are known, $u_1$ can is easily computed and the process repeated for
73+
the next step.
74+
75+
In practice, there are computational advantages at implementing a slight
76+
variation of what presented here.
77+
78+
Let us define $\tilde{k}_{i} = \sum_{j=1}^{i} \gamma_{ij} k_j$. If the matrix
79+
$\gamma_{ij}$ is invertible, we can move freely from $k_i$ to $\tilde{k}_i$. A
80+
convenient step is to also define $C$, as
81+
82+
```math
83+
C = diag(\gamma_{11}^{-1}, \gamma_{22}^{-1}, \dots, \gamma_{ss}^{-1}) - \Gamma^{-1}
84+
```
85+
86+
Which establishes that
87+
88+
```math
89+
k_i = \frac{\tilde{k}_i}{\gamma_{ii}} - \sum_{j=1}^{i -1} c_{ij} \tilde{k}_j
90+
```
91+
Substituting this, we obtain the following equations
92+
93+
```math
94+
(J \Delta t \gamma_{ii} - 1) \tilde{k}_i = - \Delta
95+
t \gamma_{ii} T(g_i) - \gamma_{ii} J \sum_{j=1}^{i-1}c_{ij} \tilde{k}_j \,,
96+
```
97+
98+
```math
99+
g_i = u_0 + \sum_{j=1}^{i-1}a_{ij} \tilde{k}_j \,,
100+
```
101+
102+
```math
103+
u_1 = u_0 + \sum_{j=1}^{s} m_j \tilde{k}_j\,,
104+
```
105+
with
106+
```math
107+
(a_{ij}) = (\alpha_{ij}) \Gamma^{-1}\,,
108+
```
109+
and
110+
```math
111+
(m_i) = (b_i) \Gamma^{-1}
112+
```
113+
114+
Finally, small changes are required to add support for explicit time derivative:
115+
116+
```math
117+
(J \Delta t \gamma_{ii} - 1) \tilde{k}_i = - \Delta
118+
t \gamma_{ii} T( t_0 + \alpha_i \Delta t, g_i) - \gamma_{ii} J \sum_{j=1}^{i-1}c_{ij} \tilde{k}_j - \Delta
119+
t \gamma_{ii} \gamma_i \Delta
120+
t \frac{\partial T}{\partial t}(t_0, u_0)
121+
```
122+
123+
where we defined
124+
125+
$\alpha_{i} = \sum_{j=1}^{i-1}\alpha_{ij} $
126+
127+
$ \gamma _{i} = \sum_{j=1}^{i}\gamma_{ij} $
128+
129+
> :note: You may wonder, what is up with all these ugly minus signs? Why don't
130+
> we cancel them out? The reason for this is because we want to have the
131+
> prefactor $(J \Delta t \gamma_{ii} - 1)$. This is called `Wfact` in the
132+
> language of `DifferentialEquations.jl`. Most other algorithms specify this
133+
> quantity, so it is convenient to be consistent.
134+
135+
## Implementation
136+
137+
In `ClimaTimeSteppers.jl`, we implement the last equation presented in the
138+
previous section. Currently, the only Rosenbrock-type algorithm implemented is
139+
`SSPKnoth`. `SSPKnoth` is 3-stage, second-order accurate Rosenbrock with
140+
141+
```math
142+
\alpha = \begin{bmatrix}
143+
0 & 0 & 0 \\
144+
1 & 0 & 0 \\
145+
\frac{1}{4} & \frac{1}{4} & 0 \\
146+
\end{bmatrix}
147+
```
148+
149+
```math
150+
\Gamma = \begin{bmatrix}
151+
1 & 0 & 0 \\
152+
1 & 1 & 0 \\
153+
-\frac{3}{4} & \frac{3}{4} & 1 \\
154+
\end{bmatrix}
155+
```
156+
157+
and
158+
```math
159+
b = (\frac{1}{6}, \frac{1}{6}, \frac{2}{3})
160+
```
161+
162+
At each stage, the state is updated to $g_i$ and precomputed quantities are updated. Tendencies are computed and, unless the stage is the last, DSS is applied to the sum of all the tendencies. After the last stage, DSS is applied to the incremented state.

docs/src/api/ode_solvers.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ HOMMEM1
5151
ARK2GKC
5252
ARK437L2SA1
5353
ARK548L2SA2
54+
SSPKnoth
5455
```
5556

5657
## Explicit Algorithm Names

docs/src/dev/report_gen.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ let # Convergence
1717
title = "All Algorithms"
1818
algorithm_names = map(T -> T(), all_subtypes(ClimaTimeSteppers.AbstractAlgorithmName))
1919
algorithm_names = filter(name -> !(name isa ARK437L2SA1 || name isa ARK548L2SA2), algorithm_names) # too high order
20+
21+
# NOTE: Some imperfections in the convergence order for SSPKnoth are to be
22+
# expected because we are not using the exact Jacobian
23+
2024
verify_convergence(title, algorithm_names, ark_analytic_nonlin_test_cts(Float64), 200)
2125
verify_convergence(title, algorithm_names, ark_analytic_sys_test_cts(Float64), 400)
2226
verify_convergence(title, algorithm_names, ark_analytic_test_cts(Float64), 40000; super_convergence = (ARS121(),))
@@ -29,6 +33,8 @@ let # Convergence
2933
num_steps_scaling_factor = 4,
3034
numerical_reference_algorithm_name = ARS343(),
3135
)
36+
rosenbrock_schems = filter(name -> name isa ClimaTimeSteppers.RosenbrockAlgorithmName, algorithm_names)
37+
verify_convergence(title, rosenbrock_schems, climacore_1Dheat_test_implicit_cts(Float64), 400)
3238
verify_convergence(
3339
title,
3440
algorithm_names,

docs/src/plotting_utils.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ imex_convergence_orders(::ARK548L2SA2) = (5, 5, 5)
5959
imex_convergence_orders(::SSP22Heuns) = (2, 2, 2)
6060
imex_convergence_orders(::SSP33ShuOsher) = (3, 3, 3)
6161
imex_convergence_orders(::RK4) = (4, 4, 4)
62+
# SSPKnoth is not really an IMEX method
63+
imex_convergence_orders(::SSPKnoth) = (2, 2, 2)
6264

6365
# Compute a confidence interval for the convergence order, returning the
6466
# estimated convergence order and its uncertainty.
@@ -119,6 +121,8 @@ function verify_convergence(
119121
default_dt = t_end / num_steps
120122

121123
algorithm(algorithm_name::ClimaTimeSteppers.ERKAlgorithmName) = ExplicitAlgorithm(algorithm_name)
124+
algorithm(algorithm_name::ClimaTimeSteppers.SSPKnoth) =
125+
ClimaTimeSteppers.RosenbrockAlgorithm(ClimaTimeSteppers.tableau(ClimaTimeSteppers.SSPKnoth()))
122126
algorithm(algorithm_name::ClimaTimeSteppers.IMEXARKAlgorithmName) =
123127
IMEXAlgorithm(algorithm_name, NewtonsMethod(; max_iters = linear_implicit ? 1 : 2))
124128

0 commit comments

Comments
 (0)