Sandals  v0.0.0
A C++ library for ODEs/DAEs integration
Loading...
Searching...
No Matches
RungeKutta.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_RUNGEKUTTA_HH
14#define SANDALS_RUNGEKUTTA_HH
15
16#include <optional>
17
18#include <Sandals.hh>
19
24
25#include <Sandals/Tableau.hh>
26#include <Sandals/Solution.hh>
27
28namespace Sandals {
29
30 /*\
31 | ____ _ __ _ _
32 | | _ \ _ _ _ __ __ _ ___| |/ / _| |_| |_ __ _
33 | | |_) | | | | '_ \ / _` |/ _ \ ' / | | | __| __/ _` |
34 | | _ <| |_| | | | | (_| | __/ . \ |_| | |_| || (_| |
35 | |_| \_\\__,_|_| |_|\__, |\___|_|\_\__,_|\__|\__\__,_|
36 | |___/
37 \*/
38
50 template <typename Real, Integer S, Integer N, Integer M = 0>
52 {
53 public:
54 using real_type = Real;
55 using VectorX = Eigen::Vector<Real, Eigen::Dynamic>;
56 using MatrixJX = Eigen::Matrix<Real, N, N>;
57
58 private:
59 using MatrixX = Eigen::Matrix<Real, Eigen::Dynamic, Eigen::Dynamic>;
60 using VectorK = Eigen::Vector<Real, N*S>;
61 using MatrixK = Eigen::Matrix<Real, N, S>;
62 using MatrixJK = Eigen::Matrix<Real, N*N, S>;
63 using MatrixJ = Eigen::Matrix<Real, N*S, N*S>;
64 using VectorP = Eigen::Matrix<Real, N+M, 1>;
65 using MatrixP = Eigen::Matrix<Real, N+M, N+M>;
66 using NewtonX = Optimist::RootFinder::Newton<Real, N>;
67 using NewtonK = Optimist::RootFinder::Newton<Real, N*S>;
74 using FunctionSC = std::function<void(Integer const, VectorX const &, Real const)>;
75
76 public:
78 const Real SQRT_EPSILON{std::sqrt(EPSILON)}; \
79
81 using Type = typename Tableau<Real, S>::Type;
82 using Time = Eigen::Vector<Real, Eigen::Dynamic>;
83
84 private:
87 mutable Eigen::FullPivLU<MatrixP> m_lu;
88
93 Real m_safety_factor{0.9};
96 Real m_min_step{EPSILON_HIGH};
98 bool m_adaptive{true};
99 bool m_verbose{false};
100 bool m_reverse{false};
102
103 Real m_projection_tolerance{EPSILON_HIGH};
105 bool m_projection{true};
106
107 public:
111 RungeKutta(const RungeKutta &) = delete;
112
116 RungeKutta & operator=(RungeKutta const & ) = delete;
117
122 RungeKutta(Tableau<Real, S> const & t_tableau)
123 : m_tableau(t_tableau) {
124 this->verbose_mode(this->m_verbose);
125 }
126
132 RungeKutta(Tableau<Real, S> const & t_tableau, System t_system)
133 : m_tableau(t_tableau), m_system(t_system) {
134 this->verbose_mode(this->m_verbose);
135 }
136
141 Type type() const {return this->m_tableau.type;}
142
147 bool is_erk() const {return this->m_tableau.type == Type::ERK;}
148
153 bool is_irk() const {return this->m_tableau.type == Type::IRK;}
154
159 bool is_dirk() const {return this->m_tableau.type == Type::DIRK;}
160
165 Tableau<Real, S> & tableau() {return this->m_tableau;}
166
171 Tableau<Real, S> const & tableau() const {return this->m_tableau;}
172
177 static constexpr Integer stages() {return S;}
178
183 std::string name() const {return this->m_tableau.name;}
184
189 Integer order() const {return this->m_tableau.order;}
190
195 bool is_embedded() const {return this->m_tableau.is_embedded;}
196
201 MatrixS A() const {return this->m_tableau.A;}
202
207 VectorS b() const {return this->m_tableau.b;}
208
213 VectorS b_embedded() const {return this->m_tableau.b_e;}
214
219 VectorS c() const {return this->m_tableau.c;}
220
225 System system() {return this->m_system;}
226
231 void system(System t_system) {this->m_system = t_system;}
232
249 ) {
250 this->m_system = std::make_shared<ImplicitWrapper<Real, N, M>>(F, JF_x, JF_x_dot, h, Jh_x, in_domain);
251 }
252
264 std::string name,
271 ) {
272 this->m_system = std::make_shared<ImplicitWrapper<Real, N, M>>(name, F, JF_x, JF_x_dot, h, Jh_x, in_domain);
273 }
274
289 ) {
290 this->m_system = std::make_shared<ExplicitWrapper<Real, N, M>>(f, Jf_x, h, Jh_x, in_domain);
291 }
292
303 std::string name,
309 ) {
310 this->m_system = std::make_shared<ExplicitWrapper<Real, N, M>>(name, f, Jf_x, h, Jh_x, in_domain);
311 }
312
329 ) {
330 this->m_system = std::make_shared<LinearWrapper<Real, N, M>>(E, A, b, h, Jh_x, in_domain);
331 }
332
344 std::string name,
351 ) {
352 this->m_system = std::make_shared<LinearWrapper<Real, N, M>>(name, E, A, b, h, Jh_x, in_domain);
353 }
354
376
400
405 bool has_system() {return this->m_system != nullptr;}
406
411 Real absolute_tolerance() {return this->m_absolute_tolerance;}
412
417 void absolute_tolerance(Real const t_absolute_tolerance) {this->m_absolute_tolerance = t_absolute_tolerance;}
418
423 Real relative_tolerance() {return this->m_relative_tolerance;}
424
429 void relative_tolerance(Real const t_relative_tolerance) {this->m_relative_tolerance = t_relative_tolerance;}
430
435 Real & safety_factor() {return this->m_safety_factor;}
436
441 void safety_factor(Real const t_safety_factor) {this->m_safety_factor = t_safety_factor;}
442
447 Real & min_safety_factor() {return this->m_min_safety_factor;}
448
453 void min_safety_factor(Real const t_min_safety_factor) {this->m_min_safety_factor = t_min_safety_factor;}
454
459 Real & max_safety_factor() {return this->m_max_safety_factor;}
460
465 void max_safety_factor(Real const t_max_safety_factor) {this->m_max_safety_factor = t_max_safety_factor;}
466
471 Real & min_step() {return this->m_min_step;}
472
477 void min_step(Real const t_min_step) {this->m_min_step = t_min_step;}
478
483 Integer & max_substeps() {return this->m_max_substeps;}
484
489 void max_substeps(Integer const t_max_substeps) {this->m_max_substeps = t_max_substeps;}
490
495 bool adaptive_mode() {return this->m_adaptive;}
496
501 void adaptive(bool t_adaptive) {this->m_adaptive = t_adaptive;}
502
506 void enable_adaptive_mode() {this->m_adaptive = true;}
507
511 void disable_adaptive_mode() {this->m_adaptive = false;}
512
517 bool verbose_mode() {return this->m_verbose;}
518
523 void verbose_mode(bool t_verbose) {
524 this->m_verbose = t_verbose;
525 this->m_newtonX.verbose_mode(t_verbose);
526 this->m_newtonK.verbose_mode(t_verbose);
527 }
528
532 void enable_verbose_mode() {this->verbose_mode(true);}
533
537 void disable_verbose_mode() {this->verbose_mode(false);}
538
543 bool reverse_mode() {return this->m_reverse;}
544
549 void reverse(bool t_reverse) {this->m_reverse = t_reverse;}
550
554 void enable_reverse_mode() {this->m_reverse = true;}
555
559 void disable_reverse_mode() {this->m_reverse = false;}
560
565 FunctionSC step_callback() {return this->m_step_callback;}
566
571 void step_callback(FunctionSC const & t_step_callback) {this->m_step_callback = t_step_callback;}
572
577 Real projection_tolerance() {return this->m_projection_tolerance;}
578
583 void projection_tolerance(Real const t_projection_tolerance)
584 {this->m_projection_tolerance = t_projection_tolerance;}
585
590 Integer & max_projection_iterations() {return this->m_max_projection_iterations;}
591
596 void max_projection_iterations(Integer const t_max_projection_iterations)
597 {this->m_max_projection_iterations = t_max_projection_iterations;}
598
603 bool projection() {return this->m_projection;}
604
609 void projection(bool t_projection) {this->m_projection = t_projection;}
610
614 void enable_projection() {this->m_projection = true;}
615
619 void disable_projection() {this->m_projection = false;}
620
655 Real estimate_step(VectorN const & x, VectorN const & x_e, Real const h_k) const
656 {
657 Real desired_error{this->m_absolute_tolerance + this->m_relative_tolerance *
658 std::max(x.array().abs().maxCoeff(), x_e.array().abs().maxCoeff())};
659 Real truncation_error{(x - x_e).array().abs().maxCoeff()};
660 return h_k * std::min(this->m_max_safety_factor, std::max(this->m_min_safety_factor,
661 this->m_safety_factor * std::pow(desired_error/truncation_error,
662 1.0/std::max(this->m_tableau.order, this->m_tableau.order_e))));
663 }
664
669 std::string info() const
670 {
671 std::ostringstream os;
672 os
673 << "Runge-Kutta method:\t" << this->name() << std::endl
674 << "\t- order:\t" << this->order() << std::endl
675 << "\t- stages:\t" << this->stages() << std::endl
676 << "\t- type:\t";
677 switch (this->type()) {
678 case Type::ERK: os << "explicit"; break;
679 case Type::IRK: os << "implicit"; break;
680 case Type::DIRK: os << "diagonally implicit"; break;
681 }
682 os
683 << std::endl
684 << "\t- embedded:\t" << this->is_embedded() << std::endl;
685 if (this->has_system()) {
686 os << "\t- system:\t" << this->m_system->name() << std::endl;
687 } else {
688 os << "\t- system:\t" << "none" << std::endl;
689 }
690 return os.str();
691 }
692
697 void info(std::ostream &os) {os << this->info();}
698
699 /*\
700 | _____ ____ _ __
701 | | ____| _ \| |/ /
702 | | _| | |_) | ' /
703 | | |___| _ <| . \
704 | |_____|_| \_\_|\_\
705 |
706 \*/
707
722 bool erk_explicit_step(VectorN const & x_old, Real const t_old, Real const h_old, VectorN & x_new,
723 Real & h_new, MatrixK & K) const
724 {
725 using Eigen::all;
726 using Eigen::seqN;
727
728 // Compute the K variables in the case of an explicit method and explicit system
729 VectorN x_node;
730 for (Integer i{0}; i < S; ++i) {
731 x_node = x_old + K(all, seqN(0, i)) * this->m_tableau.A(i, seqN(0, i)).transpose();
732 if (!this->m_reverse) {
733 K.col(i) = h_old * static_cast<Explicit<Real, N, M> const *>(this->m_system.get())->f(x_node, t_old + h_old*this->m_tableau.c(i));
734 } else {
735 K.col(i) = h_old * static_cast<Explicit<Real, N, M> const *>(this->m_system.get())->f_reverse(x_node, t_old + h_old*this->m_tableau.c(i));
736 }
737 }
738 if (!K.allFinite()) {return false;}
739
740 // Perform the step and obtain the next state
741 x_new = x_old + K * this->m_tableau.b;
742
743 // Adapt next step
744 if (this->m_adaptive && this->m_tableau.is_embedded) {
745 VectorN x_emb = x_old + K * this->m_tableau.b_e;
746 h_new = this->estimate_step(x_new, x_emb, h_old);
747 }
748 return true;
749 }
750
764 bool erk_explicit_propagate(VectorN const & x, Real const t, Real const h, MatrixK const & K,
765 MatrixJX & Jx) const
766 {
767 using Eigen::all;
768 using Eigen::seqN;
769
770 // Propagate the derivative of K with respect to x
771 VectorN x_node;
772 MatrixN Jf_x;
773 std::array<MatrixN, S> dK_dx;
774 for (Integer i{0}; i < S; ++i) {
775 // Compute the node
776 x_node = x + K(all, seqN(0, i)) * this->m_tableau.A(i, seqN(0, i)).transpose();
777
778 // Compute the Jacobian of f with respect to x at the node
779 if (!this->m_reverse) {
780 Jf_x = static_cast<Explicit<Real, N, M> const *>(this->m_system.get())->Jf_x(x_node, t + h*this->m_tableau.c(i));
781 } else {
782 Jf_x = static_cast<Explicit<Real, N, M> const *>(this->m_system.get())->Jf_x_reverse(x_node, t + h*this->m_tableau.c(i));
783 }
784
785 // Propagate the derivative of K with respect to x
786 dK_dx[i] = h * Jf_x;
787 for (Integer j{0}; j < i; ++j) {
788 dK_dx[i] += h * Jf_x * dK_dx[j] * this->m_tableau.A(i, j);
789 }
790
791 // Check for NaNs or Infs
792 if (!dK_dx[i].allFinite()) {return false;}
793 }
794
795 // Compute the derivative of x_new with respect to x
796 Jx.setIdentity();
797 for (Integer i{0}; i < S; ++i) {Jx += h * dK_dx[i] * this->m_tableau.b(i);}
798
799 return true;
800 }
801
822 void erk_implicit_function(Integer const s, VectorN const & x, Real const t, Real const h,
823 MatrixK const & K, VectorN & fun) const
824 {
825 using Eigen::all;
826 using Eigen::seqN;
827 VectorN x_node(x + K(all, seqN(0, s)) * this->m_tableau.A(s, seqN(0, s)).transpose());
828 if (!this->m_reverse) {
829 fun = this->m_system->F(x_node, K.col(s)/h, t + h * this->m_tableau.c(s));
830 } else {
831 fun = this->m_system->F_reverse(x_node, K.col(s)/h, t + h * this->m_tableau.c(s));
832 }
833 }
834
866 void erk_implicit_jacobian(Integer const s, VectorN const & x, Real const t, Real const h,
867 MatrixK const & K, MatrixN & jac) const
868 {
869 using Eigen::all;
870 using Eigen::seqN;
871 VectorN x_node(x + K(all, seqN(0, s)) * this->m_tableau.A(s, seqN(0, s)).transpose());
872 if (!this->m_reverse) {
873 jac = this->m_system->JF_x_dot(x_node, K.col(s)/h, t + h * this->m_tableau.c(s)) / h;
874 } else {
875 jac = this->m_system->JF_x_dot_reverse(x_node, K.col(s)/h, t + h * this->m_tableau.c(s)) / h;
876 }
877 }
878
893 bool erk_implicit_step(VectorN const & x_old, Real const t_old, Real const h_old, VectorN & x_new,
894 Real & h_new, MatrixK & K) const
895 {
896 VectorN K_sol;
897 VectorN K_ini(VectorN::Zero());
898
899 // Check if the solver converged
900 for (Integer s{0}; s < S; ++s) {
901 if (this->m_newtonX.solve(
902 [this, s, &K, &x_old, t_old, h_old](VectorN const & K_fun, VectorN & fun)
903 {K.col(s) = K_fun; this->erk_implicit_function(s, x_old, t_old, h_old, K, fun);},
904 [this, s, &K, &x_old, t_old, h_old](VectorN const & K_jac, MatrixN & jac)
905 {K.col(s) = K_jac; this->erk_implicit_jacobian(s, x_old, t_old, h_old, K, jac);},
906 K_ini, K_sol)) {
907 K.col(s) = K_sol;
908 } else {
909 return false;
910 }
911 }
912
913 // Perform the step and obtain the next state
914 x_new = x_old + K * this->m_tableau.b;
915
916 // Adapt next step
917 if (this->m_adaptive && this->m_tableau.is_embedded) {
918 VectorN x_emb(x_old + K * this->m_tableau.b_e);
919 h_new = this->estimate_step(x_new, x_emb, h_old);
920 }
921 return true;
922 }
923
937 bool erk_implicit_propagate(VectorN const & x, Real const t, Real const h, MatrixK const & K,
938 MatrixJX & Jx) const
939 {
940 using Eigen::all;
941 using Eigen::seqN;
942
943 // Propagate the derivative of K with respect to x
944 VectorN x_node, x_dot_node;
945 MatrixN JF_x, JF_x_dot, jac;
946 std::array<MatrixN, S> dK_dx;
947 for (Integer i{0}; i < S; ++i) {
948 // Compute the node
949 x_node = x + K(all, seqN(0, i)) * this->m_tableau.A(i, seqN(0, i)).transpose();
950 x_dot_node = K.col(i) / h;
951
952 // Compute the Jacobians of F with respect to x and x_dot at the node
953 if (!this->m_reverse) {
954 JF_x = this->m_system->JF_x(x_node, x_dot_node, t + h*this->m_tableau.c(i));
955 JF_x_dot = this->m_system->JF_x_dot(x_node, x_dot_node, t + h*this->m_tableau.c(i));
956 } else {
957 JF_x = this->m_system->JF_x_reverse(x_node, x_dot_node, t + h*this->m_tableau.c(i));
958 JF_x_dot = this->m_system->JF_x_dot_reverse(x_node, x_dot_node, t + h*this->m_tableau.c(i));
959 }
960
961 // Propagate the derivative of K with respect to x
962 jac = MatrixN::Identity();
963 for (Integer j{0}; j < i; ++j) {
964 jac += dK_dx[j] * this->m_tableau.A(i, j);
965 }
966 // Solve the linear system: JF_x_dot/h * dK_dx[i] + JF_x * jac = 0
967 // Rearranged: dK_dx[i] = -h * JF_x * jac * (JF_x_dot)^(-1)
968 // But since JF_x_dot/h is the Jacobian w.r.t. x_dot, we solve for dK_dx[i]
969 dK_dx[i] = -(JF_x * jac).lu().solve(JF_x_dot / h);
970
971 // Check for NaNs or Infs
972 if (!dK_dx[i].allFinite()) {return false;}
973 }
974
975 // Compute the derivative of x_new with respect to x
976 Jx.setIdentity();
977 for (Integer i{0}; i < S; ++i) {Jx += h * dK_dx[i] * this->m_tableau.b(i);}
978
979 return true;
980 }
981
982 /*\
983 | ___ ____ _ __
984 | |_ _| _ \| |/ /
985 | | || |_) | ' /
986 | | || _ <| . \
987 | |___|_| \_\_|\_\
988 |
989 \*/
990
1015 void irk_function(VectorN const & x, Real const t, Real const h, VectorK const & K, VectorK & fun) const
1016 {
1017 VectorN x_node;
1018 MatrixK K_mat{K.reshaped(N, S)};
1019 MatrixK fun_mat;
1020 for (Integer i{0}; i < S; ++i) {
1021 x_node = x + K_mat * this->m_tableau.A.row(i).transpose();
1022 if (!this->m_reverse) {
1023 fun_mat.col(i) = this->m_system->F(x_node, K_mat.col(i)/h, t + h * this->m_tableau.c(i));
1024 } else {
1025 fun_mat.col(i) = this->m_system->F_reverse(x_node, K_mat.col(i)/h, t + h * this->m_tableau.c(i));
1026 }
1027 }
1028 fun = fun_mat.reshaped(N*S, 1);
1029 }
1030
1067 void irk_jacobian(VectorN const & x, Real const t, Real const h, VectorK const & K, MatrixJ & jac) const
1068 {
1069 using Eigen::seqN;
1070
1071 // Reset the Jacobian matrix
1072 jac.setZero();
1073
1074 // Loop through each equation of the system
1075 MatrixK K_mat{K.reshaped(N, S)};
1076 Real t_node;
1077 VectorN x_node, x_dot_node;
1078 MatrixN JF_x, JF_x_dot;
1079 auto idx = seqN(0, N), jdx = seqN(0, N);
1080 for (Integer i{0}; i < S; ++i) {
1081 t_node = t + h * this->m_tableau.c(i);
1082 x_node = x + K_mat * this->m_tableau.A.row(i).transpose();
1083
1084 // Compute the Jacobians with respect to x and x_dot
1085 x_dot_node = K_mat.col(i) / h;
1086 if (!this->m_reverse) {
1087 JF_x = this->m_system->JF_x(x_node, x_dot_node, t_node);
1088 JF_x_dot = this->m_system->JF_x_dot(x_node, x_dot_node, t_node);
1089 } else {
1090 JF_x = this->m_system->JF_x_reverse(x_node, x_dot_node, t_node);
1091 JF_x_dot = this->m_system->JF_x_dot_reverse(x_node, x_dot_node, t_node);
1092 }
1093
1094 // Combine the Jacobians with respect to x and x_dot to obtain the Jacobian with respect to K
1095 idx = seqN(i*N, N);
1096 for (Integer j{0}; j < S; ++j) {
1097 jdx = seqN(j*N, N);
1098 if (i == j) {
1099 jac(idx, jdx) = this->m_tableau.A(i,j) * JF_x + JF_x_dot / h;
1100 } else {
1101 jac(idx, jdx) = this->m_tableau.A(i,j) * JF_x;
1102 }
1103 }
1104 }
1105 }
1106
1121 bool irk_step(VectorN const & x_old, Real const t_old, Real const h_old, VectorN & x_new,
1122 Real & h_new, MatrixK & K) const
1123 {
1124 VectorK K_vec;
1125 VectorK K_ini(VectorK::Zero());
1126
1127 // Check if the solver converged
1128 if (!this->m_newtonK.solve(
1129 [this, &x_old, t_old, h_old](VectorK const & K_fun, VectorK & fun)
1130 {this->irk_function(x_old, t_old, h_old, K_fun, fun);},
1131 [this, &x_old, t_old, h_old](VectorK const & K_jac, MatrixJ & jac)
1132 {this->irk_jacobian(x_old, t_old, h_old, K_jac, jac);},
1133 K_ini, K_vec))
1134 {return false;}
1135
1136 // Reshape the K vector to a matrix
1137 K = K_vec.reshaped(N, S);
1138 if (!K.allFinite()) {return false;}
1139
1140 // Perform the step and obtain the next state
1141 x_new = x_old + K * this->m_tableau.b;
1142
1143 // Adapt next step
1144 if (this->m_adaptive && this->m_tableau.is_embedded) {
1145 VectorN x_emb(x_old + K * this->m_tableau.b_e);
1146 h_new = this->estimate_step(x_new, x_emb, h_old);
1147 }
1148 return true;
1149 }
1150
1164 bool irk_propagate(VectorN const & x, Real const t, Real const h, MatrixK const & K,
1165 MatrixJX & Jx) const
1166 {
1167 using Eigen::all;
1168 using Eigen::seqN;
1169
1170 // Propagate the derivative of K with respect to x
1171 VectorN x_node, x_dot_node;
1172 MatrixN JF_x, JF_x_dot;
1173 Eigen::Matrix<Real, N*S, N*S> A;
1174 Eigen::Matrix<Real, N*S, N> b;
1175 std::array<MatrixN, S> dK_dx;
1176 for (Integer i{0}; i < S; ++i) {
1177 // Compute the node
1178 x_node = x + K * this->m_tableau.A.row(i).transpose();
1179 x_dot_node = K.col(i) / h;
1180
1181 // Compute the Jacobians of F with respect to x and x_dot at the node
1182 if (!this->m_reverse) {
1183 JF_x = this->m_system->JF_x(x_node, x_dot_node, t + h*this->m_tableau.c(i));
1184 JF_x_dot = this->m_system->JF_x_dot(x_node, x_dot_node, t + h*this->m_tableau.c(i));
1185 } else {
1186 JF_x = this->m_system->JF_x_reverse(x_node, x_dot_node, t + h*this->m_tableau.c(i));
1187 JF_x_dot = this->m_system->JF_x_dot_reverse(x_node, x_dot_node, t + h*this->m_tableau.c(i));
1188 }
1189
1190 // Fill the big linear system
1191 A.block(i*N, i*N, N, N) = JF_x_dot;
1192 for (Integer j{0}; j < S; ++j) {
1193 A.block(i*N, j*N, N, N) += h * this->m_tableau.A(i, j) * JF_x;
1194 }
1195 b.block(i*N, 0, N, N) = -h * JF_x;
1196
1197 // Check for NaNs or Infs
1198 if (!JF_x.allFinite() || !JF_x_dot.allFinite()) {return false;}
1199 }
1200
1201 // Solve the big linear system
1202 Eigen::Matrix<Real, N*S, N> dK_dx_mat(A.lu().solve(b));
1203 for (Integer i{0}; i < S; ++i) {
1204 dK_dx[i] = dK_dx_mat.block(i*N, 0, N, N);
1205 if (!dK_dx[i].allFinite()) {return false;}
1206 }
1207
1208 // Compute the derivative of x_new with respect to x
1209 Jx.setIdentity();
1210 for (Integer i{0}; i < S; ++i) {Jx += h * dK_dx[i] * this->m_tableau.b(i);}
1211
1212 return true;
1213 }
1214
1215 /*\
1216 | ____ ___ ____ _ __
1217 | | _ \_ _| _ \| |/ /
1218 | | | | | || |_) | ' /
1219 | | |_| | || _ <| . \
1220 | |____/___|_| \_\_|\_\
1221 |
1222 \*/
1223
1244 void dirk_function(Integer n, VectorN const & x, Real const t, Real const h, MatrixK const & K,
1245 VectorN & fun) const
1246 {
1247 using Eigen::all;
1248 using Eigen::seqN;
1249 VectorN x_node(x + K(all, seqN(0, n+1)) * this->m_tableau.A(n, seqN(0, n+1)).transpose());
1250 if (!this->m_reverse) {
1251 fun = this->m_system->F(x_node, K.col(n)/h, t + h * this->m_tableau.c(n));
1252 } else {
1253 fun = this->m_system->F_reverse(x_node, K.col(n)/h, t + h * this->m_tableau.c(n));
1254 }
1255 }
1256
1290 void dirk_jacobian(Integer n, VectorN const & x, Real const t, Real const h, MatrixK const & K,
1291 MatrixN & jac) const
1292 {
1293 using Eigen::all;
1294 using Eigen::seqN;
1295 Real t_node{t + h * this->m_tableau.c(n)};
1296 VectorN x_node(x + K(all, seqN(0, n+1)) * this->m_tableau.A(n, seqN(0, n+1)).transpose());
1297 VectorN x_dot_node(K.col(n)/h);
1298 if (!this->m_reverse) {
1299 jac = this->m_tableau.A(n,n) * this->m_system->JF_x(x_node, x_dot_node, t_node) +
1300 this->m_system->JF_x_dot(x_node, x_dot_node, t_node) / h;
1301 } else {
1302 jac = this->m_tableau.A(n,n) * this->m_system->JF_x_reverse(x_node, x_dot_node, t_node) +
1303 this->m_system->JF_x_dot_reverse(x_node, x_dot_node, t_node) / h;
1304 }
1305 }
1306
1321 bool dirk_step(VectorN const & x_old, Real const t_old, Real const h_old, VectorN & x_new,
1322 Real & h_new, MatrixK & K) const
1323 {
1324 VectorN K_sol;
1325 VectorN K_ini(VectorN::Zero());
1326
1327 // Check if the solver converged at each step
1328 for (Integer n{0}; n < S; ++n) {
1329 if (this->m_newtonX.solve(
1330 [this, n, &K, &x_old, t_old, h_old](VectorN const & K_fun, VectorN & fun)
1331 {K.col(n) = K_fun; this->dirk_function(n, x_old, t_old, h_old, K, fun);},
1332 [this, n, &K, &x_old, t_old, h_old](VectorN const & K_jac, MatrixN & jac)
1333 {K.col(n) = K_jac; this->dirk_jacobian(n, x_old, t_old, h_old, K, jac);},
1334 K_ini, K_sol)) {
1335 K.col(n) = K_sol;
1336 } else {
1337 return false;
1338 }
1339 }
1340
1341 // Perform the step and obtain the next state
1342 x_new = x_old + K * this->m_tableau.b;
1343
1344 // Adapt next step
1345 if (this->m_adaptive && this->m_tableau.is_embedded) {
1346 VectorN x_emb(x_old + K * this->m_tableau.b_e);
1347 h_new = this->estimate_step(x_new, x_emb, h_old);
1348 }
1349 return true;
1350 }
1351
1365 bool dirk_propagate(VectorN const & x, Real const t, Real const h, MatrixK const & K,
1366 MatrixJX & Jx) const
1367 {
1368 using Eigen::all;
1369 using Eigen::seqN;
1370
1371 // Propagate the derivative of K with respect to x for DIRK methods
1372 VectorN x_node, x_dot_node;
1373 MatrixN JF_x, JF_x_dot, jac;
1374 std::array<MatrixN, S> dK_dx;
1375 for (Integer i{0}; i < S; ++i) {
1376 // Compute the node
1377 x_node = x + K(all, seqN(0, i+1)) * this->m_tableau.A(i, seqN(0, i+1)).transpose();
1378 x_dot_node = K.col(i) / h;
1379
1380 // Compute the Jacobians of F with respect to x and x_dot at the node
1381 if (!this->m_reverse) {
1382 JF_x = this->m_system->JF_x(x_node, x_dot_node, t + h*this->m_tableau.c(i));
1383 JF_x_dot = this->m_system->JF_x_dot(x_node, x_dot_node, t + h*this->m_tableau.c(i));
1384 } else {
1385 JF_x = this->m_system->JF_x_reverse(x_node, x_dot_node, t + h*this->m_tableau.c(i));
1386 JF_x_dot = this->m_system->JF_x_dot_reverse(x_node, x_dot_node, t + h*this->m_tableau.c(i));
1387 }
1388
1389 // Propagate the derivative of K with respect to x
1390 jac = MatrixN::Identity();
1391 for (Integer j{0}; j < i; ++j) {
1392 jac += dK_dx[j] * this->m_tableau.A(i, j);
1393 }
1394 // Solve the linear system: JF_x_dot/h * dK_dx[i] + JF_x * jac = 0
1395 // Rearranged: dK_dx[i] = -h * JF_x * jac * (JF_x_dot)^(-1)
1396 dK_dx[i] = -(JF_x * jac).lu().solve(JF_x_dot / h);
1397
1398 // Check for NaNs or Infs
1399 if (!dK_dx[i].allFinite()) {return false;}
1400 }
1401
1402 // Compute the derivative of x_new with respect to x
1403 Jx.setIdentity();
1404 for (Integer i{0}; i < S; ++i) {Jx += h * dK_dx[i] * this->m_tableau.b(i);}
1405
1406 return true;
1407 }
1408
1421 bool step(VectorN const & x_old, Real const t_old, Real const h_old, VectorN & x_new, Real & h_new,
1422 MatrixK & K) const
1423 {
1424 #define CMD "Sandals::RungeKutta::step(...): "
1425
1426 SANDALS_ASSERT(this->m_system->in_domain(x_old, t_old), CMD "in " << this->m_tableau.name <<
1427 " solver, at t = " << t_old << ", x = " << x_old.transpose() << ", system out of domain.");
1428
1429 if (this->is_erk() && this->m_system->is_explicit()) {
1430 return this->erk_explicit_step(x_old, t_old, h_old, x_new, h_new, K);
1431 } else if (this->is_erk() && this->m_system->is_implicit()) {
1432 return this->erk_implicit_step(x_old, t_old, h_old, x_new, h_new, K);
1433 } else if (this->is_dirk()) {
1434 return this->dirk_step(x_old, t_old, h_old, x_new, h_new, K);
1435 } else {
1436 return this->irk_step(x_old, t_old, h_old, x_new, h_new, K);
1437 }
1438
1439 #undef CMD
1440 }
1441
1456 bool propagate(VectorN const & x, Real const t, Real const h, MatrixK const & K, MatrixJX & Jx) const
1457 {
1458 #define CMD "Sandals::RungeKutta::propagate(...): "
1459
1460 if (this->is_erk() && this->m_system->is_explicit()) {
1461 return this->erk_explicit_propagate(x, t, h, K, Jx);
1462 } else if (this->is_erk() && this->m_system->is_implicit()) {
1463 return this->erk_implicit_propagate(x, t, h, K, Jx);
1464 } else if (this->is_dirk()) {
1465 return this->dirk_propagate(x, t, h, K, Jx);
1466 } else {
1467 return this->irk_propagate(x, t, h, K, Jx);
1468 }
1469
1470 #undef CMD
1471 }
1472
1490 template <bool Propagate = true>
1491 bool advance(VectorN const & x_old, Real const t_old, Real const h_old, VectorN & x_new,
1492 Real & h_new, MatrixJX & Jx) const
1493 {
1494 #define CMD "Sandals::RungeKutta::advance(...): "
1495
1496 // Check step size
1497 SANDALS_ASSERT(h_old > Real(0.0), CMD "in " << this->m_tableau.name << " solver, h = "<<
1498 h_old << ", expected > 0.");
1499
1500 // Reset the derivative propagation matrix
1501 if constexpr (Propagate) {Jx.setIdentity();}
1502
1503 // If the integration step failed, try again with substepping
1504 MatrixK K;
1505 if (!this->step(x_old, t_old, h_old, x_new, h_new, K)) {
1506
1507 // Store temporary variables
1508 VectorN x_tmp(x_old);
1509 Real t_tmp{t_old}, h_tmp{h_old / Real(2.0)};
1510
1511 // Substepping logic
1512 Integer max_k{this->m_max_substeps * this->m_max_substeps}, k{2};
1513 Real h_new_tmp;
1514 while (k > 0) {
1515 // Calculate the next step with substepping logic
1516 if (this->step(x_tmp, t_tmp, h_tmp, x_new, h_new_tmp, K)) {
1517
1518 if constexpr (Propagate) {
1519 // Propagate the derivative of K with respect to x
1520 MatrixJX Jx_tmp;
1521 if (!this->propagate(x_tmp, t_tmp, h_tmp, K, Jx_tmp)) {
1522 SANDALS_WARNING(CMD "in " << this->m_tableau.name << " solver, at t = " << t_tmp <<
1523 ", Jacobian propagation failed, aborting.");
1524 return false;
1525 }
1526
1527 // Update the derivative propagation
1528 if constexpr (Propagate) {Jx *= Jx_tmp;}
1529 }
1530
1531 // Accept the step
1532 h_tmp = h_new_tmp;
1533
1534 // If substepping is enabled, double the step size
1535 if (k > 0 && k < max_k) {
1536 k -= 1;
1537 // If the substepping index is even, double the step size
1538 if (k % 2 == 0) {
1539 h_tmp = Real(2.0) * h_tmp;
1540 if (this->m_verbose) {
1541 SANDALS_WARNING(CMD "in " << this->m_tableau.name << " solver, at t = " << t_tmp <<
1542 ", integration succedded disable one substepping layer.");
1543 }
1544 }
1545 }
1546
1547 // Check the infinity norm of the solution
1548 SANDALS_ASSERT(std::isfinite(x_tmp.maxCoeff()), CMD "in " << this->m_tableau.name <<
1549 " solver, at t = " << t_tmp << ", ||x||_inf = inf, computation interrupted.");
1550
1551 } else {
1552
1553 // If the substepping index is too high, abort the integration
1554 k += 2;
1555 SANDALS_ASSERT(k < max_k, CMD "in " << this->m_tableau.name << " solver, at t = " <<
1556 t_tmp << ", integration failed with h = " << h_tmp << ", aborting.");
1557 return false;
1558
1559 // Otherwise, try again with a smaller step
1560 if (this->m_verbose) {SANDALS_WARNING(CMD "in " << this->m_tableau.name << " solver, " <<
1561 "at t = " << t_tmp << ", integration failed, adding substepping layer.");}
1562 h_tmp /= Real(2.0);
1563 continue;
1564 }
1565
1566 // Store independent variable (or time)
1567 t_tmp += h_tmp;
1568 }
1569
1570 // Store output states substepping solutions
1571 x_new = x_tmp;
1572 h_new = h_tmp;
1573
1574 } else {
1575 // Propagate the derivative of the solution with respect to the states x
1576 if constexpr (Propagate) {
1577 if (!this->propagate(x_old, t_old, h_old, K, Jx)) {
1578 SANDALS_WARNING(CMD "in " << this->m_tableau.name << " solver, at t = " << t_old <<
1579 ", Jacobian propagation failed, aborting.");
1580 return false;
1581 }
1582 }
1583 }
1584
1585 // Project intermediate solution on the invariants
1586 if (this->m_projection) {
1587 VectorN x_projected;
1588 if (this->project(x_new, t_old + h_new, x_projected)) {
1589 x_new = x_projected;
1590 } else {
1591 return false;
1592 }
1593 }
1594 return true;
1595
1596 #undef CMD
1597 }
1598
1615 template <bool Propagate = true>
1616 bool solve(VectorX const & t_mesh, VectorN const & ics, Solution<Real, N, M> & sol, MatrixJX & Jx) const
1617 {
1618 using Eigen::last;
1619
1620 #define CMD "Sandals::RungeKutta::solve(...): "
1621
1622 // Instantiate output
1623 sol.resize(t_mesh.size());
1624
1625 // Store initial conditions
1626 sol.t(0) = t_mesh(0);
1627 sol.x.col(0) = ics;
1628 sol.h.col(0) = this->m_system->h(ics, t_mesh(0));
1629
1630 // Check initial conditions
1631 if constexpr (Propagate) {
1632 if (!Jx.allFinite()) {
1633 SANDALS_ERROR(CMD "in " << this->m_tableau.name << " solver, initial Jacobian " <<
1634 "contains NaNs or Infs, aborting.");
1635 return false;
1636 }
1637 }
1638
1639 // Callback on initial conditions
1640 if (this->m_step_callback) {this->m_step_callback(0, ics, t_mesh(0));}
1641
1642 // Update the current step
1643 Integer step{0};
1644 VectorN x_old_step(ics), x_new_step(ics);
1645 MatrixJX Jx_step;
1646 Real t_step{t_mesh(0)}, h_step{t_mesh(1) - t_mesh(0)}, h_tmp_step{h_step}, h_new_step;
1647 bool mesh_point_bool, saturation_bool;
1648
1649 while (true) {
1650 // Integrate system
1651 if (!this->template advance<Propagate>(x_old_step, t_step, h_step, x_new_step, h_new_step, Jx_step))
1652 {return false;}
1653
1654 // Update the current step
1655 t_step += h_step;
1656
1657 // Saturate the suggested timestep
1658 mesh_point_bool = std::abs(t_step - t_mesh(step+1)) < SQRT_EPSILON;
1659 saturation_bool = t_step + h_new_step > t_mesh(step+1) + SQRT_EPSILON;
1660 if (this->m_adaptive && this->m_tableau.is_embedded && !mesh_point_bool && saturation_bool) {
1661 h_tmp_step = h_new_step; // Used to store the previous step and keep the integration pace
1662 h_step = t_mesh(step+1) - t_step;
1663 } else {
1664 h_step = h_new_step;
1665 }
1666
1667 // Store solution if the step is a mesh point
1668 if (!this->m_adaptive || mesh_point_bool) {
1669
1670 // Update temporaries
1671 step += 1;
1672 h_step = h_tmp_step;
1673
1674 // Update outputs
1675 sol.t(step) = t_step;
1676 sol.x.col(step) = x_new_step;
1677 sol.h.col(step) = this->m_system->h(x_new_step, t_step);
1678
1679 // Callback after the step is completed
1680 if (this->m_step_callback) {this->m_step_callback(step, x_new_step, t_step);}
1681
1682 // Check if the current step is the last one
1683 if (std::abs(t_step - t_mesh(last)) < SQRT_EPSILON) {break;}
1684
1685 // Update the previous step
1686 x_old_step = x_new_step;
1687
1688 // Propagate the derivative of the solution with respect to the states x
1689 if constexpr (Propagate) {Jx *= Jx_step;}
1690 }
1691 }
1692 return true;
1693
1694 #undef CMD
1695 }
1696
1708 bool solve(VectorX const & t_mesh, VectorN const & ics, Solution<Real, N, M> & sol) const
1709 {
1710 MatrixJX Jx; // Dummy variable
1711 return this->template solve<false>(t_mesh, ics, sol, Jx);
1712 }
1713
1731 template <bool Propagate = true>
1732 bool adaptive_solve(VectorX const & t_mesh, VectorN const & ics, Solution<Real, N, M> & sol,
1733 MatrixJX & Jx) const
1734 {
1735 using Eigen::all;
1736 using Eigen::last;
1737
1738 #define CMD "Sandals::RungeKutta::adaptive_solve(...): "
1739
1740 // Check initial conditions
1741 if constexpr (Propagate) {
1742 if (!Jx.allFinite()) {
1743 SANDALS_ERROR(CMD "in " << this->m_tableau.name << " solver, initial Jacobian " <<
1744 "contains NaNs or Infs, aborting.");
1745 return false;
1746 }
1747 }
1748
1749 // Check if the adaptive method is enabled and the method is embedded
1750 if (!this->is_embedded()) {
1751 SANDALS_WARNING(CMD "the method is not embedded, using solve(...) method.");
1752 return this->solve(t_mesh, ics, sol);
1753 } else if (!this->m_adaptive) {
1754 SANDALS_WARNING(CMD "adaptive method is disabled, using solve(...) method.");
1755 return this->solve(t_mesh, ics, sol);
1756 }
1757
1758 // Instantiate output
1759 Real t_step{t_mesh(0)}, h_step{t_mesh(1) - t_mesh(0)}, h_new_step, scale{100.0};
1760 Real h_min{std::max(this->m_min_step, h_step/scale)}, h_max{scale*h_step};
1761 if (this->m_tableau.is_embedded) {
1762 Integer safety_length{static_cast<Integer>(std::ceil(std::abs(t_mesh(last) - t_mesh(0))/(2.0*h_min)))};
1763 sol.resize(safety_length);
1764 } else {
1765 sol.resize(t_mesh.size());
1766 }
1767
1768 // Store initial conditions
1769 sol.t(0) = t_mesh(0);
1770 sol.x.col(0) = ics;
1771 sol.h.col(0) = this->m_system->h(ics, t_mesh(0));
1772
1773 // Reset the derivative propagation matrix
1774 if constexpr (Propagate) {Jx.setIdentity();}
1775
1776 // Callback on initial conditions
1777 if (this->m_step_callback) {this->m_step_callback(0, ics, t_mesh(0));}
1778
1779 // Instantiate temporary variables
1780 Integer step{0};
1781 VectorN x_old_step(ics), x_new_step(ics);
1782 MatrixJX Jx_step;
1783
1784 while (true) {
1785 // Integrate system
1786 this->template advance<Propagate>(x_old_step, t_step, h_step, x_new_step, h_new_step, Jx_step);
1787
1788 // Update the current step
1789 t_step += h_step;
1790
1791 // Saturate the suggested timestep
1792 if (this->m_adaptive && this->m_tableau.is_embedded) {
1793 h_step = std::max(std::min(h_new_step, h_max), h_min);
1794 }
1795
1796 SANDALS_ASSERT(step < sol.size(), CMD "safety length exceeded.");
1797
1798 // Update temporaries
1799 step += 1;
1800
1801 // Update outputs
1802 sol.t(step) = t_step;
1803 sol.x.col(step) = x_new_step;
1804 sol.h.col(step) = this->m_system->h(x_new_step, t_step);
1805
1806 // Callback after the step is completed
1807 if (this->m_step_callback) {this->m_step_callback(step, x_new_step, t_step);}
1808
1809 // Check if the current step is the last one
1810 if (std::abs(t_step - t_mesh(last)) < SQRT_EPSILON) {break;}
1811 else if (t_step + h_step > t_mesh(last)) {h_step = t_mesh(last) - t_step;}
1812
1813 // Update the previous step
1814 x_old_step = x_new_step;
1815
1816 // Propagate the derivative of the solution with respect to the states x
1817 if constexpr (Propagate) {Jx *= Jx_step;}
1818 }
1819
1820 // Resize the output
1822
1823 return true;
1824
1825 #undef CMD
1826 }
1827
1840 bool adaptive_solve(VectorX const & t_mesh, VectorN const & ics, Solution<Real, N, M> & sol) const
1841 {
1842 MatrixJX Jx; // Dummy variable
1843 return this->template adaptive_solve<false>(t_mesh, ics, sol, Jx);
1844 }
1845
1855 bool project(VectorN const & x, Real const t, VectorN & x_projected) const
1856 {
1857 #define CMD "Sandals::RungeKutta::project(...): "
1858
1859 // Check if there are any constraints
1860 x_projected = x;
1861 if (M > Integer(0)) {
1862
1863 VectorM h;
1864 MatrixM Jh_x;
1865 VectorP b, x_step;
1866 MatrixP A;
1867 A.setZero();
1868 A.template block<N, N>(0, 0) = MatrixN::Identity();
1869 for (Integer k{0}; k < this->m_max_projection_iterations; ++k) {
1870
1871 /* Standard projection method
1872 [A] {x} = {b}
1873 / I Jh_x^T \ / dx \ / x_t - x_k \
1874 | | | | = | |
1875 \ Jh_x 0 / \ lambda / \ -h /
1876 */
1877
1878 // Evaluate the invariants vector and its Jacobian
1879 h = this->m_system->h(x_projected, t);
1880 Jh_x = this->m_system->Jh_x(x_projected, t);
1881
1882 // Check if the solution is found
1883 if (h.norm() < this->m_projection_tolerance) {return true;}
1884
1885 // Build the linear system
1886 A.template block<N, M>(0, N) = Jh_x.transpose();
1887 A.template block<M, N>(N, 0) = Jh_x;
1888 b.template head<N>() = x - x_projected;
1889 b.template tail<M>() = -h;
1890
1891 // Compute the solution of the linear system
1892 this->m_lu.compute(A);
1893 SANDALS_ASSERT(this->m_lu.rank() == N+M, CMD "singular Jacobian detected.");
1894 x_step = this->m_lu.solve(b);
1895
1896 // Check if the step is too small
1897 if (x_step.norm() < this->m_projection_tolerance * this->m_projection_tolerance) {return false;}
1898
1899 // Update the solution
1900 x_projected.noalias() += x_step(Eigen::seqN(0, N));
1901 }
1902 if (this->m_verbose) {SANDALS_WARNING(CMD "maximum number of iterations reached.");}
1903 return false;
1904 } else {
1905 return true;
1906 }
1907
1908 #undef CMD
1909 }
1910
1922 bool project_ics(VectorN const & x, Real const t, std::vector<Integer> const & projected_equations,
1923 std::vector<Integer> const & projected_invariants, VectorN & x_projected) const
1924 {
1925 #define CMD "Sandals::RungeKutta::project_ics(...): "
1926
1927 Integer X{static_cast<Integer>(projected_equations.size())};
1928 Integer H{static_cast<Integer>(projected_invariants.size())};
1929
1930 // Check if there are any constraints
1931 x_projected = x;
1932 if (H > Integer(0)) {
1933
1934 VectorM h;
1935 MatrixM Jh_x;
1936 VectorX b(X+H), x_step(X+H);
1937 MatrixX A(X+H, X+H);
1938 A.setZero();
1939 A.block(0, 0, X, X) = MatrixX::Identity(X+H, X+H);
1940 Eigen::FullPivLU<MatrixX> lu;
1941 for (Integer k{0}; k < this->m_max_projection_iterations; ++k) {
1942
1943 /* Standard projection method
1944 [A] {x} = {b}
1945 / I Jh_x^T \ / dx \ / x_t - x_k \
1946 | | | | = | |
1947 \ Jh_x 0 / \ lambda / \ -h /
1948 */
1949
1950 // Evaluate the invariants vector and its Jacobian
1951 h = this->m_system->h(x_projected, t);
1952 Jh_x = this->m_system->Jh_x(x_projected, t);
1953
1954 // Select only the projected invariants
1955 h = h(projected_invariants);
1956 Jh_x = Jh_x(projected_invariants, projected_equations);
1957
1958 // Check if the solution is found
1959 if (h.norm() < this->m_projection_tolerance) {return true;}
1960
1961 // Build the linear system
1962 A.block(0, X, X, H) = Jh_x.transpose();
1963 A.block(X, 0, H, X) = Jh_x;
1964 b.head(X) = x(projected_equations) - x_projected(projected_equations);
1965 b.tail(H) = -h;
1966
1967 // Compute the solution of the linear system
1968 lu.compute(A);
1969 SANDALS_ASSERT(lu.rank() == X+H, CMD "singular Jacobian detected.");
1970 x_step = this->m_lu.solve(b);
1971
1972 // Check if the step is too small
1973 if (x_step.norm() < this->m_projection_tolerance * this->m_projection_tolerance) {return false;}
1974
1975 // Update the solution
1976 x_projected(projected_equations).noalias() += x_step;
1977 }
1978 if (this->m_verbose) {SANDALS_WARNING(CMD "maximum number of iterations reached.");}
1979 return false;
1980 } else {
1981 return true;
1982 }
1983
1984 #undef CMD
1985 }
1986
1994 Real estimate_order(std::vector<VectorX> const & t_mesh, VectorN const & ics, std::function<MatrixX(VectorX)> & sol) const
1995 {
1996 using Eigen::last;
1997
1998 #define CMD "Sandals::RungeKutta::estimate_order(...): "
1999
2000 SANDALS_ASSERT(t_mesh.size() > Integer(1), CMD "expected at least two time meshes.");
2001
2002 for (Integer i{0}; i < static_cast<Integer>(t_mesh.size()); ++i) {
2003
2004 // Check if the time mesh has the same initial and final time
2005 SANDALS_ASSERT((t_mesh[0](0) - t_mesh[i](0)) < SQRT_EPSILON,
2006 CMD "expected the same initial time.");
2007 SANDALS_ASSERT((t_mesh[0](last) - t_mesh[i](last)) < SQRT_EPSILON,
2008 CMD "expected the same final time.");
2009
2010 // Check if the mesh step is fixed all over the time mesh
2011 for (Integer j{1}; j < static_cast<Integer>(t_mesh[i].size()); ++j) {
2012 SANDALS_ASSERT((t_mesh[i](j) - t_mesh[i](j-1)) - (t_mesh[i](1) - t_mesh[i](0)) < SQRT_EPSILON,
2013 CMD "expected a fixed step.");
2014 }
2015 }
2016
2017 // Solve the system for each time scale
2018 Solution<Real, N, M> sol_num;
2019 MatrixX sol_ana;
2020 VectorX h_vec(t_mesh.size()), e_vec(t_mesh.size());
2021 for (Integer i{0}; i < static_cast<Integer>(t_mesh.size()); ++i) {
2022 SANDALS_ASSERT(this->solve(t_mesh[i], ics, sol_num), CMD "failed to solve the system for " <<
2023 "the" << i << "-th time mesh.");
2024 sol_ana = sol(sol_num.t);
2025 SANDALS_ASSERT(sol_ana.rows() == sol_num.x.rows(),
2026 CMD "expected the same number of states in analytical solution.");
2027 SANDALS_ASSERT(sol_ana.cols() == sol_num.x.cols(),
2028 CMD "expected the same number of steps in analytical solution.");
2029 h_vec(i) = std::abs(sol_num.t(1) - sol_num.t(0));
2030 e_vec(i) = (sol_ana - sol_num.x).array().abs().maxCoeff();
2031 }
2032
2033 // Compute the order of the method thorugh least squares
2034 VectorX A(h_vec.array().log());
2035 VectorX b(e_vec.array().log());
2036 return ((A.transpose() * A).ldlt().solve(A.transpose() * b))(0);
2037
2038 #undef CMD
2039 }
2040
2041 }; // class RungeKutta
2042
2043} // namespace Sandals
2044
2045#endif // SANDALS_RUNGEKUTTA_HH
#define CMD
#define SANDALS_BASIC_CONSTANTS(Real)
Definition Sandals.hh:70
#define SANDALS_ERROR(MSG)
Definition Sandals.hh:34
#define SANDALS_ASSERT(COND, MSG)
Definition Sandals.hh:44
#define SANDALS_WARNING(MSG)
Definition Sandals.hh:53
Class container for the system of explicit ODEs.
Definition Explicit.hh:42
MatrixJF Jf_x_reverse(VectorF const &x, Real const t) const
Definition Explicit.hh:170
virtual VectorF f(VectorF const &x, Real const t) const =0
virtual MatrixJF Jf_x(VectorF const &x, Real const t) const =0
VectorF f_reverse(VectorF const &x, Real const t) const
Definition Explicit.hh:158
static const FunctionH DefaultH
Definition Explicit.hh:254
std::function< MatrixJF(VectorF const &, Real const)> FunctionJF
Definition Explicit.hh:249
static const FunctionID DefaultID
Definition Explicit.hh:256
std::function< VectorH(VectorF const &, Real const)> FunctionH
Definition Explicit.hh:250
std::function< bool(VectorF const &, Real const)> FunctionID
Definition Explicit.hh:252
static const FunctionJH DefaultJH
Definition Explicit.hh:255
std::function< MatrixJH(VectorF const &, Real const)> FunctionJH
Definition Explicit.hh:251
std::function< VectorF(VectorF const &, Real const)> FunctionF
Definition Explicit.hh:248
Eigen::Matrix< Real, N, N > MatrixJF
Definition Implicit.hh:47
Eigen::Vector< Real, N > VectorF
Definition Implicit.hh:46
Eigen::Vector< Real, M > VectorH
Definition Implicit.hh:48
std::shared_ptr< Implicit< Real, N, M > > Pointer
Definition Implicit.hh:45
Eigen::Matrix< Real, M, N > MatrixJH
Definition Implicit.hh:49
std::function< bool(VectorF const &, Real const)> FunctionID
Definition Implicit.hh:279
std::function< VectorF(VectorF const &, VectorF const &, Real const)> FunctionF
Definition Implicit.hh:275
std::function< MatrixJF(VectorF const &, VectorF const &, Real const)> FunctionJF
Definition Implicit.hh:276
std::function< VectorH(VectorF const &, Real const)> FunctionH
Definition Implicit.hh:277
std::function< MatrixJH(VectorF const &, Real const)> FunctionJH
Definition Implicit.hh:278
static const FunctionID DefaultID
Definition Implicit.hh:283
static const FunctionH DefaultH
Definition Implicit.hh:281
static const FunctionJH DefaultJH
Definition Implicit.hh:282
std::function< VectorB(Real const)> FunctionB
Definition Linear.hh:212
static const FunctionH DefaultH
Definition Linear.hh:217
std::function< MatrixE(Real const)> FunctionE
Definition Linear.hh:210
static const FunctionJH DefaultJH
Definition Linear.hh:218
static const FunctionID DefaultID
Definition Linear.hh:219
std::function< MatrixJH(VectorF const &, Real const)> FunctionJH
Definition Linear.hh:214
std::function< bool(VectorF const &, Real const)> FunctionID
Definition Linear.hh:215
std::function< MatrixA(Real const)> FunctionA
Definition Linear.hh:211
std::function< VectorH(VectorF const &, Real const)> FunctionH
Definition Linear.hh:213
Real & min_step()
Definition RungeKutta.hh:471
bool projection()
Definition RungeKutta.hh:603
bool adaptive_mode()
Definition RungeKutta.hh:495
bool is_erk() const
Definition RungeKutta.hh:147
typename Tableau< Real, S >::Matrix MatrixS
Definition RungeKutta.hh:69
Real estimate_order(std::vector< VectorX > const &t_mesh, VectorN const &ics, std::function< MatrixX(VectorX)> &sol) const
Definition RungeKutta.hh:1994
void enable_reverse_mode()
Definition RungeKutta.hh:554
void projection(bool t_projection)
Definition RungeKutta.hh:609
void dirk_function(Integer n, VectorN const &x, Real const t, Real const h, MatrixK const &K, VectorN &fun) const
Definition RungeKutta.hh:1244
typename Tableau< Real, S >::Vector VectorS
Definition RungeKutta.hh:68
Real & min_safety_factor()
Definition RungeKutta.hh:447
void semi_explicit_system(typename SemiExplicitWrapper< Real, N, M >::FunctionA A, typename SemiExplicitWrapper< Real, N, M >::FunctionTA TA_x, typename SemiExplicitWrapper< Real, N, M >::FunctionB b, typename SemiExplicitWrapper< Real, N, M >::FunctionJB Jb_x, typename SemiExplicitWrapper< Real, N, M >::FunctionH h=SemiExplicitWrapper< Real, N, M >::DefaultH, typename SemiExplicitWrapper< Real, N, M >::FunctionJH Jh_x=SemiExplicitWrapper< Real, N, M >::DefaultJH, typename SemiExplicitWrapper< Real, N, M >::FunctionID in_domain=SemiExplicitWrapper< Real, N, M >::DefaultID)
Definition RungeKutta.hh:365
bool propagate(VectorN const &x, Real const t, Real const h, MatrixK const &K, MatrixJX &Jx) const
Definition RungeKutta.hh:1456
bool reverse_mode()
Definition RungeKutta.hh:543
typename Implicit< Real, N, M >::MatrixJF MatrixN
Definition RungeKutta.hh:71
void erk_implicit_function(Integer const s, VectorN const &x, Real const t, Real const h, MatrixK const &K, VectorN &fun) const
Definition RungeKutta.hh:822
Real & safety_factor()
Definition RungeKutta.hh:435
Real m_absolute_tolerance
Definition RungeKutta.hh:91
Real m_min_safety_factor
Definition RungeKutta.hh:94
RungeKutta & operator=(RungeKutta const &)=delete
bool irk_propagate(VectorN const &x, Real const t, Real const h, MatrixK const &K, MatrixJX &Jx) const
Definition RungeKutta.hh:1164
typename Implicit< Real, N, M >::MatrixJH MatrixM
Definition RungeKutta.hh:73
Eigen::Matrix< Real, N+M, 1 > VectorP
Definition RungeKutta.hh:64
Tableau< Real, S > & tableau()
Definition RungeKutta.hh:165
System system()
Definition RungeKutta.hh:225
VectorS b_embedded() const
Definition RungeKutta.hh:213
RungeKutta(const RungeKutta &)=delete
bool solve(VectorX const &t_mesh, VectorN const &ics, Solution< Real, N, M > &sol, MatrixJX &Jx) const
Definition RungeKutta.hh:1616
FunctionSC step_callback()
Definition RungeKutta.hh:565
typename Implicit< Real, N, M >::Pointer System
Definition RungeKutta.hh:80
bool step(VectorN const &x_old, Real const t_old, Real const h_old, VectorN &x_new, Real &h_new, MatrixK &K) const
Definition RungeKutta.hh:1421
void enable_projection()
Definition RungeKutta.hh:614
Eigen::Matrix< Real, N *N, S > MatrixJK
Definition RungeKutta.hh:62
RungeKutta(Tableau< Real, S > const &t_tableau, System t_system)
Definition RungeKutta.hh:132
Real m_relative_tolerance
Definition RungeKutta.hh:92
Integer & max_substeps()
Definition RungeKutta.hh:483
typename Implicit< Real, N, M >::VectorF VectorN
Definition RungeKutta.hh:70
void linear_system(std::string name, typename LinearWrapper< Real, N, M >::FunctionE E, typename LinearWrapper< Real, N, M >::FunctionA A, typename LinearWrapper< Real, N, M >::FunctionB b, typename LinearWrapper< Real, N, M >::FunctionH h=LinearWrapper< Real, N, M >::DefaultH, typename LinearWrapper< Real, N, M >::FunctionJH Jh_x=LinearWrapper< Real, N, M >::DefaultJH, typename LinearWrapper< Real, N, M >::FunctionID in_domain=LinearWrapper< Real, N, M >::DefaultID)
Definition RungeKutta.hh:343
RungeKutta(Tableau< Real, S > const &t_tableau)
Definition RungeKutta.hh:122
Eigen::Matrix< Real, N+M, N+M > MatrixP
Definition RungeKutta.hh:65
void adaptive(bool t_adaptive)
Definition RungeKutta.hh:501
Integer order() const
Definition RungeKutta.hh:189
void projection_tolerance(Real const t_projection_tolerance)
Definition RungeKutta.hh:583
Eigen::Matrix< Real, N *S, N *S > MatrixJ
Definition RungeKutta.hh:63
void irk_jacobian(VectorN const &x, Real const t, Real const h, VectorK const &K, MatrixJ &jac) const
Definition RungeKutta.hh:1067
VectorS b() const
Definition RungeKutta.hh:207
typename Tableau< Real, S >::Type Type
Definition RungeKutta.hh:81
bool erk_explicit_step(VectorN const &x_old, Real const t_old, Real const h_old, VectorN &x_new, Real &h_new, MatrixK &K) const
Definition RungeKutta.hh:722
NewtonX m_newtonX
Definition RungeKutta.hh:85
bool dirk_propagate(VectorN const &x, Real const t, Real const h, MatrixK const &K, MatrixJX &Jx) const
Definition RungeKutta.hh:1365
static constexpr Integer stages()
Definition RungeKutta.hh:177
bool is_dirk() const
Definition RungeKutta.hh:159
VectorS c() const
Definition RungeKutta.hh:219
void reverse(bool t_reverse)
Definition RungeKutta.hh:549
bool m_reverse
Definition RungeKutta.hh:100
bool m_adaptive
Definition RungeKutta.hh:98
void max_safety_factor(Real const t_max_safety_factor)
Definition RungeKutta.hh:465
Eigen::Vector< Real, Eigen::Dynamic > VectorX
Definition RungeKutta.hh:55
std::string name() const
Definition RungeKutta.hh:183
Real m_min_step
Definition RungeKutta.hh:96
bool adaptive_solve(VectorX const &t_mesh, VectorN const &ics, Solution< Real, N, M > &sol, MatrixJX &Jx) const
Definition RungeKutta.hh:1732
void implicit_system(typename ImplicitWrapper< Real, N, M >::FunctionF F, typename ImplicitWrapper< Real, N, M >::FunctionJF JF_x, typename ImplicitWrapper< Real, N, M >::FunctionJF JF_x_dot, typename ImplicitWrapper< Real, N, M >::FunctionH h=ImplicitWrapper< Real, N, M >::DefaultH, typename ImplicitWrapper< Real, N, M >::FunctionJH Jh_x=ImplicitWrapper< Real, N, M >::DefaultJH, typename ImplicitWrapper< Real, N, M >::FunctionID in_domain=ImplicitWrapper< Real, N, M >::DefaultID)
Definition RungeKutta.hh:242
void semi_explicit_system(std::string name, typename SemiExplicitWrapper< Real, N, M >::FunctionA A, typename SemiExplicitWrapper< Real, N, M >::FunctionTA TA_x, typename SemiExplicitWrapper< Real, N, M >::FunctionB b, typename SemiExplicitWrapper< Real, N, M >::FunctionJB Jb_x, typename SemiExplicitWrapper< Real, N, M >::FunctionH h=SemiExplicitWrapper< Real, N, M >::DefaultH, typename SemiExplicitWrapper< Real, N, M >::FunctionJH Jh_x=SemiExplicitWrapper< Real, N, M >::DefaultJH, typename SemiExplicitWrapper< Real, N, M >::FunctionID in_domain=SemiExplicitWrapper< Real, N, M >::DefaultID)
Definition RungeKutta.hh:388
void enable_adaptive_mode()
Definition RungeKutta.hh:506
Real projection_tolerance()
Definition RungeKutta.hh:577
void verbose_mode(bool t_verbose)
Definition RungeKutta.hh:523
Real real_type
Definition RungeKutta.hh:54
std::function< void(Integer const, VectorX const &, Real const)> FunctionSC
Definition RungeKutta.hh:74
void absolute_tolerance(Real const t_absolute_tolerance)
Definition RungeKutta.hh:417
void erk_implicit_jacobian(Integer const s, VectorN const &x, Real const t, Real const h, MatrixK const &K, MatrixN &jac) const
Definition RungeKutta.hh:866
Real relative_tolerance()
Definition RungeKutta.hh:423
bool verbose_mode()
Definition RungeKutta.hh:517
void linear_system(typename LinearWrapper< Real, N, M >::FunctionE E, typename LinearWrapper< Real, N, M >::FunctionA A, typename LinearWrapper< Real, N, M >::FunctionB b, typename LinearWrapper< Real, N, M >::FunctionH h=LinearWrapper< Real, N, M >::DefaultH, typename LinearWrapper< Real, N, M >::FunctionJH Jh_x=LinearWrapper< Real, N, M >::DefaultJH, typename LinearWrapper< Real, N, M >::FunctionID in_domain=LinearWrapper< Real, N, M >::DefaultID)
Definition RungeKutta.hh:322
const Real SQRT_EPSILON
Definition RungeKutta.hh:78
void explicit_system(typename ExplicitWrapper< Real, N, M >::FunctionF f, typename ExplicitWrapper< Real, N, M >::FunctionJF Jf_x, typename ExplicitWrapper< Real, N, M >::FunctionH h=ExplicitWrapper< Real, N, M >::DefaultH, typename ExplicitWrapper< Real, N, M >::FunctionJH Jh_x=ExplicitWrapper< Real, N, M >::DefaultJH, typename ExplicitWrapper< Real, N, M >::FunctionID in_domain=ExplicitWrapper< Real, N, M >::DefaultID)
Definition RungeKutta.hh:283
Eigen::FullPivLU< MatrixP > m_lu
Definition RungeKutta.hh:87
bool erk_explicit_propagate(VectorN const &x, Real const t, Real const h, MatrixK const &K, MatrixJX &Jx) const
Definition RungeKutta.hh:764
System m_system
Definition RungeKutta.hh:90
void min_safety_factor(Real const t_min_safety_factor)
Definition RungeKutta.hh:453
Tableau< Real, S > m_tableau
Definition RungeKutta.hh:89
bool solve(VectorX const &t_mesh, VectorN const &ics, Solution< Real, N, M > &sol) const
Definition RungeKutta.hh:1708
bool adaptive_solve(VectorX const &t_mesh, VectorN const &ics, Solution< Real, N, M > &sol) const
Definition RungeKutta.hh:1840
void relative_tolerance(Real const t_relative_tolerance)
Definition RungeKutta.hh:429
void max_substeps(Integer const t_max_substeps)
Definition RungeKutta.hh:489
void min_step(Real const t_min_step)
Definition RungeKutta.hh:477
Eigen::Matrix< Real, N, S > MatrixK
Definition RungeKutta.hh:61
void enable_verbose_mode()
Definition RungeKutta.hh:532
Integer & max_projection_iterations()
Definition RungeKutta.hh:590
bool is_irk() const
Definition RungeKutta.hh:153
Optimist::RootFinder::Newton< Real, N > NewtonX
Definition RungeKutta.hh:66
void step_callback(FunctionSC const &t_step_callback)
Definition RungeKutta.hh:571
bool erk_implicit_step(VectorN const &x_old, Real const t_old, Real const h_old, VectorN &x_new, Real &h_new, MatrixK &K) const
Definition RungeKutta.hh:893
Real m_projection_tolerance
Definition RungeKutta.hh:103
Eigen::Matrix< Real, N, N > MatrixJX
Definition RungeKutta.hh:56
FunctionSC m_step_callback
Definition RungeKutta.hh:101
Real estimate_step(VectorN const &x, VectorN const &x_e, Real const h_k) const
Definition RungeKutta.hh:655
void dirk_jacobian(Integer n, VectorN const &x, Real const t, Real const h, MatrixK const &K, MatrixN &jac) const
Definition RungeKutta.hh:1290
void system(System t_system)
Definition RungeKutta.hh:231
void disable_adaptive_mode()
Definition RungeKutta.hh:511
Tableau< Real, S > const & tableau() const
Definition RungeKutta.hh:171
Real m_max_safety_factor
Definition RungeKutta.hh:95
bool erk_implicit_propagate(VectorN const &x, Real const t, Real const h, MatrixK const &K, MatrixJX &Jx) const
Definition RungeKutta.hh:937
bool m_verbose
Definition RungeKutta.hh:99
Real m_safety_factor
Definition RungeKutta.hh:93
bool project_ics(VectorN const &x, Real const t, std::vector< Integer > const &projected_equations, std::vector< Integer > const &projected_invariants, VectorN &x_projected) const
Definition RungeKutta.hh:1922
void disable_reverse_mode()
Definition RungeKutta.hh:559
bool dirk_step(VectorN const &x_old, Real const t_old, Real const h_old, VectorN &x_new, Real &h_new, MatrixK &K) const
Definition RungeKutta.hh:1321
Eigen::Vector< Real, N *S > VectorK
Definition RungeKutta.hh:60
void info(std::ostream &os)
Definition RungeKutta.hh:697
Type type() const
Definition RungeKutta.hh:141
void disable_projection()
Definition RungeKutta.hh:619
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic > MatrixX
Definition RungeKutta.hh:59
void max_projection_iterations(Integer const t_max_projection_iterations)
Definition RungeKutta.hh:596
void safety_factor(Real const t_safety_factor)
Definition RungeKutta.hh:441
Eigen::Vector< Real, Eigen::Dynamic > Time
Definition RungeKutta.hh:82
void disable_verbose_mode()
Definition RungeKutta.hh:537
Integer m_max_substeps
Definition RungeKutta.hh:97
bool is_embedded() const
Definition RungeKutta.hh:195
std::string info() const
Definition RungeKutta.hh:669
void explicit_system(std::string name, typename ExplicitWrapper< Real, N, M >::FunctionF f, typename ExplicitWrapper< Real, N, M >::FunctionJF Jf_x, typename ExplicitWrapper< Real, N, M >::FunctionH h=ExplicitWrapper< Real, N, M >::DefaultH, typename ExplicitWrapper< Real, N, M >::FunctionJH Jh_x=ExplicitWrapper< Real, N, M >::DefaultJH, typename ExplicitWrapper< Real, N, M >::FunctionID in_domain=ExplicitWrapper< Real, N, M >::DefaultID)
Definition RungeKutta.hh:302
bool has_system()
Definition RungeKutta.hh:405
void irk_function(VectorN const &x, Real const t, Real const h, VectorK const &K, VectorK &fun) const
Definition RungeKutta.hh:1015
typename Implicit< Real, N, M >::VectorH VectorM
Definition RungeKutta.hh:72
MatrixS A() const
Definition RungeKutta.hh:201
Real absolute_tolerance()
Definition RungeKutta.hh:411
Optimist::RootFinder::Newton< Real, N *S > NewtonK
Definition RungeKutta.hh:67
Integer m_max_projection_iterations
Definition RungeKutta.hh:104
bool project(VectorN const &x, Real const t, VectorN &x_projected) const
Definition RungeKutta.hh:1855
bool advance(VectorN const &x_old, Real const t_old, Real const h_old, VectorN &x_new, Real &h_new, MatrixJX &Jx) const
Definition RungeKutta.hh:1491
NewtonK m_newtonK
Definition RungeKutta.hh:86
void implicit_system(std::string name, typename ImplicitWrapper< Real, N, M >::FunctionF F, typename ImplicitWrapper< Real, N, M >::FunctionJF JF_x, typename ImplicitWrapper< Real, N, M >::FunctionJF JF_x_dot, typename ImplicitWrapper< Real, N, M >::FunctionH h=ImplicitWrapper< Real, N, M >::DefaultH, typename ImplicitWrapper< Real, N, M >::FunctionJH Jh_x=ImplicitWrapper< Real, N, M >::DefaultJH, typename ImplicitWrapper< Real, N, M >::FunctionID in_domain=ImplicitWrapper< Real, N, M >::DefaultID)
Definition RungeKutta.hh:263
Real & max_safety_factor()
Definition RungeKutta.hh:459
bool irk_step(VectorN const &x_old, Real const t_old, Real const h_old, VectorN &x_new, Real &h_new, MatrixK &K) const
Definition RungeKutta.hh:1121
bool m_projection
Definition RungeKutta.hh:105
std::function< MatrixJB(VectorF const &, Real const)> FunctionJB
Definition SemiExplicit.hh:273
static const FunctionH DefaultH
Definition SemiExplicit.hh:278
std::function< bool(VectorF const &, Real const)> FunctionID
Definition SemiExplicit.hh:276
std::function< VectorH(VectorF const &, Real const)> FunctionH
Definition SemiExplicit.hh:274
std::function< VectorB(VectorF const &, Real const)> FunctionB
Definition SemiExplicit.hh:272
std::function< TensorTA(VectorF const &, Real const)> FunctionTA
Definition SemiExplicit.hh:271
std::function< MatrixA(VectorF const &, Real const)> FunctionA
Definition SemiExplicit.hh:270
static const FunctionJH DefaultJH
Definition SemiExplicit.hh:279
static const FunctionID DefaultID
Definition SemiExplicit.hh:280
std::function< MatrixJH(VectorF const &, Real const)> FunctionJH
Definition SemiExplicit.hh:275
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
Class container for the numerical solution of a system of ODEs/DAEs.
Definition Solution.hh:62
MatrixN x
Definition Solution.hh:68
Integer size() const
Definition Solution.hh:126
void resize(Integer const size)
Definition Solution.hh:87
void conservative_resize(Integer const size)
Definition Solution.hh:97
Vector t
Definition Solution.hh:67
MatrixM h
Definition Solution.hh:69
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
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
Vector c
Definition Tableau.hh:53
Vector b
Definition Tableau.hh:51
bool is_embedded
Definition Tableau.hh:54