Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revive SSPKnoth #292

Merged
merged 1 commit into from
Jun 12, 2024
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
20 changes: 13 additions & 7 deletions docs/Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

julia_version = "1.10.3"
manifest_format = "2.0"
project_hash = "6c87fe520a2f2573c1d64ad2168076eae19c6786"
project_hash = "bd8d0393b0badb814c7eec6dee2d48c4c931c7ec"

[[deps.ADTypes]]
git-tree-sha1 = "fc02d55798c1af91123d07915a990fbb9a10d146"
Expand Down Expand Up @@ -112,9 +112,9 @@ version = "7.11.0"

[[deps.ArrayLayouts]]
deps = ["FillArrays", "LinearAlgebra"]
git-tree-sha1 = "29649b61e0313db0a7ad5ecf41210e4e85aea234"
git-tree-sha1 = "420e2853770f50e5306b9d96b5a66f26e7c73bc6"
uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
version = "1.9.3"
version = "1.9.4"
weakdeps = ["SparseArrays"]

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

[[deps.Literate]]
deps = ["Base64", "IOCapture", "JSON", "REPL"]
git-tree-sha1 = "596df2daea9c27da81eee63ef2cf101baf10c24c"
uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
version = "2.18.0"

[[deps.LogExpFunctions]]
deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea"
Expand Down Expand Up @@ -1648,9 +1654,9 @@ version = "0.6.12"

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

[deps.RecursiveArrayTools.extensions]
RecursiveArrayToolsFastBroadcastExt = "FastBroadcast"
Expand Down Expand Up @@ -1740,9 +1746,9 @@ version = "0.6.42"

[[deps.SciMLBase]]
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"]
git-tree-sha1 = "1d1d1ff37d2917cad263fa186cbc19ce4b587ccf"
git-tree-sha1 = "c15e03738d4170f92ba477273ef0528341f79a9a"
uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
version = "2.40.0"
version = "2.41.0"

[deps.SciMLBase.extensions]
SciMLBaseChainRulesCoreExt = "ChainRulesCore"
Expand Down
5 changes: 5 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
Krylov = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7"
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
NVTX = "5da4648a-3479-48b8-97b9-01cb529c0a1f"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
ClimaCore = "0.14.8"
20 changes: 20 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
using Documenter, DocumenterCitations
using ClimaTimeSteppers
using Literate

tutorial_basedir = "tutorials"
tutorial_basedir_from_here = joinpath(@__DIR__, "src", tutorial_basedir)

jl_files_in_basedir = filter(endswith(".jl"), readdir(tutorial_basedir_from_here))

println("Building literate tutorials...")
generated_tutorials = String[]
for filename in jl_files_in_basedir
Literate.markdown(
joinpath(tutorial_basedir_from_here, filename),
tutorial_basedir_from_here;
execute = true,
flavor = Literate.CommonMarkFlavor(),
)
push!(generated_tutorials, joinpath(tutorial_basedir, replace(filename, ".jl" => ".md")))
end

# https://github.com/jheinen/GR.jl/issues/278#issuecomment-587090846
ENV["GKSwstype"] = "nul"
Expand All @@ -12,12 +30,14 @@ pages = [
"Algorithm Formulations" => [
"ODE Solvers" => "algorithm_formulations/ode_solvers.md",
"Newtons Method" => "algorithm_formulations/newtons_method.md",
"Rosenbrock Method" => "algorithm_formulations/rosenbrock.md",
"Old LSRK Formulations" => "algorithm_formulations/lsrk.md",
"Old MRRK Formulations" => "algorithm_formulations/mrrk.md",
],
"Test problems" => [
"test_problems/index.md",
],
"Tutorials" => generated_tutorials,
"API docs" => [
"ODE Solvers" => "api/ode_solvers.md",
"Newtons Method" => "api/newtons_method.md",
Expand Down
162 changes: 162 additions & 0 deletions docs/src/algorithm_formulations/rosenbrock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Rosenbrock methods

In this page, we introduce Rosenbrock-type methods to solve ordinary
differential equations. In doing do, we roughly follow Chapter IV.7 of "Solving
Ordinary Differential Equations II" by Hairer and Wanner. (Beware, notation is
not the same.). In this spirit, let us introduce the need for Rosenbrock-type
methods in the same way as Hairer and Wanner, by quoting Rosenbrock himself:

> When the functions are non-linear, implicit equations can in general be solved
> only by iteration. This is a severe drawback, as it adds to the problem of
> stability, that of convergence of the iterative process. An alternative, which
> avoids this difficulty, is ...

Rosenbrock method!

Before reading this page, we recommend reading the page on ODE solvers first
[TODO: Add link]

## Introduction to the formalism

Let us consider an ordinary differential equation of the form

```math
\frac{d}{dt}u(t) = T(u(t), t)\,,
```

where $u$ is the state, $T$ is the tendency,
and $t$ the time. For the sake of simplicity, let us ignore the explicit time
dependency in the tendency (we will get back to that at the end).

The simplest way to introduce the Rosenbrock method is to start from a
diagonally implicit Runge-Kutta scheme (see page on DIRK). In an implicit
Runge-Kutta method with $s$ stages and tableau $a, b, c$, the updated value
$u_1$ for a step of size $\Delta t$ is obtained starting from the known value $u_0$
with

```math
u_1 = u_0 + \sum_{i=1}^s b_i k_i\,,
```
with
```math
k_i = \Delta t T ( u_0 + \sum_{j=1}^{i-1}\alpha_{ij}k_j + \alpha_{ii} k_i)\,.
```
$\alpha_{ij}$, $b_i$ are carefully chosen coefficients.

Rosenbrock's idea consists in linearizing $T$. This operation can be interpreted
as taking one iteration for Netwon solver and yields

```math
k_i = \Delta t T(g_i) + \Delta t T'(g_i) \alpha_{ii} k_i
```

with $J(g_i)$ Jacobian of the tendency and with

```math
g_i = u_0 + \sum_{j=1}^{i-1}\alpha_{ij} k_j
```

In Rosenbrock-type methods, the Jacobian $T'$ is replaced with linear combinations
of the Jacobian evaluated at $u_0$, $J$:
```math
T'(g_i) \alpha_{ii} k_i \approx J \sum_{j=1}^{i-1}\gamma_{ij} k_j + J \gamma_{ii} k_i\,,
```
with $\gamma_{ij}$ other coefficients that can be chosen.

Now, each stage consists of solving a system of linear equations in $k_i$ with
matrix $I - \Delta t \gamma_{ii}$:
```math
(I - J \Delta t \gamma_{ii}) k_i = \Delta
t T(g_i) + J \sum_{j=1}^{i-1}\gamma_{ij} k_j
```
for each $i$ in $1, \dots, s$. Once $k_i$ are known, $u_1$ can is easily computed and the process repeated for
the next step.

In practice, there are computational advantages at implementing a slight
variation of what presented here.

Let us define $\tilde{k}_{i} = \sum_{j=1}^{i} \gamma_{ij} k_j$. If the matrix
$\gamma_{ij}$ is invertible, we can move freely from $k_i$ to $\tilde{k}_i$. A
convenient step is to also define $C$, as

```math
C = diag(\gamma_{11}^{-1}, \gamma_{22}^{-1}, \dots, \gamma_{ss}^{-1}) - \Gamma^{-1}
```

Which establishes that

```math
k_i = \frac{\tilde{k}_i}{\gamma_{ii}} - \sum_{j=1}^{i -1} c_{ij} \tilde{k}_j
```
Substituting this, we obtain the following equations

```math
(J \Delta t \gamma_{ii} - 1) \tilde{k}_i = - \Delta
t \gamma_{ii} T(g_i) - \gamma_{ii} J \sum_{j=1}^{i-1}c_{ij} \tilde{k}_j \,,
```

```math
g_i = u_0 + \sum_{j=1}^{i-1}a_{ij} \tilde{k}_j \,,
```

```math
u_1 = u_0 + \sum_{j=1}^{s} m_j \tilde{k}_j\,,
```
with
```math
(a_{ij}) = (\alpha_{ij}) \Gamma^{-1}\,,
```
and
```math
(m_i) = (b_i) \Gamma^{-1}
```

Finally, small changes are required to add support for explicit time derivative:

```math
(J \Delta t \gamma_{ii} - 1) \tilde{k}_i = - \Delta
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
t \gamma_{ii} \gamma_i \Delta
t \frac{\partial T}{\partial t}(t_0, u_0)
```

where we defined

$\alpha_{i} = \sum_{j=1}^{i-1}\alpha_{ij} $

$ \gamma _{i} = \sum_{j=1}^{i}\gamma_{ij} $

> :note: You may wonder, what is up with all these ugly minus signs? Why don't
> we cancel them out? The reason for this is because we want to have the
> prefactor $(J \Delta t \gamma_{ii} - 1)$. This is called `Wfact` in the
> language of `DifferentialEquations.jl`. Most other algorithms specify this
> quantity, so it is convenient to be consistent.

## Implementation

In `ClimaTimeSteppers.jl`, we implement the last equation presented in the
previous section. Currently, the only Rosenbrock-type algorithm implemented is
`SSPKnoth`. `SSPKnoth` is 3-stage, second-order accurate Rosenbrock with

```math
\alpha = \begin{bmatrix}
0 & 0 & 0 \\
1 & 0 & 0 \\
\frac{1}{4} & \frac{1}{4} & 0 \\
\end{bmatrix}
```

```math
\Gamma = \begin{bmatrix}
1 & 0 & 0 \\
1 & 1 & 0 \\
-\frac{3}{4} & \frac{3}{4} & 1 \\
\end{bmatrix}
```

and
```math
b = (\frac{1}{6}, \frac{1}{6}, \frac{2}{3})
```

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.
1 change: 1 addition & 0 deletions docs/src/api/ode_solvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ HOMMEM1
ARK2GKC
ARK437L2SA1
ARK548L2SA2
SSPKnoth
```

## Explicit Algorithm Names
Expand Down
6 changes: 6 additions & 0 deletions docs/src/dev/report_gen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ let # Convergence
title = "All Algorithms"
algorithm_names = map(T -> T(), all_subtypes(ClimaTimeSteppers.AbstractAlgorithmName))
algorithm_names = filter(name -> !(name isa ARK437L2SA1 || name isa ARK548L2SA2), algorithm_names) # too high order

# NOTE: Some imperfections in the convergence order for SSPKnoth are to be
# expected because we are not using the exact Jacobian

verify_convergence(title, algorithm_names, ark_analytic_nonlin_test_cts(Float64), 200)
verify_convergence(title, algorithm_names, ark_analytic_sys_test_cts(Float64), 400)
verify_convergence(title, algorithm_names, ark_analytic_test_cts(Float64), 40000; super_convergence = (ARS121(),))
Expand All @@ -29,6 +33,8 @@ let # Convergence
num_steps_scaling_factor = 4,
numerical_reference_algorithm_name = ARS343(),
)
rosenbrock_schems = filter(name -> name isa ClimaTimeSteppers.RosenbrockAlgorithmName, algorithm_names)
verify_convergence(title, rosenbrock_schems, climacore_1Dheat_test_implicit_cts(Float64), 400)
verify_convergence(
title,
algorithm_names,
Expand Down
4 changes: 4 additions & 0 deletions docs/src/plotting_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ imex_convergence_orders(::ARK548L2SA2) = (5, 5, 5)
imex_convergence_orders(::SSP22Heuns) = (2, 2, 2)
imex_convergence_orders(::SSP33ShuOsher) = (3, 3, 3)
imex_convergence_orders(::RK4) = (4, 4, 4)
# SSPKnoth is not really an IMEX method
imex_convergence_orders(::SSPKnoth) = (2, 2, 2)

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

algorithm(algorithm_name::ClimaTimeSteppers.ERKAlgorithmName) = ExplicitAlgorithm(algorithm_name)
algorithm(algorithm_name::ClimaTimeSteppers.SSPKnoth) =
ClimaTimeSteppers.RosenbrockAlgorithm(ClimaTimeSteppers.tableau(ClimaTimeSteppers.SSPKnoth()))
algorithm(algorithm_name::ClimaTimeSteppers.IMEXARKAlgorithmName) =
IMEXAlgorithm(algorithm_name, NewtonsMethod(; max_iters = linear_implicit ? 1 : 2))

Expand Down
Loading
Loading