Sandals  v0.0.0
A C++ library for ODEs/DAEs integration
Loading...
Searching...
No Matches
Tableau.hxx
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * Copyright (c) 2025, Davide Stocco and Enrico Bertolazzi. *
3 * *
4 * The Sandals project is distributed under the BSD 2-Clause License. *
5 * *
6 * Davide Stocco Enrico Bertolazzi *
7 * University of Trento University of Trento *
8 * e-mail: davide.stocco@unitn.it e-mail: enrico.bertolazzi@unitn.it *
9\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10
11#pragma once
12
13#ifndef SANDALS_TABLEAU_HXX
14#define SANDALS_TABLEAU_HXX
15
16namespace Sandals {
17
18 /*\
19 | _____ _ _
20 | |_ _|_ _| |__ | | ___ __ _ _ _
21 | | |/ _` | '_ \| |/ _ \/ _` | | | |
22 | | | (_| | |_) | | __/ (_| | |_| |
23 | |_|\__,_|_.__/|_|\___|\__,_|\__,_|
24 |
25 \*/
26
34 template <Integer S>
35 struct Tableau
36 {
37 using Type = enum class type : Integer {ERK=0, IRK=1, DIRK=2};
38 using Vector = Eigen::Vector<Real, S>;
39 using Matrix = Eigen::Matrix<Real, S, S>;
40
41 std::string name;
47 Vector b_e{Vector::Zero()};
49 bool is_embedded{false};
50
56 bool check(bool verbose = false) const {
57
58 #define CMD "Sandals::" << this->name << "::check(...): "
59
60 // Check the occupancy of the matrix A
61 if (type == Type::ERK && (!this->A.isLowerTriangular() || !this->A.diagonal().isZero())) {
62 SANDALS_ASSERT_WARNING(!verbose, CMD "A matrix occupancy not consistent with an ERK method.");
63 return false;
64 } else if (type == Type::DIRK && (!this->A.isLowerTriangular() || this->A.diagonal().isZero())) {
65 SANDALS_ASSERT_WARNING(!verbose, CMD "A matrix occupancy not consistent with a DIRK method.");
66 return false;
67 } else if (type == Type::IRK && this->A.isZero()) {
68 SANDALS_ASSERT_WARNING(!verbose, CMD "A matrix occupancy not consistent with an IRK method.");
69 return false;
70 }
71
72 // Check the order of the method
73 Integer computed_order{compute_order(this->A, this->b, this->c, verbose)};
74 if (this->order != computed_order) {
75 SANDALS_ASSERT_WARNING(!verbose, CMD "method order check failed, " << computed_order << " ≠ "
76 << this->order << ".");
77 return false;
78 }
79
80 // Check the embedded method consistency
81 if (!this->is_embedded && (this->order_e != -1 || !this->b_e.isZero())) {
82 SANDALS_ASSERT_WARNING(!verbose, CMD "embedded method inconsistency.");
83 return false;
84 } else {
85 return true;
86 }
87
88 // Check the embedded method order
89 Integer computed_order_e{compute_order(this->A, this->b_e, this->c, verbose)};
90 if (this->order_e != computed_order_e) {
91 SANDALS_ASSERT_WARNING(!verbose, CMD "embedded method order check failed, " << computed_order_e
92 << " ≠ " << this->order_e << ".");
93 return false;
94 } else {
95 return true;
96 }
97
98 #undef CMD
99 }
100
101 private:
102
113 Integer compute_order(Matrix const &A, Vector const &b, Vector const &c, bool verbose = false) const {
114
115 #define CMD "Sandals::" << this->name << "::tableau_order(...): "
116
117 // Temporary variables initialization
118 Integer order{0};
119 Real tolerance{std::pow(EPSILON, 2.0/3.0)};
120
121 // Precheck consistency
122 Real tmp{(A*Vector::Ones() - c).norm()};
123 if (tmp > tolerance) {
124 SANDALS_ASSERT_WARNING(!verbose, CMD "precheck failed, ||A*I - c|| = " << tmp << " ≠ 0.");
125 return order;
126 }
127
128 // Check order 1
129 tmp = b.sum();
130 if (std::abs(tmp - 1.0) > tolerance) {
131 SANDALS_ASSERT_WARNING(!verbose, CMD "order 1 check failed, a_1 = " << tmp << " ≠ 1.");
132 return order;
133 }
134
135 order = 1; // Order 1 is the highest order that can be checked
136
137 // Check order 2
138 tmp = b.transpose() * c;
139 if (std::abs(tmp - 1.0/2.0) > tolerance) {
140 SANDALS_ASSERT_WARNING(!verbose, CMD "order 2 check failed, a_1 = " << tmp << " ≠ 1/2.");
141 return order;
142 }
143
144 order = 2; // Order 2 is the highest order that can be checked
145
146 // Check order 3
147 Vector c_2{c.array().pow(2)};
148 tmp = b.transpose() * c_2;
149 if (std::abs(tmp - 1.0/3.0) > tolerance) {
150 SANDALS_ASSERT_WARNING(!verbose, CMD "order 3 check failed, a_1 = " << tmp << " ≠ 1/3.");
151 return order;
152 }
153
154 Vector Ac{A * c};
155 tmp = b.transpose() * Ac;
156 if (std::abs(tmp - 1.0/6.0) > tolerance) {
157 SANDALS_ASSERT_WARNING(!verbose, CMD "order 3 check failed, a_2 = " << tmp << " ≠ 1/6.");
158 return order;
159 }
160
161 order = 3; // Order 3 is the highest order that can be checked
162
163 // Check order 4
164 Vector c_3{c.array().pow(3)};
165 tmp = b.transpose() * c_3;
166 if (std::abs(tmp - 1.0/4.0) > tolerance) {
167 SANDALS_ASSERT_WARNING(!verbose, CMD "order 4 check failed, a_1 = " << tmp << " ≠ 1/4.");
168 return order;
169 }
170
171 Vector cAc{c.array() * Ac.array()};
172 tmp = b.transpose() * cAc;
173 if (std::abs(tmp - 1.0/8.0) > tolerance) {
174 SANDALS_ASSERT_WARNING(!verbose, CMD "order 4 check failed, a_2 = " << tmp << " ≠ 1/8.");
175 return order;
176 }
177
178 Vector bA{(b.transpose() * A).transpose()};
179 tmp = bA.transpose() * c_2;
180 if (std::abs(tmp - 1.0/12.0) > tolerance) {
181 SANDALS_ASSERT_WARNING(!verbose, CMD "order 4 check failed, a_3 = " << tmp << " ≠ 1/12.");
182 return order;
183 }
184
185 tmp = b.transpose() * A * A * c;
186 if (std::abs(tmp - 1.0/24.0) > tolerance) {
187 SANDALS_ASSERT_WARNING(!verbose, CMD "order 4 check failed, a_4 = " << tmp << " ≠ 1/24.");
188 return order;
189 }
190
191 order = 4; // Order 4 is the highest order that can be checked
192
193 // Check order 5
194 Vector c_4{c.array().pow(4)};
195 tmp = b.transpose() * c_4;
196 if (std::abs(tmp - 1.0/5.0) > tolerance) {
197 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_1 = " << tmp << " ≠ 1/5.");
198 return order;
199 }
200
201 Vector b_c2{b.array() * c_2.array()};
202 tmp = b_c2.transpose() * Ac;
203 if (std::abs(tmp - 1.0/10.0) > tolerance) {
204 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_2 = " << tmp << " ≠ 1/10.");
205 return order;
206 }
207
208 Vector b_Ac{b.array() * Ac.array()};
209 tmp = b_Ac.transpose() * Ac;
210 if (std::abs(tmp - 1.0/20.0) > tolerance) {
211 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_3 = " << tmp << " ≠ 1/20.");
212 return order;
213 }
214
215 Vector b_c{b.array() * c.array()};
216 Vector Ac2{A * c_2};
217 tmp = b_c.transpose() * Ac2;
218 if (std::abs(tmp - 1.0/15.0) > tolerance) {
219 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_4 = " << tmp << " ≠ 1/15.");
220 return order;
221 }
222
223 Vector Ac3{A * c_3};
224 tmp = b.transpose() * Ac3;
225 if (std::abs(tmp - 1.0/20.0) > tolerance) {
226 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_5 = " << tmp << " ≠ 1/20.");
227 return order;
228 }
229
230 Vector AAc{A * Ac};
231 tmp = b_c.transpose() * AAc;
232 if (std::abs(tmp - 1.0/30.0) > tolerance) {
233 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_6 = " << tmp << " ≠ 1/30.");
234 return order;
235 }
236
237 tmp = bA.transpose() * (c.array() * Ac.array()).matrix();
238 if (std::abs(tmp - 1.0/40.0) > tolerance) {
239 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_7 = " << tmp << " ≠ 1/40.");
240 return order;
241 }
242
243 tmp = bA.transpose() * Ac2;
244 if (std::abs(tmp - 1.0/60.0) > tolerance) {
245 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_8 = " << tmp << " ≠ 1/60.");
246 return order;
247 }
248
249 tmp = bA.transpose() * AAc;
250 if (std::abs(tmp - 1.0/120.0) > tolerance) {
251 SANDALS_ASSERT_WARNING(!verbose, CMD "order 5 check failed, a_9 = " << tmp << " ≠ 1/120.");
252 return order;
253 }
254
255 order = 5; // Order 5 is the highest order that can be checked
256
257 // Check order 6
258 Vector c_5{c.array().pow(5)};
259 tmp = b.transpose() * c_5;
260 if (std::abs(tmp - 1.0/6.0) > tolerance) {
261 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_1 = " << tmp << " ≠ 1/6.");
262 return order;
263 }
264
265 tmp = (b.array() * c_3.array()).matrix().transpose() * Ac;
266 if (std::abs(tmp - 1.0/12.0) > tolerance) {
267 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_2 = " << tmp << " ≠ 1/12.");
268 return order;
269 }
270
271 Vector Ac_2{Ac.array().pow(2)};
272 tmp = b_c.transpose() * Ac_2;
273 if (std::abs(tmp - 1.0/24.0) > tolerance) {
274 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_3 = " << tmp << " ≠ 1/24.");
275 return order;
276 }
277
278 tmp = b_c2.transpose() * Ac2;
279 if (std::abs(tmp - 1.0/18.0) > tolerance) {
280 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_4 = " << tmp << " ≠ 1/18.");
281 return order;
282 }
283
284 tmp = b.transpose() * (Ac2.array() * Ac.array()).matrix();
285 if (std::abs(tmp - 1.0/36.0) > tolerance) {
286 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_5 = " << tmp << " ≠ 1/36.");
287 return order;
288 }
289
290 tmp = b_c.transpose() * Ac3;
291 if (std::abs(tmp - 1.0/24.0) > tolerance) {
292 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_6 = " << tmp << " ≠ 1/24.");
293 return order;
294 }
295
296 Vector Ac4{A * c_4};
297 tmp = b.transpose() * Ac4;
298 if (std::abs(tmp - 1.0/30.0) > tolerance) {
299 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_7 = " << tmp << " ≠ 1/30.");
300 return order;
301 }
302
303 Vector bc2A{A.transpose() * b_c2};
304 tmp = bc2A.transpose() * Ac;
305 if (std::abs(tmp - 1.0/36.0) > tolerance) {
306 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_8 = " << tmp << " ≠ 1/36.");
307 return order;
308 }
309
310 tmp = b_Ac.transpose() * AAc;
311 if (std::abs(tmp - 1.0/72.0) > tolerance) {
312 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_9 = " << tmp << " ≠ 1/72.");
313 return order;
314 }
315
316 Vector bcA{b_c.transpose() * A};
317 tmp = bcA.transpose() * cAc;
318 if (std::abs(tmp - 1.0/48.0) > tolerance) {
319 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_10 = " << tmp << " ≠ 1/48.");
320 return order;
321 }
322
323 tmp = (bA.array() * c_2.array()).matrix().transpose() * Ac;
324 if (std::abs(tmp - 1.0/60.0) > tolerance) {
325 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_11 = " << tmp << " ≠ 1/60.");
326 return order;
327 }
328
329 tmp = bA.transpose() * Ac_2;
330 if (std::abs(tmp - 1.0/120.0) > tolerance) {
331 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_12 = " << tmp << " ≠ 1/120.");
332 return order;
333 }
334
335 tmp = bcA.transpose() * Ac2;
336 if (std::abs(tmp - 1.0/72.0) > tolerance) {
337 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_13 = " << tmp << " ≠ 1/72.");
338 return order;
339 }
340
341 Vector bA_c{bA.array() * c.array()};
342 tmp = bA_c.transpose() * Ac2;
343 if (std::abs(tmp - 1.0/90.0) > tolerance) {
344 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_14 = " << tmp << " ≠ 1/90.");
345 return order;
346 }
347
348 tmp = bA.transpose() * Ac3;
349 if (std::abs(tmp - 1.0/120.0) > tolerance) {
350 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_15 = " << tmp << " ≠ 1/120.");
351 return order;
352 }
353
354 tmp = bcA.transpose() * AAc;
355 if (std::abs(tmp - 1.0/144.0) > tolerance) {
356 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_16 = " << tmp << " ≠ 1/144.");
357 return order;
358 }
359
360 tmp = bA_c.transpose() * AAc;
361 if (std::abs(tmp - 1.0/180.0) > tolerance) {
362 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_17 = " << tmp << " ≠ 1/180.");
363 return order;
364 }
365
366 Vector AcAc{A * cAc};
367 tmp = bA.transpose() * AcAc;
368 if (std::abs(tmp - 1.0/240.0) > tolerance) {
369 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_18 = " << tmp << " ≠ 1/240.");
370 return order;
371 }
372
373 tmp = bA.transpose() * A * A * c_2;
374 if (std::abs(tmp - 1.0/360.0) > tolerance) {
375 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_19 = " << tmp << " ≠ 1/360.");
376 return order;
377 }
378
379 tmp = bA.transpose() * A * A * Ac;
380 if (std::abs(tmp - 1.0/720.0) > tolerance) {
381 SANDALS_ASSERT_WARNING(!verbose, CMD "order 6 check failed, a_20 = " << tmp << " ≠ 1/720.");
382 return order;
383 }
384
385 order = 6; // Order 6 is the highest order that can be checked with this method
386
387 return order;
388
389 #undef CMD
390 }
391 }; // struct Tableau
392
393} // namespace Sandals
394
395#endif // SANDALS_TABLEAU_HXX
#define CMD
#define SANDALS_ASSERT_WARNING(COND, MSG)
Definition Sandals.hh:60
The namespace for the Sandals library.
Definition Sandals.hh:73
double Real
Definition Sandals.hh:84
static Real const EPSILON
Definition Sandals.hh:120
int Integer
Definition Sandals.hh:85
Struct container for the Butcher tableau of a Runge-Kutta method.
Definition Tableau.hxx:36
bool is_embedded
Definition Tableau.hxx:49
enum class type :Integer {ERK=0, IRK=1, DIRK=2} Type
Definition Tableau.hxx:37
Eigen::Matrix< Real, S, S > Matrix
Definition Tableau.hxx:39
Integer order
Definition Tableau.hxx:43
Vector b_e
Definition Tableau.hxx:47
Type type
Definition Tableau.hxx:42
Eigen::Vector< Real, S > Vector
Definition Tableau.hxx:38
Matrix A
Definition Tableau.hxx:45
Integer compute_order(Matrix const &A, Vector const &b, Vector const &c, bool verbose=false) const
Definition Tableau.hxx:113
Integer order_e
Definition Tableau.hxx:44
std::string name
Definition Tableau.hxx:41
Vector b
Definition Tableau.hxx:46
bool check(bool verbose=false) const
Definition Tableau.hxx:56
Vector c
Definition Tableau.hxx:48