Skip to content

Commit dcaaaa3

Browse files
authored
Merge branch 'main' into paper-2025-joss
2 parents c17b4fd + ce5b07d commit dcaaaa3

30 files changed

Lines changed: 2992 additions & 1219 deletions

.github/workflows/CompatHelper.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
- name: "Run CompatHelper"
4141
run: |
4242
import CompatHelper
43-
CompatHelper.main(; subdirs=["", "docs", "examples", "test"])
43+
CompatHelper.main(; subdirs=["", "docs", "test"])
4444
shell: julia --color=yes {0}
4545
env:
4646
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/Downgrade.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ jobs:
5151
env:
5252
PYTHON: ""
5353
GKSwstype: "100" # for Plots/GR
54+
POSITIVEINTEGRATORS_DOWNGRADE_CI: "true"

.github/workflows/SpellCheck.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ jobs:
1010
- name: Checkout Actions Repository
1111
uses: actions/checkout@v4
1212
- name: Check spelling
13-
uses: crate-ci/typos@v1.29.5
13+
uses: crate-ci/typos@v1.31.1

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ for human readability.
1212

1313
- The minimum required Julia version was updated to v1.10
1414

15+
### Added
16+
17+
- MPDeC methods of Öffner and Torlo
1518

1619
## Changes when updating to v0.2 from v0.1.x
1720

Project.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,35 @@
11
name = "PositiveIntegrators"
22
uuid = "d1b20bf0-b083-4985-a874-dc5121669aa5"
33
authors = ["Stefan Kopecz, Hendrik Ranocha, and contributors"]
4-
version = "0.2.7"
4+
version = "0.2.11-DEV"
55

66
[deps]
77
FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898"
88
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
99
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
1010
MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221"
1111
OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8"
12+
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
1213
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1314
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1415
SimpleUnPack = "ce78b400-467f-4804-87d8-8f486da07d0a"
1516
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1617
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
18+
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1719
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
1820

1921
[compat]
2022
FastBroadcast = "0.3.5"
2123
LinearAlgebra = "1"
22-
LinearSolve = "2.32"
24+
LinearSolve = "3.7.1"
2325
MuladdMacro = "0.2.4"
2426
OrdinaryDiffEqCore = "1.16"
27+
RecipesBase = "1.3.4"
2528
Reexport = "1.2.2"
26-
SciMLBase = "2.68"
29+
SciMLBase = "2.78"
2730
SimpleUnPack = "1"
2831
SparseArrays = "1"
2932
StaticArrays = "1.9.7"
33+
Statistics = "1"
3034
SymbolicIndexingInterface = "0.3.36"
3135
julia = "1.10"

docs/Project.toml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,40 @@
22
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
33
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
44
DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def"
5+
DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d"
56
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
67
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
78
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
89
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
10+
OrdinaryDiffEqFIRK = "5960d6e9-dd7a-4743-88e7-cf307b64f125"
11+
OrdinaryDiffEqLowOrderRK = "1344f307-1e59-4825-a18e-ace9aa3fa4c6"
912
OrdinaryDiffEqRosenbrock = "43230ef6-c299-4910-a778-202eb28ce4ce"
13+
OrdinaryDiffEqSDIRK = "2d112036-d095-4a1e-ab9a-08536f3ecdbf"
14+
OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a"
15+
OrdinaryDiffEqVerner = "79d7bb75-1356-48c1-b8c0-6832512096c2"
1016
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
1117
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
18+
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
1219
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1320
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1421

1522
[compat]
1623
BenchmarkTools = "1"
1724
DiffEqBase = "6.160"
1825
DiffEqCallbacks = "4"
26+
DiffEqDevTools = "2.45.1"
1927
Documenter = "1"
2028
InteractiveUtils = "1"
2129
LinearAlgebra = "1"
22-
LinearSolve = "2.32"
30+
LinearSolve = "3.7.1"
31+
OrdinaryDiffEqFIRK = "1.7"
32+
OrdinaryDiffEqLowOrderRK = "1.2"
2333
OrdinaryDiffEqRosenbrock = "1.4"
34+
OrdinaryDiffEqSDIRK = "1.2"
35+
OrdinaryDiffEqTsit5 = "1.1"
36+
OrdinaryDiffEqVerner = "1.1"
2437
Pkg = "1"
2538
Plots = "1"
39+
PrettyTables = "2.4.0"
2640
SparseArrays = "1"
2741
StaticArrays = "1.9.7"

docs/make.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,14 @@ makedocs(modules = [PositiveIntegrators],
7979
"Stratospheric reaction problem" => "stratospheric_reaction.md",
8080
"Linear Advection" => "linear_advection.md",
8181
"Heat Equation, Neumann BCs" => "heat_equation_neumann.md",
82-
"Heat Equation, Dirichlet BCs" => "heat_equation_dirichlet.md"
82+
"Heat Equation, Dirichlet BCs" => "heat_equation_dirichlet.md",
83+
"Scalar equation" => "scalar_pds.md"
84+
],
85+
"Benchmarks" => [
86+
"Experimental order of convergence" => "convergence.md",
87+
"NPZD model" => "npzd_model_benchmark.md",
88+
"Robertson problem" => "robertson_benchmark.md",
89+
"Stratospheric reaction problem" => "stratospheric_reaction_benchmark.md"
8390
],
8491
"Troubleshooting, FAQ" => "faq.md",
8592
"API reference" => "api_reference.md",

docs/src/api_reference.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,20 @@ SSPMPRK22
3535
MPRK43I
3636
MPRK43II
3737
SSPMPRK43
38+
MPDeC
3839
```
3940

4041
## Auxiliary functions
4142

4243
```@docs
4344
isnegative
4445
isnonnegative
46+
rel_max_error_tend
47+
rel_max_error_overall
48+
rel_l1_error_tend
49+
rel_l2_error_tend
50+
work_precision_adaptive
51+
work_precision_adaptive!
52+
work_precision_fixed
53+
work_precision_fixed!
4554
```

docs/src/convergence.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# [Experimental convergence order of MPRK schemes](@id convergence_mprk)
2+
3+
In this tutorial, we check that the implemented MPRK schemes have the expected order of convergence.
4+
5+
## Conservative production-destruction systems
6+
7+
First, we consider conservative production-destruction systems (PDS). To investigate the convergence order, we define the non-autonomous test problem
8+
9+
```math
10+
\begin{aligned}
11+
u_1' &= \cos(\pi t)^2 u_2 - \sin(2\pi t)^2 u_1, & u_1(0)&=0.9, \\
12+
u_2' & = \sin(2\pi t)^2 u_1 - \cos(\pi t)^2 u_2, & u_2(0)&=0.1,
13+
\end{aligned}
14+
```
15+
for ``0≤ t≤ 1``.
16+
The PDS is conservative since the sum of the right-hand side terms equals zero.
17+
An implementation of the problem is given next.
18+
19+
20+
```@example eoc
21+
using PositiveIntegrators
22+
23+
# define problem
24+
P(u, p, t) = [0.0 cos.(π * t) .^ 2 * u[2]; sin.(2 * π * t) .^ 2 * u[1] 0.0]
25+
prob = ConservativePDSProblem(P, [0.9; 0.1], (0.0, 1.0))
26+
27+
nothing # hide
28+
```
29+
30+
To use `analyticless_test_convergence` from [DiffEqDevTools.jl](https://github.com/SciML/DiffEqDevTools.jl), we need to pick a solver to compute the reference solution and specify tolerances.
31+
Since the problem is not stiff, we use the high-order explicit solver `Vern9()` from [OrdinaryDiffEqVerner.jl](https://docs.sciml.ai/OrdinaryDiffEq/stable/).
32+
Moreover, we choose time step sizes to investigate the convergence behavior.
33+
34+
```@example eoc
35+
using OrdinaryDiffEqVerner
36+
using DiffEqDevTools: analyticless_test_convergence
37+
38+
# solver and tolerances to compute reference solution
39+
test_setup = Dict(:alg => Vern9(), :reltol => 1e-14, :abstol => 1e-14)
40+
41+
# choose step sizes
42+
dts = 0.5 .^ (5:10)
43+
44+
nothing # hide
45+
```
46+
47+
### Second-order MPRK schemes
48+
49+
First, we test several second-order MPRK schemes.
50+
51+
```@example eoc
52+
# select schemes
53+
algs2 = [MPRK22(0.5); MPRK22(2.0 / 3.0); MPRK22(1.0); SSPMPRK22(0.5, 1.0)]
54+
labels2 = ["MPRK22(0.5)"; "MPRK22(2.0/3.0)"; "MPRK22(1.0)"; "SSPMPRK22(0.5, 1.0)"]
55+
56+
# compute errors and experimental order of convergence
57+
err_eoc = []
58+
for i in eachindex(algs2)
59+
sim = analyticless_test_convergence(dts, prob, algs2[i], test_setup)
60+
61+
err = sim.errors[:l∞]
62+
eoc = [NaN; -log2.(err[2:end] ./ err[1:(end - 1)])]
63+
64+
push!(err_eoc, tuple.(err, eoc))
65+
end
66+
```
67+
68+
Next, we print a table with the computed data.
69+
The table lists the errors obtained with the respective time step size ``Δ t`` as well as the estimated order of convergence in parentheses.
70+
71+
```@example eoc
72+
using Printf: @sprintf
73+
using PrettyTables: pretty_table
74+
75+
# gather data for table
76+
data = hcat(dts, reduce(hcat,err_eoc))
77+
78+
# print table
79+
formatter = (v, i, j) -> (j>1) ? (@sprintf "%5.2e (%4.2f) " v[1] v[2]) : (@sprintf "%5.2e " v)
80+
pretty_table(data, formatters = formatter, header = ["Δt"; labels2])
81+
```
82+
83+
The table shows that all schemes converge as expected.
84+
85+
### Third-order MPRK schemes
86+
87+
In this section, we proceed as above, but consider third-order MPRK schemes instead.
88+
89+
```@example eoc
90+
# select 3rd order schemes
91+
algs3 = [MPRK43I(1.0, 0.5); MPRK43I(0.5, 0.75); MPRK43II(0.5); MPRK43II(2.0 / 3.0);
92+
SSPMPRK43()]
93+
labels3 = ["MPRK43I(1.0,0.5)"; "MPRK43I(0.5, 0.75)"; "MPRK43II(0.5)"; "MPRK43II(2.0/3.0)";
94+
"SSPMPRK43()"]
95+
96+
# compute errors and experimental order of convergence
97+
err_eoc = []
98+
for i in eachindex(algs3)
99+
sim = analyticless_test_convergence(dts, prob, algs3[i], test_setup)
100+
101+
err = sim.errors[:l∞]
102+
eoc = [NaN; -log2.(err[2:end] ./ err[1:(end - 1)])]
103+
104+
push!(err_eoc, tuple.(err, eoc))
105+
end
106+
107+
# gather data for table
108+
data = hcat(dts, reduce(hcat,err_eoc))
109+
110+
# print table
111+
formatter = (v, i, j) -> (j>1) ? (@sprintf "%5.2e (%4.2f) " v[1] v[2]) : (@sprintf "%5.2e " v)
112+
pretty_table(data, formatters = formatter, header = ["Δt"; labels3])
113+
```
114+
115+
As above, the table shows that all schemes converge as expected.
116+
117+
## Non-conservative PDS
118+
119+
In this section we consider the non-autonomous but non-conservative test problem
120+
121+
```math
122+
\begin{aligned}
123+
u_1' &= \cos(\pi t)^2 u_2 - \sin(2\pi t)^2 u_1 - \cos(2\pi t)^2 u_1, & u_1(0)&=0.9,\\
124+
u_2' & = \sin(2\pi t)^2 u_1 - \cos(\pi t)^2 u_2 - \sin(\pi t)^2 u_2, & u_2(0)&=0.1,
125+
\end{aligned}
126+
```
127+
128+
for ``0≤ t≤ 1``.
129+
Since the sum of the right-hand side terms does not cancel, the PDS is indeed non-conservative.
130+
Hence, we need to use [`PDSProblem`](@ref) for its implementation.
131+
132+
```@example eoc
133+
# choose problem
134+
P(u, p, t) = [0.0 cos.(π * t) .^ 2 * u[2]; sin.(2 * π * t) .^ 2 * u[1] 0.0]
135+
D(u, p, t) = [cos.(2 * π * t) .^ 2 * u[1]; sin.(π * t) .^ 2 * u[2]]
136+
prob = PDSProblem(P, D, [0.9; 0.1], (0.0, 1.0))
137+
138+
nothing # hide
139+
```
140+
141+
The following sections will show that the selected MPRK schemes show the expected convergence order also for this non-conservative PDS.
142+
143+
### Second-order MPRK schemes
144+
145+
```@example eoc
146+
# compute errors and experimental order of convergence
147+
err_eoc = []
148+
for i in eachindex(algs2)
149+
sim = analyticless_test_convergence(dts, prob, algs2[i], test_setup)
150+
151+
err = sim.errors[:l∞]
152+
eoc = [NaN; -log2.(err[2:end] ./ err[1:(end - 1)])]
153+
154+
push!(err_eoc, tuple.(err, eoc))
155+
end
156+
157+
# gather data for table
158+
data = hcat(dts, reduce(hcat,err_eoc))
159+
160+
# print table
161+
formatter = (v, i, j) -> (j>1) ? (@sprintf "%5.2e (%4.2f) " v[1] v[2]) : (@sprintf "%5.2e " v)
162+
pretty_table(data, formatters = formatter, header = ["Δt"; labels2])
163+
```
164+
165+
### Third-order MPRK schemes
166+
167+
```@example eoc
168+
# compute errors and experimental order of convergence
169+
err_eoc = []
170+
for i in eachindex(algs3)
171+
sim = analyticless_test_convergence(dts, prob, algs3[i], test_setup)
172+
173+
err = sim.errors[:l∞]
174+
eoc = [NaN; -log2.(err[2:end] ./ err[1:(end - 1)])]
175+
176+
push!(err_eoc, tuple.(err, eoc))
177+
end
178+
179+
# gather data for table
180+
data = hcat(dts, reduce(hcat,err_eoc))
181+
182+
# print table
183+
formatter = (v, i, j) -> (j>1) ? (@sprintf "%5.2e (%4.2f) " v[1] v[2]) : (@sprintf "%5.2e " v)
184+
pretty_table(data, formatters = formatter, header = ["Δt"; labels3])
185+
```

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The application of MPRK schemes requires the ODE system to be represented as a p
3737
```math
3838
u_i'(t) = \sum_{j=1}^N \bigl(p_{ij}(t,\boldsymbol u) - d_{ij}(t,\boldsymbol u)\bigr),\quad i=1,\dots,N,
3939
```
40-
where ``\boldsymbol u=(u_1,\dots,u_n)^T`` is the vector of unknowns and both production terms ``p_{ij}(t,\boldsymbol u)`` and destruction terms ``d_{ij}(t,\boldsymbol u)`` must be nonnegative for all ``i,j=1,\dots,N``. The meaning behind ``p_{ij}`` and ``d_{ij}`` is as follows:
40+
where ``\boldsymbol u=(u_1,\dots,u_N)^T`` is the vector of unknowns and both production terms ``p_{ij}(t,\boldsymbol u)`` and destruction terms ``d_{ij}(t,\boldsymbol u)`` must be nonnegative for all ``i,j=1,\dots,N``. The meaning behind ``p_{ij}`` and ``d_{ij}`` is as follows:
4141
* ``p_{ij}`` with ``i\ne j`` represents the sum of all nonnegative terms which
4242
appear in equation ``i`` with a positive sign and in equation ``j`` with a negative sign.
4343
* ``d_{ij}`` with ``i\ne j`` represents the sum of all nonnegative terms which

0 commit comments

Comments
 (0)