Brick
Loading...
Searching...
No Matches
numerical_integration.h
1#pragma once
2
3#include <Eigen/Dense>
4#include <functional>
5
6/**
7 * This header provides a variety of methods for solving ODEs depending on the
8 * needs of the system. First, second, and fourth order methods are
9 * provided, with first order being the fastest and least accurate, and fourth
10 * order being the slowest but most accurate.
11 *
12 * Each method also provides a function for solving time-invariant ODEs with
13 * and without an input u:
14 * dx/dt = f(x, u)
15 * dx/dt = f(x)
16 * and time-variant ODEs:
17 * dy/dt = f(t, y)
18 *
19 * Each function here takes a std::function as an input. This must be a vector
20 * valued function where x is some Eigen::Vector<double, X>, u is some
21 * Eigen::Vector<double, U>, and t is a double.
22 *
23 * The template arguments are determined by the compiler as long as they are
24 * valid, so you do not need to explicity state them when calling a function.
25 *
26 * To learn about Runge-Kutta methods in general read:
27 * https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods
28 *
29 * To see the specific methods used here read:
30 * https://en.wikipedia.org/wiki/List_of_Runge%E2%80%93Kutta_methods
31 */
32
33template <int X, int U>
34using WithInputDerivative = std::function<
35 Eigen::Vector<double, X>(const Eigen::Vector<double, X>&, const Eigen::Vector<double, U>&)>;
36
37template <int X>
38using WithoutInputDerivative =
39 std::function<Eigen::Vector<double, X>(const Eigen::Vector<double, X>&)>;
40
41template <int Y>
42using TimeVariantDerivative =
43 std::function<Eigen::Vector<double, Y>(const double&, const Eigen::Vector<double, Y>&)>;
44
45/**
46 * Performs first order numerical integration of the time-invariant differential
47 * equation dx/dt = f(x, u) using Euler's method.
48 *
49 * 0|
50 * ---|---
51 * | 1
52 *
53 * @param f The function to integrate, with two arguments x and u.
54 * @param x The initial value of x.
55 * @param u The input value u held constant over the integration period.
56 * @param h The time over which to integrate.
57 */
58template <int X, int U>
59Eigen::Vector<double, X> euler_with_input(
60 const WithInputDerivative<X, U>& f, const Eigen::Vector<double, X>& x,
61 const Eigen::Vector<double, U>& u, const double& h
62) {
63 Eigen::Vector<double, X> k1 = f(x, u);
64
65 return x + h * k1;
66}
67
68/**
69 * Performs first order numerical integration of the time-invariant differential
70 * equation dx/dt = f(x) using Euler's method.
71 *
72 * 0|
73 * ---|---
74 * | 1
75 *
76 * @param f The function to integrate, with one argument x.
77 * @param x The initial value of x.
78 * @param h The time over which to integrate.
79 */
80template <int X>
81Eigen::Vector<double, X> euler_without_input(
82 const WithoutInputDerivative<X>& f, const Eigen::Vector<double, X>& x, const double& h
83) {
84 Eigen::Vector<double, X> k1 = f(x);
85
86 return x + h * k1;
87}
88
89/**
90 * Performs first order numerical integration of the time-variant differential
91 * equation dy/dt = f(t, y) using Euler's method.
92 *
93 * 0|
94 * ---|---
95 * | 1
96 *
97 * @param f The function to integrate, with two arguments t and y.
98 * @param t The initial value of t.
99 * @param y The initial value of y.
100 * @param h The time over which to integrate.
101 */
102template <int Y>
103Eigen::Vector<double, Y> euler_time_variant(
104 const TimeVariantDerivative<Y>& f, const double& t, const Eigen::Vector<double, Y>& y,
105 const double& h
106) {
107 Eigen::Vector<double, Y> k1 = f(t, y);
108
109 return y + h * k1;
110}
111
112/**
113 * Performs second order numerical integration of the time-invariant differential
114 * equation dx/dt = f(x, u) using the explicit midpoint method.
115 *
116 * 0|
117 * 1/2|1/2
118 * ---|-------
119 * | 0 1
120 *
121 * @param f The function to integrate, with one argument x.
122 * @param x The initial value of x.
123 * @param u The input value u held constant over the integration period.
124 * @param h The time over which to integrate.
125 */
126template <int X, int U>
127Eigen::Vector<double, X> RK2_with_input(
128 const WithInputDerivative<X, U>& f, const Eigen::Vector<double, X>& x,
129 const Eigen::Vector<double, U>& u, const double& h
130) {
131 Eigen::Vector<double, X> k1 = f(x, u);
132 Eigen::Vector<double, X> k2 = f(x + h * 0.5 * k1, u);
133
134 return x + h * k2;
135}
136
137/**
138 * Performs second order numerical integration of the time-invariant differential
139 * equation dx/dt = f(x) using the explicit midpoint method.
140 *
141 * 0|
142 * 1/2|1/2
143 * ---|-------
144 * | 0 1
145 *
146 * @param f The function to integrate, with one argument x.
147 * @param x The initial value of x.
148 * @param h The time over which to integrate.
149 */
150template <int X>
151Eigen::Vector<double, X> RK2_without_input(
152 const WithoutInputDerivative<X>& f, const Eigen::Vector<double, X>& x, const double& h
153) {
154 Eigen::Vector<double, X> k1 = f(x);
155 Eigen::Vector<double, X> k2 = f(x + h * 0.5 * k1);
156
157 return x + h * k2;
158}
159
160/**
161 * Performs second order numerical integration of the time-variant differential
162 * equation dy/dt = f(t, y) using the explicit midpoint method.
163 *
164 * 0|
165 * 1/2|1/2
166 * ---|-------
167 * | 0 1
168 *
169 * @param f The function to integrate, with two arguments t and y.
170 * @param t The initial value of t.
171 * @param y The initial value of y.
172 * @param h The time over which to integrate.
173 */
174template <int Y>
175Eigen::Vector<double, Y> RK2_time_variant(
176 const TimeVariantDerivative<Y>& f, const double& t, const Eigen::Vector<double, Y>& y,
177 const double& h
178) {
179 Eigen::Vector<double, Y> k1 = f(t, y);
180 Eigen::Vector<double, Y> k2 = f(t + h * 0.5, y + h * 0.5 * k1);
181
182 return y + h * k2;
183}
184
185/**
186 * Performs fourth order numerical integration of the time-invariant differential
187 * equation dx/dt = f(x, u) using the fourth order Runge-Kutta method.
188 *
189 * 0|
190 * 1/2|1/2
191 * 1/2| 0 1/2
192 * 1| 0 0 1
193 * ---|---------------
194 * |1/6 1/3 1/3 1/6
195 *
196 * @param f The function to integrate, with one argument x.
197 * @param x The initial value of x.
198 * @param u The input value u held constant over the integration period.
199 * @param h The time over which to integrate.
200 */
201template <int X, int U>
202Eigen::Vector<double, X> RK4_with_input(
203 const WithInputDerivative<X, U>& f, const Eigen::Vector<double, X>& x,
204 const Eigen::Vector<double, U>& u, const double& h
205) {
206 Eigen::Vector<double, X> k1 = f(x, u);
207 Eigen::Vector<double, X> k2 = f(x + h * 0.5 * k1, u);
208 Eigen::Vector<double, X> k3 = f(x + h * 0.5 * k2, u);
209 Eigen::Vector<double, X> k4 = f(x + h * k3, u);
210
211 return x + h / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4);
212}
213
214/**
215 * Performs fourth order numerical integration of the time-invariant differential
216 * equation dx/dt = f(x) using the fourth order Runge-Kutta method.
217 *
218 * 0|
219 * 1/2|1/2
220 * 1/2| 0 1/2
221 * 1| 0 0 1
222 * ---|---------------
223 * |1/6 1/3 1/3 1/6
224 *
225 * @param f The function to integrate, with one argument x.
226 * @param x The initial value of x.
227 * @param h The time over which to integrate.
228 */
229template <int X>
230Eigen::Vector<double, X> RK4_without_input(
231 const WithoutInputDerivative<X>& f, const Eigen::Vector<double, X>& x, const double& h
232) {
233 Eigen::Vector<double, X> k1 = f(x);
234 Eigen::Vector<double, X> k2 = f(x + h * 0.5 * k1);
235 Eigen::Vector<double, X> k3 = f(x + h * 0.5 * k2);
236 Eigen::Vector<double, X> k4 = f(x + h * k3);
237
238 return x + h / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4);
239}
240
241/**
242 * Performs second order numerical integration of the time-variant differential
243 * equation dy/dt = f(t, y) using the fourth order Runge-Kutta method.
244 *
245 * 0|
246 * 1/2|1/2
247 * 1/2| 0 1/2
248 * 1| 0 0 1
249 * ---|---------------
250 * |1/6 1/3 1/3 1/6
251 *
252 * @param f The function to integrate, with two arguments t and y.
253 * @param t The initial value of t.
254 * @param y The initial value of y.
255 * @param h The time over which to integrate.
256 */
257template <int Y>
258Eigen::Vector<double, Y> RK4_time_variant(
259 const TimeVariantDerivative<Y>& f, const double& t, const Eigen::Vector<double, Y>& y,
260 const double& h
261) {
262 Eigen::Vector<double, Y> k1 = f(t, y);
263 Eigen::Vector<double, Y> k2 = f(t + h * 0.5, y + h * 0.5 * k1);
264 Eigen::Vector<double, Y> k3 = f(t + h * 0.5, y + h * 0.5 * k2);
265 Eigen::Vector<double, Y> k4 = f(t + h, y + h * k3);
266
267 return y + h / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4);
268}