numerics 0.1.0
Loading...
Searching...
No Matches
ode.cpp
Go to the documentation of this file.
1#include "ode/ode.hpp"
2#include <algorithm>
3#include <cmath>
4
5namespace num {
6using std::max;
7
8static void axpy_vec(real alpha, const Vector& x, Vector& y) {
9 for (idx i = 0; i < x.size(); ++i)
10 y[i] += alpha * x[i];
11}
12
13static real eps_guard(real t1) {
14 return 1e-14 * std::abs(t1);
15}
16
18 : f_(std::move(f)),
19 y_(std::move(y0)),
20 dydt_(y_.size()),
21 t_(p.t0),
22 t1_(p.tf),
23 h_(p.h) {
24}
25
26void EulerSteps::advance() {
27 if (t_ >= t1_ - eps_guard(t1_)) {
28 done_ = true;
29 return;
30 }
31 real dt = std::min(h_, t1_ - t_);
32 f_(t_, y_, dydt_);
33 axpy_vec(dt, dydt_, y_);
34 t_ += dt;
35 ++steps_;
36}
37
39 while (!done_) {
40 advance();
41 }
42 return {std::move(y_), t1_, steps_, true};
43}
44
46 : f_(std::move(f)),
47 y_(std::move(y0)),
48 k1_(y_.size()),
49 k2_(y_.size()),
50 k3_(y_.size()),
51 k4_(y_.size()),
52 ytmp_(y_.size()),
53 t_(p.t0),
54 t1_(p.tf),
55 h_(p.h) {
56}
57
58void RK4Steps::advance() {
59 if (t_ >= t1_ - eps_guard(t1_)) {
60 done_ = true;
61 return;
62 }
63 const idx n = y_.size();
64 const real dt = std::min(h_, t1_ - t_);
65
66 f_(t_, y_, k1_);
67 for (idx i = 0; i < n; ++i) {
68 ytmp_[i] = y_[i] + (0.5 * dt * k1_[i]);
69 }
70 f_(t_ + (0.5 * dt), ytmp_, k2_);
71 for (idx i = 0; i < n; ++i) {
72 ytmp_[i] = y_[i] + (0.5 * dt * k2_[i]);
73 }
74 f_(t_ + (0.5 * dt), ytmp_, k3_);
75 for (idx i = 0; i < n; ++i) {
76 ytmp_[i] = y_[i] + (dt * k3_[i]);
77 }
78 f_(t_ + dt, ytmp_, k4_);
79 for (idx i = 0; i < n; ++i) {
80 y_[i] += (dt / 6.0) * (k1_[i] + (2 * k2_[i]) + (2 * k3_[i]) + k4_[i]);
81 }
82 t_ += dt;
83 ++steps_;
84}
85
87 while (!done_) {
88 advance();
89 }
90 return {std::move(y_), t1_, steps_, true};
91}
92
93//
94// Butcher tableau from Dormand & Prince, "A family of embedded Runge-Kutta
95// formulae", J. Comput. Appl. Math. 6(1), 19-26 (1980).
96// 5th-order propagation, 4th-order embedded error estimate, FSAL property.
97
98static constexpr real rk45_a21 = 1.0 / 5.0;
99static constexpr real rk45_a31 = 3.0 / 40.0, rk45_a32 = 9.0 / 40.0;
100static constexpr real rk45_a41 = 44.0 / 45.0, rk45_a42 = -56.0 / 15.0,
101 rk45_a43 = 32.0 / 9.0;
102static constexpr real rk45_a51 = 19372.0 / 6561.0, rk45_a52 = -25360.0 / 2187.0,
103 rk45_a53 = 64448.0 / 6561.0, rk45_a54 = -212.0 / 729.0;
104static constexpr real rk45_a61 = 9017.0 / 3168.0, rk45_a62 = -355.0 / 33.0,
105 rk45_a63 = 46732.0 / 5247.0, rk45_a64 = 49.0 / 176.0,
106 rk45_a65 = -5103.0 / 18656.0;
107
108static constexpr real rk45_b1 = 35.0 / 384.0, rk45_b3 = 500.0 / 1113.0,
109 rk45_b4 = 125.0 / 192.0, rk45_b5 = -2187.0 / 6784.0,
110 rk45_b6 = 11.0 / 84.0;
111
112static constexpr real rk45_e1 = 71.0 / 57600.0, rk45_e3 = -71.0 / 16695.0,
113 rk45_e4 = 71.0 / 1920.0, rk45_e5 = -17253.0 / 339200.0,
114 rk45_e6 = 22.0 / 525.0, rk45_e7 = -1.0 / 40.0;
115
117 : f_(std::move(f)),
118 y_(std::move(y0)),
119 k1_(y_.size()),
120 k2_(y_.size()),
121 k3_(y_.size()),
122 k4_(y_.size()),
123 k5_(y_.size()),
124 k6_(y_.size()),
125 k7_(y_.size()),
126 ytmp_(y_.size()),
127 err_(y_.size()),
128 t_(p.t0),
129 t1_(p.tf),
130 h_(std::min(p.h, p.tf - p.t0)),
131 rtol_(p.rtol),
132 atol_(p.atol),
133 max_steps_(p.max_steps) {
134 f_(t_, y_, k1_); // prime k1 for FSAL
135}
136
137void RK45Steps::advance() {
138 if (t_ >= t1_ - eps_guard(t1_)) {
139 done_ = true;
140 return;
141 }
142 if (steps_ >= max_steps_) {
143 done_ = true;
144 converged_ = false;
145 return;
146 }
147
148 const idx n = y_.size();
149
150 for (;;) {
151 h_ = std::min(h_, t1_ - t_);
152
153 for (idx i = 0; i < n; ++i) {
154 ytmp_[i] = y_[i] + (h_ * rk45_a21 * k1_[i]);
155 }
156 f_(t_ + (h_ / 5.0), ytmp_, k2_);
157
158 for (idx i = 0; i < n; ++i) {
159 ytmp_[i] = y_[i] + (h_ * ((rk45_a31 * k1_[i]) + (rk45_a32 * k2_[i])));
160 }
161 f_(t_ + (3 * h_ / 10.0), ytmp_, k3_);
162
163 for (idx i = 0; i < n; ++i) {
164 ytmp_[i] =
165 y_[i]
166 + (h_
167 * ((rk45_a41 * k1_[i]) + (rk45_a42 * k2_[i]) + (rk45_a43 * k3_[i])));
168 }
169 f_(t_ + (4 * h_ / 5.0), ytmp_, k4_);
170
171 for (idx i = 0; i < n; ++i) {
172 ytmp_[i] = y_[i]
173 + (h_
174 * ((rk45_a51 * k1_[i]) + (rk45_a52 * k2_[i])
175 + (rk45_a53 * k3_[i]) + (rk45_a54 * k4_[i])));
176 }
177 f_(t_ + (8 * h_ / 9.0), ytmp_, k5_);
178
179 for (idx i = 0; i < n; ++i) {
180 ytmp_[i] =
181 y_[i]
182 + (h_
183 * ((rk45_a61 * k1_[i]) + (rk45_a62 * k2_[i]) + (rk45_a63 * k3_[i])
184 + (rk45_a64 * k4_[i]) + (rk45_a65 * k5_[i])));
185 }
186 f_(t_ + h_, ytmp_, k6_);
187
188 for (idx i = 0; i < n; ++i) {
189 ytmp_[i] = y_[i]
190 + (h_
191 * ((rk45_b1 * k1_[i]) + (rk45_b3 * k3_[i]) + (rk45_b4 * k4_[i])
192 + (rk45_b5 * k5_[i]) + (rk45_b6 * k6_[i])));
193 }
194 f_(t_ + h_, ytmp_, k7_);
195
196 for (idx i = 0; i < n; ++i) {
197 err_[i] = h_
198 * ((k1_[i] * rk45_e1) + (rk45_e3 * k3_[i]) + (rk45_e4 * k4_[i])
199 + (k5_[i] * rk45_e5) + (rk45_e6 * k6_[i]) + (rk45_e7 * k7_[i]));
200 }
201
202 real err_norm = 0;
203 for (idx i = 0; i < n; ++i) {
204 real sc = (rtol_ * max(std::abs(y_[i]), std::abs(ytmp_[i]))) + atol_;
205 err_norm = std::max(err_norm, std::abs(err_[i] / sc));
206 }
207
208 real factor = 0.9 * std::pow(err_norm + 1e-10, -0.2);
209 factor = std::max(real(0.1), std::min(real(10.0), factor));
210
211 if (err_norm <= 1.0) {
212 t_ += h_;
213 y_ = ytmp_;
214 k1_ = k7_; // FSAL
215 ++steps_;
216 h_ *= factor;
217 return;
218 }
219 h_ *= factor;
220 }
221}
222
224 while (!done_) {
225 advance();
226 }
227 return {std::move(y_), t_, steps_, converged_};
228}
229
231 : accel_(std::move(accel)),
232 q_(std::move(q0)),
233 v_(std::move(v0)),
234 a_cur_(q_.size()),
235 a_next_(q_.size()),
236 t_(p.t0),
237 t1_(p.tf),
238 h_(p.h) {
239 accel_(q_, a_cur_); // prime initial acceleration
240}
241
242void VerletSteps::advance() {
243 if (t_ >= t1_ - eps_guard(t1_)) {
244 done_ = true;
245 return;
246 }
247 const idx n = q_.size();
248 const real dt = std::min(h_, t1_ - t_);
249
250 for (idx i = 0; i < n; ++i) {
251 q_[i] += (dt * v_[i]) + (0.5 * dt * dt * a_cur_[i]);
252 }
253
254 accel_(q_, a_next_);
255
256 for (idx i = 0; i < n; ++i) {
257 v_[i] += 0.5 * dt * (a_cur_[i] + a_next_[i]);
258 }
259
260 std::swap(a_cur_, a_next_);
261 t_ += dt;
262 ++steps_;
263}
264
266 while (!done_) {
267 advance();
268 }
269 return {std::move(q_), std::move(v_), t_, steps_};
270}
271
273 : accel_(std::move(accel)),
274 q_(std::move(q0)),
275 v_(std::move(v0)),
276 acc_(q_.size()),
277 t_(p.t0),
278 t1_(p.tf),
279 h_(p.h) {
280}
281
282void Yoshida4Steps::advance() {
283 if (t_ >= t1_ - eps_guard(t1_)) {
284 done_ = true;
285 return;
286 }
287 static const real w1 = 1.0 / (2.0 - std::cbrt(2.0));
288 static const real w0 = 1.0 - 2.0 * w1;
289 static const real c1 = w1 * 0.5;
290 static const real c2 = (w0 + w1) * 0.5;
291 static const real d1 = w1;
292 static const real d2 = w0;
293
294 const idx n = q_.size();
295 const real dt = std::min(h_, t1_ - t_);
296
297 for (idx i = 0; i < n; ++i)
298 q_[i] += c1 * dt * v_[i];
299 accel_(q_, acc_);
300 for (idx i = 0; i < n; ++i)
301 v_[i] += d1 * dt * acc_[i];
302 for (idx i = 0; i < n; ++i)
303 q_[i] += c2 * dt * v_[i];
304 accel_(q_, acc_);
305 for (idx i = 0; i < n; ++i)
306 v_[i] += d2 * dt * acc_[i];
307 for (idx i = 0; i < n; ++i)
308 q_[i] += c2 * dt * v_[i];
309 accel_(q_, acc_);
310 for (idx i = 0; i < n; ++i)
311 v_[i] += d1 * dt * acc_[i];
312 for (idx i = 0; i < n; ++i)
313 q_[i] += c1 * dt * v_[i];
314
315 t_ += dt;
316 ++steps_;
317}
318
320 while (!done_)
321 advance();
322 return {std::move(q_), std::move(v_), t_, steps_};
323}
324
326 : accel_(std::move(accel)),
327 q_(std::move(q0)),
328 v_(std::move(v0)),
329 a1_(q_.size()),
330 a2_(q_.size()),
331 a3_(q_.size()),
332 a4_(q_.size()),
333 qtmp_(q_.size()),
334 t_(p.t0),
335 t1_(p.tf),
336 h_(p.h) {
337}
338
339void RK4_2ndSteps::advance() {
340 if (t_ >= t1_ - eps_guard(t1_)) {
341 done_ = true;
342 return;
343 }
344 const idx n = q_.size();
345 const real dt = std::min(h_, t1_ - t_);
346
347 accel_(q_, a1_);
348 for (idx i = 0; i < n; ++i)
349 qtmp_[i] = q_[i] + 0.5 * dt * v_[i];
350 accel_(qtmp_, a2_);
351 for (idx i = 0; i < n; ++i)
352 qtmp_[i] = q_[i] + 0.5 * dt * (v_[i] + 0.5 * dt * a1_[i]);
353 accel_(qtmp_, a3_);
354 for (idx i = 0; i < n; ++i)
355 qtmp_[i] = q_[i] + dt * (v_[i] + 0.5 * dt * a2_[i]);
356 accel_(qtmp_, a4_);
357
358 for (idx i = 0; i < n; ++i) {
359 q_[i] += dt * v_[i] + (dt * dt / 6.0) * (a1_[i] + a2_[i] + a3_[i]);
360 v_[i] += (dt / 6.0) * (a1_[i] + 2.0 * a2_[i] + 2.0 * a3_[i] + a4_[i]);
361 }
362 t_ += dt;
363 ++steps_;
364}
365
367 while (!done_)
368 advance();
369 return {std::move(q_), std::move(v_), t_, steps_};
370}
371
373 return EulerSteps(std::move(f), std::move(y0), p);
374}
376 return RK4Steps(std::move(f), std::move(y0), p);
377}
379 return RK45Steps(std::move(f), std::move(y0), p);
380}
381
383 return VerletSteps(std::move(a), std::move(q0), std::move(v0), p);
384}
386 return Yoshida4Steps(std::move(a), std::move(q0), std::move(v0), p);
387}
389 return RK4_2ndSteps(std::move(a), std::move(q0), std::move(v0), p);
390}
391
393 auto s = euler(std::move(f), std::move(y0), p);
394 if (obs)
395 for (auto step : s)
396 obs(step.t, step.y);
397 return s.run();
398}
400 auto s = rk4(std::move(f), std::move(y0), p);
401 if (obs)
402 for (auto step : s)
403 obs(step.t, step.y);
404 return s.run();
405}
407 auto s = rk45(std::move(f), std::move(y0), p);
408 if (obs)
409 for (auto step : s)
410 obs(step.t, step.y);
411 return s.run();
412}
414 Vector q0,
415 Vector v0,
416 ODEParams p,
417 SympObserverFn obs) {
418 auto s = verlet(std::move(a), std::move(q0), std::move(v0), p);
419 if (obs)
420 for (auto step : s)
421 obs(step.t, step.q, step.v);
422 return s.run();
423}
425 Vector q0,
426 Vector v0,
427 ODEParams p,
428 SympObserverFn obs) {
429 auto s = yoshida4(std::move(a), std::move(q0), std::move(v0), p);
430 if (obs)
431 for (auto step : s)
432 obs(step.t, step.q, step.v);
433 return s.run();
434}
436 Vector q0,
437 Vector v0,
438 ODEParams p,
439 SympObserverFn obs) {
440 auto s = rk4_2nd(std::move(a), std::move(q0), std::move(v0), p);
441 if (obs)
442 for (auto step : s)
443 obs(step.t, step.q, step.v);
444 return s.run();
445}
446
447} // namespace num
constexpr idx size() const noexcept
Definition vector.hpp:83
EulerSteps(ODERhsFn f, Vector y0, ODEParams p)
Definition ode.cpp:17
ODEResult run()
Definition ode.cpp:38
ODEResult run()
Definition ode.cpp:223
RK45Steps(ODERhsFn f, Vector y0, ODEParams p)
Definition ode.cpp:116
RK4Steps(ODERhsFn f, Vector y0, ODEParams p)
Definition ode.cpp:45
ODEResult run()
Definition ode.cpp:86
SymplecticResult run()
Definition ode.cpp:366
RK4_2ndSteps(AccelFn accel, Vector q0, Vector v0, ODEParams p)
Definition ode.cpp:325
VerletSteps(AccelFn accel, Vector q0, Vector v0, ODEParams p)
Definition ode.cpp:230
SymplecticResult run()
Definition ode.cpp:265
Yoshida4Steps(AccelFn accel, Vector q0, Vector v0, ODEParams p)
Definition ode.cpp:272
SymplecticResult run()
Definition ode.cpp:319
EulerSteps euler(ODERhsFn f, Vector y0, ODEParams p={})
Definition ode.cpp:372
ODEResult ode_rk4(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Classic 4th-order Runge-Kutta, fixed step.
Definition ode.cpp:399
double real
Definition types.hpp:10
Yoshida4Steps yoshida4(AccelFn accel, Vector q0, Vector v0, ODEParams p={})
Definition ode.cpp:385
std::function< void(real t, const Vector &q, const Vector &v)> SympObserverFn
Definition ode.hpp:15
std::function< void(real t, const Vector &y, Vector &dydt)> ODERhsFn
Definition ode.hpp:12
SymplecticResult ode_verlet(AccelFn accel, Vector q0, Vector v0, ODEParams p={}, SympObserverFn obs=nullptr)
Velocity Verlet, 2nd-order symplectic, 1 force evaluation per step.
Definition ode.cpp:413
SymplecticResult ode_rk4_2nd(AccelFn accel, Vector q0, Vector v0, ODEParams p={}, SympObserverFn obs=nullptr)
RK4 for second-order systems q'' = accel(q), Nystrom form.
Definition ode.cpp:435
std::size_t idx
Definition types.hpp:11
std::function< void(real t, const Vector &y)> ObserverFn
Definition ode.hpp:14
constexpr real e
Definition math.hpp:44
RK4Steps rk4(ODERhsFn f, Vector y0, ODEParams p={})
Definition ode.cpp:375
BasicVector< real > Vector
Real-valued dense vector with full backend dispatch (CPU + GPU)
Definition vector.hpp:129
std::function< void(const Vector &q, Vector &acc)> AccelFn
Definition ode.hpp:13
RK4_2ndSteps rk4_2nd(AccelFn accel, Vector q0, Vector v0, ODEParams p={})
Definition ode.cpp:388
ODEResult ode_euler(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Forward Euler, 1st-order, fixed step.
Definition ode.cpp:392
SymplecticResult ode_yoshida4(AccelFn accel, Vector q0, Vector v0, ODEParams p={}, SympObserverFn obs=nullptr)
Yoshida 4th-order symplectic, 3 force evaluations per step.
Definition ode.cpp:424
RK45Steps rk45(ODERhsFn f, Vector y0, ODEParams p={})
Definition ode.cpp:378
VerletSteps verlet(AccelFn accel, Vector q0, Vector v0, ODEParams p={})
Definition ode.cpp:382
ODEResult ode_rk45(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Adaptive Dormand-Prince RK45 with FSAL and PI step-size control.
Definition ode.cpp:406
ODE and symplectic integrators.