0.5.0
Loading...
Searching...
No Matches
NumericalIntegration.hpp
Go to the documentation of this file.
1// This file is part of INSTINCT, the INS Toolkit for Integrated
2// Navigation Concepts and Training by the Institute of Navigation of
3// the University of Stuttgart, Germany.
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9/// @file NumericalIntegration.hpp
10/// @brief Provides Numerical integration methods
11/// @author T. Topp (topp@ins.uni-stuttgart.de)
12/// @date 2023-12-11
13
14#pragma once
15
16#include <array>
17#include <cmath>
18#include <numeric>
19#include <type_traits>
20#include <gcem.hpp>
21
22#include "util/Eigen.hpp"
23#include <unsupported/Eigen/MatrixFunctions>
24
26
27#include "util/Assert.h"
28#include "util/Logger.hpp"
29
30namespace NAV
31{
32
34{
35
36#if defined(__GNUC__) && !defined(__clang__)
37 #pragma GCC diagnostic push
38 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // NOLINT(clang-diagnostic-unknown-warning-option)
39#endif
40
41/// @brief Calculates explicit Runge-Kutta methods. Order is defined by the Butcher tableau
42/// @param[in] y_n State vector at time t_n
43/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
44/// @param[in] h Integration step in [s]
45/// @param[in] f Time derivative function
46/// @param[in] constParam Constant parameters passed to each time derivative function call
47/// @param[in] t_n Time t_n
48/// @return State vector at time t_(n+1)
49template<typename Y, typename Z, std::floating_point Scalar, size_t s, std::array<std::array<Scalar, s + 1>, s + 1> butcherTableau>
50inline Y RungeKuttaExplicit(const Y& y_n, const std::array<Z, s>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n)
51{
52 static_assert(gcem::abs(static_cast<Scalar>(1.0) - std::accumulate(butcherTableau[s].begin(), butcherTableau[s].end(), static_cast<Scalar>(0.0))) < 1e-8, // NOLINT(boost-use-ranges,modernize-use-ranges) // There is no ranges::accumulate
53 "The sum of the last row in the Butcher tableau has to be 1");
54 for (size_t r = 0; r <= s; ++r)
55 {
56 for (size_t c = r + 1; c <= s; ++c)
57 {
58 INS_ASSERT_USER_ERROR(butcherTableau.at(r).at(c) == 0.0, "All terms in the upper triangle have to be 0");
59 }
60 }
61
62 // std::string result = "y_(n+1) = y_n";
63
64 // std::string sum_b_str;
65 // std::string sum_b_val;
66 Y sum_b{};
67 std::array<Y, s> k{};
68 for (size_t i = 1; i <= s; ++i)
69 {
70 auto b = butcherTableau[s].at(i);
71 auto c = butcherTableau.at(i - 1)[0];
72 Y sum_a{};
73 // std::string sum_a_str;
74 // std::string sum_a_val;
75 for (size_t j = 2; j <= i; ++j)
76 {
77 auto a = butcherTableau.at(i - 1).at(j - 1);
78 if (j == 2) { sum_a = a * k.at(j - 2); }
79 else { sum_a += a * k.at(j - 2); }
80 // sum_a_str += fmt::format("a{}{} * k{}", i, j-1, j-1);
81 // sum_a_val += fmt::format("{:^3.1f} * k{}", a, j - 1);
82 // if (j < i) {
83 // sum_a_str += " + ";
84 // sum_a_val += " + ";
85 // }
86 }
87
88 if (i < 2) { k.at(i - 1) = f(y_n, z.at(i - 1), constParam, t_n + c * h); } // Do not add sum_a, because can have nan values
89 else { k.at(i - 1) = f(y_n + sum_a * h, z.at(i - 1), constParam, t_n + c * h); }
90 // if (sum_a_str.empty()) { sum_a_str = "0"; }
91 // if (sum_a_val.empty()) { sum_a_val = "0"; }
92 // fmt::println("k{} = f(t_n + c{} * h, y_n + ({}) * h)", i, i, sum_a_str);
93 // fmt::println("k{} = f(t_n + {:^3.1f} * h, y_n + ({:^{w}}) * h)", i, c, sum_a_val, fmt::arg("w",sum_a_str.length()));
94
95 if (i == 1) { sum_b = b * k.at(i - 1); }
96 else { sum_b += b * k.at(i - 1); }
97 // sum_b_str += fmt::format("b{} * k{}", i, i);
98 // sum_b_val += fmt::format("{:.2f} * k{}", butcherTableau[s].at(i), i);
99 // if (i < s)
100 // {
101 // sum_b_str += " + ";
102 // sum_b_val += " + ";
103 // }
104 }
105
106 // std::cout << result + fmt::format(" + h * ({})", sum_b_str) << std::endl;
107 // std::cout << result + fmt::format(" + h * ({})", sum_b_val) << std::endl;
108 return y_n + h * sum_b;
109}
110
111/// @brief Calculates explicit Runge-Kutta methods. Order is defined by the Butcher tableau
112/// @param[in] y_n State vector at time t_n
113/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
114/// @param[in] h Integration step in [s]
115/// @param[in] f Time derivative function
116/// @param[in] constParam Constant parameters passed to each time derivative function call
117/// @param[in] t_n Time t_n
118/// @return State vector at time t_(n+1)
119template<typename Derived, typename Z, std::floating_point Scalar, size_t s, std::array<std::array<Scalar, s + 1>, s + 1> butcherTableau>
120inline typename Derived::PlainObject RungeKuttaExplicitQuat(const Eigen::MatrixBase<Derived>& y_n, const std::array<Z, s>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n)
121{
122 static_assert(gcem::abs(static_cast<Scalar>(1.0) - std::accumulate(butcherTableau[s].begin(), butcherTableau[s].end(), static_cast<Scalar>(0.0))) < 1e-8, // NOLINT(boost-use-ranges,modernize-use-ranges) // There is no ranges::accumulate
123 "The sum of the last row in the Butcher tableau has to be 1");
124 for (size_t r = 0; r <= s; ++r)
125 {
126 for (size_t c = r + 1; c <= s; ++c)
127 {
128 INS_ASSERT_USER_ERROR(butcherTableau.at(r).at(c) == 0.0, "All terms in the upper triangle have to be 0");
129 }
130 }
131
132 // using DerivativeVector = Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime>;
133 using DerivativeVector = decltype(f(y_n, z.front(), constParam, t_n));
134 Eigen::Index N = y_n.rows() - 4;
135
136 // Calculates y_n + y_delta, but treats
137 // - the last 4 entries in y_n as a quaternion
138 // - the last 3 entries in y_delta as angular rate measurements
139 auto applyDerivative = [&]<typename Derived2>(const Eigen::MatrixBase<Derived2>& y_delta) {
140 Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime> y_nh;
141 if constexpr (Derived::RowsAtCompileTime == Eigen::Dynamic) { y_nh = Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime>::Zero(y_n.rows()); }
142 y_nh.head(N) = y_n.head(N) + y_delta.head(N);
143
144 Eigen::Map<Eigen::Quaternion<typename Derived::Scalar>>(y_nh.template tail<4>().data()) =
145 Eigen::Map<const Eigen::Quaternion<typename Derived::Scalar>>(y_n.template tail<4>().data())
146 // * math::expMapMatrix(y_delta.template tail<3>()); // matrix exponential (~2% faster than normal multiplication because no need for quaternion normalization afterwards)
147 * math::expMapQuat(y_delta.template tail<3>()); // quaternionic exponential (~3% faster than normal multiplication because no normalization and no exp matrix)
148 return y_nh;
149 };
150
151 // std::string result = "y_(n+1) = y_n";
152
153 // std::string sum_b_str;
154 // std::string sum_b_val;
155 DerivativeVector sum_b{};
156 std::array<DerivativeVector, s> k{};
157 for (size_t i = 1; i <= s; ++i)
158 {
159 auto b = butcherTableau[s].at(i);
160 auto c = butcherTableau.at(i - 1)[0];
161 DerivativeVector sum_a{};
162 // std::string sum_a_str;
163 // std::string sum_a_val;
164 for (size_t j = 2; j <= i; ++j)
165 {
166 auto a = butcherTableau.at(i - 1).at(j - 1);
167 if (j == 2) { sum_a = a * k.at(j - 2); }
168 else { sum_a += a * k.at(j - 2); }
169 // sum_a_str += fmt::format("a{}{} * k{}", i, j-1, j-1);
170 // sum_a_val += fmt::format("{:^3.1f} * k{}", a, j - 1);
171 // if (j < i) {
172 // sum_a_str += " + ";
173 // sum_a_val += " + ";
174 // }
175 }
176
177 if (i < 2) { k.at(i - 1) = f(y_n, z.at(i - 1), constParam, t_n + c * h); } // Do not add sum_a, because can have nan values
178 else { k.at(i - 1) = f(applyDerivative(sum_a * h), z.at(i - 1), constParam, t_n + c * h); }
179 // if (sum_a_str.empty()) { sum_a_str = "0"; }
180 // if (sum_a_val.empty()) { sum_a_val = "0"; }
181 // fmt::println("k{} = f(t_n + c{} * h, y_n + ({}) * h)", i, i, sum_a_str);
182 // fmt::println("k{} = f(t_n + {:^3.1f} * h, y_n + ({:^{w}}) * h)", i, c, sum_a_val, fmt::arg("w",sum_a_str.length()));
183
184 if (i == 1) { sum_b = b * k.at(i - 1); }
185 else { sum_b += b * k.at(i - 1); }
186 // sum_b_str += fmt::format("b{} * k{}", i, i);
187 // sum_b_val += fmt::format("{:.2f} * k{}", butcherTableau[s].at(i), i);
188 // if (i < s)
189 // {
190 // sum_b_str += " + ";
191 // sum_b_val += " + ";
192 // }
193 }
194
195 // std::cout << result + fmt::format(" + h * ({})", sum_b_str) << std::endl;
196 // std::cout << result + fmt::format(" + h * ({})", sum_b_val) << std::endl;
197 return applyDerivative(h * sum_b);
198}
199
200#if defined(__GNUC__) && !defined(__clang__)
201 #pragma GCC diagnostic pop
202#endif
203
204/// @brief Butcher tableau for Runge-Kutta 1st order (explicit) / (Forward) Euler method
205template<typename Scalar>
206constexpr std::array<std::array<Scalar, 2>, 2> RK1 = { { { 0.0, /*|*/ },
207 //------------------
208 { 0.0, /*|*/ 1.0 } } };
209
210/// @brief Butcher tableau for Runge-Kutta 2nd order (explicit) / Explicit midpoint method
211template<typename Scalar>
212constexpr std::array<std::array<Scalar, 3>, 3> RK2 = { { { 0.0, /*|*/ },
213 { 0.5, /*|*/ 0.5 },
214 //-----------------------
215 { 0.0, /*|*/ 0.0, 1.0 } } };
216
217/// @brief Butcher tableau for Heun's method (2nd order) (explicit)
218template<typename Scalar>
219constexpr std::array<std::array<Scalar, 3>, 3> Heun2 = { { { 0.0, /*|*/ },
220 { 1.0, /*|*/ 1.0 },
221 //-----------------------
222 { 0.0, /*|*/ 0.5, 0.5 } } };
223
224/// @brief Butcher tableau for Runge-Kutta 3rd order (explicit) / Simpson's rule
225template<typename Scalar>
226constexpr std::array<std::array<Scalar, 4>, 4> RK3 = { { { 0.0, /*|*/ },
227 { 0.5, /*|*/ 0.5 },
228 { 1.0, /*|*/ -1.0, 2.0 },
229 //----------------------------------------------
230 { 0.0, /*|*/ 1.0 / 6.0, 4.0 / 6.0, 1.0 / 6.0 } } };
231
232/// @brief Butcher tableau for Heun's method (3nd order) (explicit)
233template<typename Scalar>
234constexpr std::array<std::array<Scalar, 4>, 4> Heun3 = { { { 0.0, /* |*/ },
235 { 1.0 / 3.0, /*|*/ 1.0 / 3.0 },
236 { 2.0 / 3.0, /*|*/ 0.0, 2.0 / 3.0 },
237 //----------------------------------------
238 { 0.0, /*|*/ 1.0 / 4.0, 0.0, 3.0 / 4.0 } } };
239
240/// @brief Butcher tableau for Runge-Kutta 4th order (explicit)
241template<typename Scalar>
242constexpr std::array<std::array<Scalar, 5>, 5> RK4 = { { { 0.0, /*|*/ },
243 { 0.5, /*|*/ 0.5 },
244 { 0.5, /*|*/ 0.0, 0.5 },
245 { 1.0, /*|*/ 0.0, 0.0, 1.0 },
246 //------------------
247 { 0.0, /*|*/ 1.0 / 6.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 6.0 } } };
248
249} // namespace ButcherTableau
250
251/// @brief Runge-Kutta 1st order (explicit) / (Forward) Euler method
252/// \anchor eq-RungeKutta1-explicit \f{equation}{ \label{eq:eq-RungeKutta1-explicit}
253/// y_{n+1} = y_n + h f(t_n, y_n)
254/// \f}
255/// Butcher tableau:
256/// \anchor eq-RungeKutta1-explicit-bt \f{equation}{ \label{eq:eq-RungeKutta1-explicit-bt}
257/// \renewcommand\arraystretch{1.2}
258/// \begin{array}{c|c}
259/// 0 \\ \hline
260/// & 1 \\
261/// \end{array}
262/// \f}
263/// @param[in] y_n State vector at time t_n
264/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
265/// @param[in] h Integration step in [s]
266/// @param[in] f Time derivative function
267/// @param[in] constParam Constant parameters passed to each time derivative function call
268/// @param[in] t_n Time t_n
269template<typename Y, typename Z, std::floating_point Scalar>
270Y RungeKutta1(const Y& y_n, const std::array<Z, 1>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
271{
273}
274
275/// @brief Runge-Kutta 1st order (explicit) / (Forward) Euler method with Quaternion as last entry in state
276/// @param[in] y_n State vector at time t_n (last 4 entries are treated as a quaternion)
277/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
278/// @param[in] h Integration step in [s]
279/// @param[in] f Time derivative function
280/// @param[in] constParam Constant parameters passed to each time derivative function call
281/// @param[in] t_n Time t_n
282template<typename Y, typename Z, std::floating_point Scalar>
283Y RungeKutta1Quat(const Y& y_n, const std::array<Z, 1>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
284{
286}
287
288/// @brief Runge-Kutta 2nd order (explicit) / Explicit midpoint method
289/// \anchor eq-RungeKutta2-explicit \f{equation}{ \label{eq:eq-RungeKutta2-explicit}
290/// \begin{aligned}
291/// y_{n+1} &= y_n + h k_2 \\[1em]
292/// k_1 &= f(t_n, y_n) \\
293/// k_2 &= f(t_n + \frac{h}{2}, y_n + \frac{h}{2} k_1) \\
294/// \end{aligned}
295/// \f}
296/// Butcher tableau:
297/// \anchor eq-RungeKutta2-explicit-bt \f{equation}{ \label{eq:eq-RungeKutta2-explicit-bt}
298/// \renewcommand\arraystretch{1.2}
299/// \begin{array}{c|cc}
300/// 0 \\
301/// \frac{1}{2} & \frac{1}{2} \\ \hline
302/// & 0 & 1 \\
303/// \end{array}
304/// \f}
305/// @param[in] y_n State vector at time t_n
306/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
307/// @param[in] h Integration step in [s]
308/// @param[in] f Time derivative function
309/// @param[in] constParam Constant parameters passed to each time derivative function call
310/// @param[in] t_n Time t_n
311template<typename Y, typename Z, std::floating_point Scalar>
312Y RungeKutta2(const Y& y_n, const std::array<Z, 2>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
313{
315}
316
317/// @brief Runge-Kutta 2nd order (explicit) / Explicit midpoint method with Quaternion as last entry in state
318/// @param[in] y_n State vector at time t_n (last 4 entries are treated as a quaternion)
319/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
320/// @param[in] h Integration step in [s]
321/// @param[in] f Time derivative function
322/// @param[in] constParam Constant parameters passed to each time derivative function call
323/// @param[in] t_n Time t_n
324template<typename Y, typename Z, std::floating_point Scalar>
325Y RungeKutta2Quat(const Y& y_n, const std::array<Z, 2>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
326{
328}
329
330/// @brief Heun's method (2nd order) (explicit)
331/// \anchor eq-Heun2 \f{equation}{ \label{eq:eq-Heun2}
332/// \begin{aligned}
333/// y_{n+1} &= y_n + \frac{h}{2} (k_1 + k_2) \\[1em]
334/// k_1 &= f(t_n, y_n) \\
335/// k_2 &= f(t_n + h, y_n + h k_1) \\
336/// \end{aligned}
337/// \f}
338/// Butcher tableau:
339/// \anchor eq-Heun2-bt \f{equation}{ \label{eq:eq-Heun2-bt}
340/// \renewcommand\arraystretch{1.2}
341/// \begin{array}{c|cc}
342/// 0 \\
343/// 1 & 1 \\ \hline
344/// & \frac{1}{2} & \frac{1}{2} \\
345/// \end{array}
346/// \f}
347/// @param[in] y_n State vector at time t_n
348/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
349/// @param[in] h Integration step in [s]
350/// @param[in] f Time derivative function
351/// @param[in] constParam Constant parameters passed to each time derivative function call
352/// @param[in] t_n Time t_n
353template<typename Y, typename Z, std::floating_point Scalar>
354Y Heun2(const Y& y_n, const std::array<Z, 2>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
355{
357}
358
359/// @brief Heun's method (2nd order) (explicit) with Quaternion as last entry in state
360/// @param[in] y_n State vector at time t_n (last 4 entries are treated as a quaternion)
361/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
362/// @param[in] h Integration step in [s]
363/// @param[in] f Time derivative function
364/// @param[in] constParam Constant parameters passed to each time derivative function call
365/// @param[in] t_n Time t_n
366template<typename Y, typename Z, std::floating_point Scalar>
367Y Heun2Quat(const Y& y_n, const std::array<Z, 2>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
368{
370}
371
372/// @brief Runge-Kutta 3rd order (explicit) / Simpson's rule
373/// \anchor eq-RungeKutta3-explicit \f{equation}{ \label{eq:eq-RungeKutta3-explicit}
374/// \begin{aligned}
375/// y_{n+1} &= y_n + \frac{h}{6} ( k_1 + 4 k_2 + k_3 ) \\[1em]
376/// k_1 &= f(t_n, y_n) \\
377/// k_2 &= f(t_n + \frac{h}{2}, y_n + \frac{h}{2} k_1) \\
378/// k_3 &= f(t_n + h, y_n + h (-k_1 + 2 k_2)) \\
379/// \end{aligned}
380/// \f}
381/// Butcher tableau:
382/// \anchor eq-RungeKutta3-explicit-bt \f{equation}{ \label{eq:eq-RungeKutta3-explicit-bt}
383/// \renewcommand\arraystretch{1.2}
384/// \begin{array}{c|ccc}
385/// 0 \\
386/// \frac{1}{2} & \frac{1}{2} \\
387/// 1 & -1 & 2 \\ \hline
388/// & \frac{1}{6} & \frac{4}{6} & \frac{1}{6} \\
389/// \end{array}
390/// \f}
391/// @param[in] y_n State vector at time t_n
392/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
393/// @param[in] h Integration step in [s]
394/// @param[in] f Time derivative function
395/// @param[in] constParam Constant parameters passed to each time derivative function call
396/// @param[in] t_n Time t_n
397template<typename Y, typename Z, std::floating_point Scalar>
398Y RungeKutta3(const Y& y_n, const std::array<Z, 3>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
399{
401}
402
403/// @brief Runge-Kutta 3rd order (explicit) / Simpson's rule with Quaternion as last entry in state
404/// @param[in] y_n State vector at time t_n (last 4 entries are treated as a quaternion)
405/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
406/// @param[in] h Integration step in [s]
407/// @param[in] f Time derivative function
408/// @param[in] constParam Constant parameters passed to each time derivative function call
409/// @param[in] t_n Time t_n
410template<typename Y, typename Z, std::floating_point Scalar>
411Y RungeKutta3Quat(const Y& y_n, const std::array<Z, 3>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
412{
414}
415
416/// @brief Heun's method (3nd order) (explicit)
417/// \anchor eq-Heun3 \f{equation}{ \label{eq:eq-Heun3}
418/// \begin{aligned}
419/// y_{n+1} &= y_n + \frac{h}{4} (k_1 + 3 k_3) \\[1em]
420/// k_1 &= f(t_n, y_n) \\
421/// k_2 &= f(t_n + \frac{h}{3}, y_n + \frac{h}{3} k_1) \\
422/// k_3 &= f(t_n + \frac{2 h}{3}, y_n + \frac{2 h}{3} k_2) \\
423/// \end{aligned}
424/// \f}
425/// Butcher tableau:
426/// \anchor eq-Heun3-bt \f{equation}{ \label{eq:eq-Heun3-bt}
427/// \renewcommand\arraystretch{1.2}
428/// \begin{array}{c|ccc}
429/// 0 \\
430/// \frac{1}{3} & \frac{1}{3} \\
431/// \frac{2}{3} & 0 & \frac{2}{3} \\ \hline
432/// & \frac{1}{4} & 0 & \frac{3}{4} \\
433/// \end{array}
434/// \f}
435/// @param[in] y_n State vector at time t_n
436/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
437/// @param[in] h Integration step in [s]
438/// @param[in] f Time derivative function
439/// @param[in] constParam Constant parameters passed to each time derivative function call
440/// @param[in] t_n Time t_n
441template<typename Y, typename Z, std::floating_point Scalar>
442Y Heun3(const Y& y_n, const std::array<Z, 3>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
443{
445}
446
447/// @brief Heun's method (3nd order) (explicit) with Quaternion as last entry in state
448/// @param[in] y_n State vector at time t_n (last 4 entries are treated as a quaternion)
449/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
450/// @param[in] h Integration step in [s]
451/// @param[in] f Time derivative function
452/// @param[in] constParam Constant parameters passed to each time derivative function call
453/// @param[in] t_n Time t_n
454template<typename Y, typename Z, std::floating_point Scalar>
455Y Heun3Quat(const Y& y_n, const std::array<Z, 3>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
456{
458}
459
460/// @brief Runge-Kutta 4th order (explicit)
461/// \anchor eq-RungeKutta4-explicit \f{equation}{ \label{eq:eq-RungeKutta4-explicit}
462/// \begin{aligned}
463/// y_{n+1} &= y_n + \frac{h}{6} ( k_1 + 2 k_2 + 2 k_3 + k_4 ) \\[1em]
464/// k_1 &= f(t_n, y_n) \\
465/// k_2 &= f(t_n + \frac{h}{2}, y_n + \frac{h}{2} k_1) \\
466/// k_3 &= f(t_n + \frac{h}{2}, y_n + \frac{h}{2} k_2) \\
467/// k_4 &= f(t_n + h, y_n + h k_3) \\
468/// \end{aligned}
469/// \f}
470/// Butcher tableau:
471/// \anchor eq-RungeKutta4-explicit-bt \f{equation}{ \label{eq:eq-RungeKutta4-explicit-bt}
472/// \renewcommand\arraystretch{1.2}
473/// \begin{array}{c|cccc}
474/// 0 \\
475/// \frac{1}{2} & \frac{1}{2} \\
476/// \frac{1}{2} & 0 & \frac{1}{2} \\
477/// 1 & 0 & 0 & 1 \\ \hline
478/// & \frac{1}{6} & \frac{1}{3} & \frac{1}{3} & \frac{1}{6} \\
479/// \end{array}
480/// \f}
481/// @param[in] y_n State vector at time t_n
482/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
483/// @param[in] h Integration step in [s]
484/// @param[in] f Time derivative function
485/// @param[in] constParam Constant parameters passed to each time derivative function call
486/// @param[in] t_n Time t_n
487template<typename Y, typename Z, std::floating_point Scalar>
488Y RungeKutta4(const Y& y_n, const std::array<Z, 4>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
489{
491}
492
493/// @brief Runge-Kutta 4th order (explicit) with Quaternion as last entry in state
494/// @param[in] y_n State vector at time t_n (last 4 entries are treated as a quaternion)
495/// @param[in] z Array of measurements, one for each evaluation point of the Runge Kutta
496/// @param[in] h Integration step in [s]
497/// @param[in] f Time derivative function
498/// @param[in] constParam Constant parameters passed to each time derivative function call
499/// @param[in] t_n Time t_n
500template<typename Y, typename Z, std::floating_point Scalar>
501Y RungeKutta4Quat(const Y& y_n, const std::array<Z, 4>& z, const Scalar& h, const auto& f, const auto& constParam, const Scalar& t_n = 0)
502{
504}
505
506} // namespace NAV
Assertion helpers.
#define INS_ASSERT_USER_ERROR(_EXP, _MSG)
Assert function with message.
Definition Assert.h:21
Vector space operations.
Utility class for logging to console and file.
Simple Math functions.
Derived::PlainObject RungeKuttaExplicitQuat(const Eigen::MatrixBase< Derived > &y_n, const std::array< Z, s > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n)
Calculates explicit Runge-Kutta methods. Order is defined by the Butcher tableau.
constexpr std::array< std::array< Scalar, 5 >, 5 > RK4
Butcher tableau for Runge-Kutta 4th order (explicit)
Y RungeKuttaExplicit(const Y &y_n, const std::array< Z, s > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n)
Calculates explicit Runge-Kutta methods. Order is defined by the Butcher tableau.
constexpr std::array< std::array< Scalar, 4 >, 4 > Heun3
Butcher tableau for Heun's method (3nd order) (explicit)
constexpr std::array< std::array< Scalar, 3 >, 3 > Heun2
Butcher tableau for Heun's method (2nd order) (explicit)
constexpr std::array< std::array< Scalar, 4 >, 4 > RK3
Butcher tableau for Runge-Kutta 3rd order (explicit) / Simpson's rule.
constexpr std::array< std::array< Scalar, 3 >, 3 > RK2
Butcher tableau for Runge-Kutta 2nd order (explicit) / Explicit midpoint method.
constexpr std::array< std::array< Scalar, 2 >, 2 > RK1
Butcher tableau for Runge-Kutta 1st order (explicit) / (Forward) Euler method.
Eigen::Quaternion< typename Derived::Scalar > expMapQuat(const Eigen::MatrixBase< Derived > &v)
Calculates the quaternionic exponential map of the given vector.
Definition Math.hpp:139
Y RungeKutta3(const Y &y_n, const std::array< Z, 3 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Runge-Kutta 3rd order (explicit) / Simpson's rule .
Y Heun3Quat(const Y &y_n, const std::array< Z, 3 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Heun's method (3nd order) (explicit) with Quaternion as last entry in state.
Y Heun3(const Y &y_n, const std::array< Z, 3 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Heun's method (3nd order) (explicit) .
Y RungeKutta1(const Y &y_n, const std::array< Z, 1 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Runge-Kutta 1st order (explicit) / (Forward) Euler method .
Y RungeKutta2Quat(const Y &y_n, const std::array< Z, 2 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Runge-Kutta 2nd order (explicit) / Explicit midpoint method with Quaternion as last entry in state.
Y RungeKutta4(const Y &y_n, const std::array< Z, 4 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Runge-Kutta 4th order (explicit) .
Y RungeKutta2(const Y &y_n, const std::array< Z, 2 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Runge-Kutta 2nd order (explicit) / Explicit midpoint method .
Y RungeKutta4Quat(const Y &y_n, const std::array< Z, 4 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Runge-Kutta 4th order (explicit) with Quaternion as last entry in state.
Y RungeKutta1Quat(const Y &y_n, const std::array< Z, 1 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Runge-Kutta 1st order (explicit) / (Forward) Euler method with Quaternion as last entry in state.
Y Heun2(const Y &y_n, const std::array< Z, 2 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Heun's method (2nd order) (explicit) .
Y Heun2Quat(const Y &y_n, const std::array< Z, 2 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Heun's method (2nd order) (explicit) with Quaternion as last entry in state.
Y RungeKutta3Quat(const Y &y_n, const std::array< Z, 3 > &z, const Scalar &h, const auto &f, const auto &constParam, const Scalar &t_n=0)
Runge-Kutta 3rd order (explicit) / Simpson's rule with Quaternion as last entry in state.