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