differential_equations/tableau/dirk.rs
1//! Diagonally Implicit Runge-Kutta (DIRK) tableau
2
3use crate::{tableau::ButcherTableau, traits::Real};
4
5impl<T: Real> ButcherTableau<T, 2> {
6 /// SDIRK-2-1: 2-stage, 2nd order SDIRK method with 1st order embedding
7 ///
8 /// # Overview
9 /// This provides a 2-stage, singly diagonally implicit Runge-Kutta method with:
10 /// - Primary order: 2
11 /// - Embedded order: 1 (for error estimation)
12 /// - Number of stages: 2
13 /// - A-stable and B-stable
14 ///
15 /// # Notes
16 /// - This is a simple SDIRK method where all diagonal entries are equal (γ = 1)
17 /// - Good for basic adaptive stepping with stiff problems
18 /// - The embedded method provides basic error estimation for step size control
19 /// - Particularly useful as a starting method for more complex stiff systems
20 ///
21 /// # Butcher Tableau
22 /// ```text
23 /// 1 | 1 0
24 /// 0 | -1 1
25 /// -----|--------
26 /// | 1/2 1/2 (2nd order)
27 /// | 1 0 (1st order embedding)
28 /// ```
29 ///
30 /// # References
31 /// - Hairer, E., Wanner, G. (1996). "Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems"
32 ///
33 pub fn sdirk21() -> Self {
34 let c = [1.0, 0.0];
35 let a = [[1.0, 0.0], [-1.0, 1.0]];
36 let b = [0.5, 0.5];
37 let bh = [1.0, 0.0]; // 1st order embedding
38
39 let c = c.map(|x| T::from_f64(x).unwrap());
40 let a = a.map(|row| row.map(|x| T::from_f64(x).unwrap()));
41 let b = b.map(|x| T::from_f64(x).unwrap());
42 let bh = Some(bh.map(|x| T::from_f64(x).unwrap()));
43
44 ButcherTableau {
45 c,
46 a,
47 b,
48 bh,
49 bi: None,
50 er: None,
51 }
52 }
53}
54
55impl<T: Real> ButcherTableau<T, 3> {
56 /// ESDIRK-3-3: 3-stage, 3rd order ESDIRK method
57 ///
58 /// # Overview
59 /// This provides a 3-stage, explicit singly diagonally implicit Runge-Kutta method with:
60 /// - Primary order: 3
61 /// - Number of stages: 3
62 /// - A-stable
63 ///
64 /// # Notes
65 /// - This method pairs with SSPRK(3,3)-Shu-Osher-ERK to make a 3rd order IMEX method
66 /// - Has an explicit first stage (ESDIRK property) making it computationally efficient
67 /// - The first stage being explicit reduces the computational cost per step
68 /// - Suitable for problems with both stiff and non-stiff components
69 ///
70 /// # Butcher Tableau
71 /// ```text
72 /// 0 | 0 0 0
73 /// 1 | 4γ+2β 1-4γ-2β 0
74 /// 1/2 | α₃₁ γ β
75 /// -------|------------------
76 /// | 1/6 1/6 2/3
77 /// ```
78 /// where:
79 /// - β = √3/6 + 1/2 ≈ 0.7886751346
80 /// - γ = (-1/8)(√3 + 1) ≈ -0.3416407865
81 /// - α₃₁ = 1/2 - β - γ ≈ 0.0529656519
82 ///
83 /// # References
84 /// - Conde, S., et al. (2017). "Implicit and implicit-explicit strong stability preserving Runge-Kutta methods"
85 ///
86 pub fn esdirk33() -> Self {
87 // Parameters (computed in f64 for precision)
88 let sqrt3 = 3.0_f64.sqrt();
89 let beta = sqrt3 / 6.0 + 0.5;
90 let gamma = (-1.0 / 8.0) * (sqrt3 + 1.0);
91
92 let c = [0.0, 1.0, 0.5];
93 let a = [
94 [0.0, 0.0, 0.0],
95 [
96 4.0 * gamma + 2.0 * beta,
97 1.0 - 4.0 * gamma - 2.0 * beta,
98 0.0,
99 ],
100 [0.5 - beta - gamma, gamma, beta],
101 ];
102 let b = [1.0 / 6.0, 1.0 / 6.0, 2.0 / 3.0];
103
104 let c = c.map(|x| T::from_f64(x).unwrap());
105 let a = a.map(|row| row.map(|x| T::from_f64(x).unwrap()));
106 let b = b.map(|x| T::from_f64(x).unwrap());
107
108 ButcherTableau {
109 c,
110 a,
111 b,
112 bh: None,
113 bi: None,
114 er: None,
115 }
116 }
117}
118
119impl<T: Real> ButcherTableau<T, 4> {
120 /// ESDIRK3(2)4L[2]SA: 4-stage, 3rd order ESDIRK method with embedded 2nd order
121 ///
122 /// # Overview
123 /// This provides a 4-stage, explicit singly diagonally implicit Runge-Kutta method with:
124 /// - Primary order: 3
125 /// - Embedded order: 2 (for error estimation)
126 /// - Number of stages: 4
127 /// - A-stable and B-stable
128 ///
129 /// # References
130 /// - Kennedy, C.A. and Carpenter, M.H. (2003). "Additive Runge-Kutta schemes for convection-diffusion-reaction equations"
131 ///
132 pub fn esdirk324l2sa() -> Self {
133 // Gamma parameter and derived values (computed in f64 for precision)
134 let g = 0.435_866_521_508_459;
135 let g2 = g * g;
136 let g3 = g2 * g;
137 let g4 = g3 * g;
138 let g5 = g4 * g;
139
140 let c3 = 3.0 / 5.0;
141
142 // Compute coefficients in f64
143 let a32 = c3 * (c3 - 2.0 * g) / (4.0 * g);
144 let a31 = c3 - g - a32;
145
146 let b2 = (-2.0 + 3.0 * c3 + 6.0 * g * (1.0 - c3)) / (12.0 * g * (c3 - 2.0 * g));
147 let b3 = (1.0 - 6.0 * g + 6.0 * g2) / (3.0 * c3 * (c3 - 2.0 * g));
148 let b1 = 1.0 - g - b2 - b3;
149
150 // Embedding coefficients
151 let d2_term1 = c3 * (-1.0 + 6.0 * g - 24.0 * g3 + 12.0 * g4 - 6.0 * g5)
152 / (4.0 * g * (2.0 * g - c3) * (1.0 - 6.0 * g + 6.0 * g2));
153 let d2_term2 = (3.0 - 27.0 * g + 68.0 * g2 - 55.0 * g3 + 21.0 * g4 - 6.0 * g5)
154 / (2.0 * (2.0 * g - c3) * (1.0 - 6.0 * g + 6.0 * g2));
155 let d2 = d2_term1 + d2_term2;
156
157 let d3 = -g * (-2.0 + 21.0 * g - 68.0 * g2 + 79.0 * g3 - 33.0 * g4 + 12.0 * g5)
158 / (c3 * (c3 - 2.0 * g) * (1.0 - 6.0 * g + 6.0 * g2));
159
160 let d4 = -3.0 * g2 * (-1.0 + 4.0 * g - 2.0 * g2 + g3) / (1.0 - 6.0 * g + 6.0 * g2);
161 let d1 = 1.0 - d2 - d3 - d4;
162
163 let c = [0.0, 2.0 * g, 3.0 / 5.0, 1.0];
164 let a = [
165 [0.0, 0.0, 0.0, 0.0],
166 [g, g, 0.0, 0.0],
167 [a31, a32, g, 0.0],
168 [b1, b2, b3, g],
169 ];
170 let b = [b1, b2, b3, g];
171 let bh = [d1, d2, d3, d4]; // 2nd order embedding
172
173 let c = c.map(|x| T::from_f64(x).unwrap());
174 let a = a.map(|row| row.map(|x| T::from_f64(x).unwrap()));
175 let b = b.map(|x| T::from_f64(x).unwrap());
176 let bh = Some(bh.map(|x| T::from_f64(x).unwrap()));
177
178 ButcherTableau {
179 c,
180 a,
181 b,
182 bh,
183 bi: None,
184 er: None,
185 }
186 }
187}