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}