9 for (
idx i = 0; i < x.size(); ++i)
13static real eps_guard(
real t1) {
return 1
e-14 * std::abs(t1); }
16 : f_(std::move(f)), y_(std::move(y0)), dydt_(y_.size()), t_(p.t0),
19void EulerSteps::advance() {
20 if (t_ >= t1_ - eps_guard(t1_)) {
24 real dt = std::min(h_, t1_ - t_);
26 axpy_vec(dt, dydt_, y_);
34 return {std::move(y_), t1_, steps_,
true};
38 : f_(std::move(f)), y_(std::move(y0)), k1_(y_.size()), k2_(y_.size()),
39 k3_(y_.size()), k4_(y_.size()), ytmp_(y_.size()), t_(p.t0), t1_(p.tf),
42void RK4Steps::advance() {
43 if (t_ >= t1_ - eps_guard(t1_)) {
48 const real dt = std::min(h_, t1_ - t_);
51 for (
idx i = 0; i < n; ++i)
52 ytmp_[i] = y_[i] + 0.5 * dt * k1_[i];
53 f_(t_ + 0.5 * dt, ytmp_, k2_);
54 for (
idx i = 0; i < n; ++i)
55 ytmp_[i] = y_[i] + 0.5 * dt * k2_[i];
56 f_(t_ + 0.5 * dt, ytmp_, k3_);
57 for (
idx i = 0; i < n; ++i)
58 ytmp_[i] = y_[i] + dt * k3_[i];
59 f_(t_ + dt, ytmp_, k4_);
60 for (
idx i = 0; i < n; ++i)
61 y_[i] += (dt / 6.0) * (k1_[i] + 2 * k2_[i] + 2 * k3_[i] + k4_[i]);
69 return {std::move(y_), t1_, steps_,
true};
77static constexpr real rk45_a21 = 1.0 / 5.0;
78static constexpr real rk45_a31 = 3.0 / 40.0, rk45_a32 = 9.0 / 40.0;
79static constexpr real rk45_a41 = 44.0 / 45.0, rk45_a42 = -56.0 / 15.0,
80 rk45_a43 = 32.0 / 9.0;
81static constexpr real rk45_a51 = 19372.0 / 6561.0, rk45_a52 = -25360.0 / 2187.0,
82 rk45_a53 = 64448.0 / 6561.0, rk45_a54 = -212.0 / 729.0;
83static constexpr real rk45_a61 = 9017.0 / 3168.0, rk45_a62 = -355.0 / 33.0,
84 rk45_a63 = 46732.0 / 5247.0, rk45_a64 = 49.0 / 176.0,
85 rk45_a65 = -5103.0 / 18656.0;
87static constexpr real rk45_b1 = 35.0 / 384.0, rk45_b3 = 500.0 / 1113.0,
88 rk45_b4 = 125.0 / 192.0, rk45_b5 = -2187.0 / 6784.0,
89 rk45_b6 = 11.0 / 84.0;
91static constexpr real rk45_e1 = 71.0 / 57600.0, rk45_e3 = -71.0 / 16695.0,
92 rk45_e4 = 71.0 / 1920.0, rk45_e5 = -17253.0 / 339200.0,
93 rk45_e6 = 22.0 / 525.0, rk45_e7 = -1.0 / 40.0;
96 : f_(std::move(f)), y_(std::move(y0)), k1_(y_.size()), k2_(y_.size()),
97 k3_(y_.size()), k4_(y_.size()), k5_(y_.size()), k6_(y_.size()),
98 k7_(y_.size()), ytmp_(y_.size()), err_(y_.size()), t_(p.t0), t1_(p.tf),
99 h_(std::min(p.h, p.tf - p.t0)), rtol_(p.rtol), atol_(p.atol),
100 max_steps_(p.max_steps) {
104void RK45Steps::advance() {
105 if (t_ >= t1_ - eps_guard(t1_)) {
109 if (steps_ >= max_steps_) {
118 h_ = std::min(h_, t1_ - t_);
120 for (
idx i = 0; i < n; ++i) {
121 ytmp_[i] = y_[i] + (h_ * rk45_a21 * k1_[i]);
123 f_(t_ + (h_ / 5.0), ytmp_, k2_);
125 for (
idx i = 0; i < n; ++i) {
126 ytmp_[i] = y_[i] + (h_ * ((rk45_a31 * k1_[i]) + (rk45_a32 * k2_[i])));
128 f_(t_ + (3 * h_ / 10.0), ytmp_, k3_);
130 for (
idx i = 0; i < n; ++i) {
131 ytmp_[i] = y_[i] + (h_ * ((rk45_a41 * k1_[i]) + (rk45_a42 * k2_[i]) +
132 (rk45_a43 * k3_[i])));
134 f_(t_ + (4 * h_ / 5.0), ytmp_, k4_);
136 for (
idx i = 0; i < n; ++i) {
137 ytmp_[i] = y_[i] + (h_ * ((rk45_a51 * k1_[i]) + (rk45_a52 * k2_[i]) +
138 (rk45_a53 * k3_[i]) + (rk45_a54 * k4_[i])));
140 f_(t_ + (8 * h_ / 9.0), ytmp_, k5_);
142 for (
idx i = 0; i < n; ++i) {
143 ytmp_[i] = y_[i] + (h_ * ((rk45_a61 * k1_[i]) + (rk45_a62 * k2_[i]) +
144 rk45_a63 * k3_[i] + rk45_a64 * k4_[i] +
147 f_(t_ + h_, ytmp_, k6_);
149 for (
idx i = 0; i < n; ++i) {
150 ytmp_[i] = y_[i] + (h_ * ((rk45_b1 * k1_[i]) + rk45_b3 * k3_[i] +
151 rk45_b4 * k4_[i] + (rk45_b5 * k5_[i]) +
152 (rk45_b6 * k6_[i])));
154 f_(t_ + h_, ytmp_, k7_);
156 for (
idx i = 0; i < n; ++i) {
158 h_ * ((k1_[i] * rk45_e1) + (rk45_e3 * k3_[i]) + (rk45_e4 * k4_[i]) +
159 (k5_[i] * rk45_e5) + (rk45_e6 * k6_[i]) + (rk45_e7 * k7_[i]));
163 for (
idx i = 0; i < n; ++i) {
164 real sc = (rtol_ * max(std::abs(y_[i]), std::abs(ytmp_[i]))) + atol_;
165 err_norm = std::max(err_norm, std::abs(err_[i] / sc));
168 real factor = 0.9 * std::pow(err_norm + 1
e-10, -0.2);
169 factor = std::max(
real(0.1), std::min(
real(10.0), factor));
171 if (err_norm <= 1.0) {
186 return {std::move(y_), t_, steps_, converged_};
190 : accel_(std::move(accel)), q_(std::move(q0)), v_(std::move(v0)),
191 a_cur_(q_.size()), a_next_(q_.size()), t_(p.t0), t1_(p.tf), h_(p.h) {
195void VerletSteps::advance() {
196 if (t_ >= t1_ - eps_guard(t1_)) {
201 const real dt = std::min(h_, t1_ - t_);
203 for (
idx i = 0; i < n; ++i)
204 q_[i] += dt * v_[i] + 0.5 * dt * dt * a_cur_[i];
208 for (
idx i = 0; i < n; ++i)
209 v_[i] += 0.5 * dt * (a_cur_[i] + a_next_[i]);
211 std::swap(a_cur_, a_next_);
219 return {std::move(q_), std::move(v_), t_, steps_};
223 : accel_(std::move(accel)), q_(std::move(q0)), v_(std::move(v0)),
224 acc_(q_.size()), t_(p.t0), t1_(p.tf), h_(p.h) {}
226void Yoshida4Steps::advance() {
227 if (t_ >= t1_ - eps_guard(t1_)) {
231 static const real w1 = 1.0 / (2.0 - std::cbrt(2.0));
232 static const real w0 = 1.0 - 2.0 * w1;
233 static const real c1 = w1 * 0.5;
234 static const real c2 = (w0 + w1) * 0.5;
235 static const real d1 = w1;
236 static const real d2 = w0;
239 const real dt = std::min(h_, t1_ - t_);
241 for (
idx i = 0; i < n; ++i)
242 q_[i] += c1 * dt * v_[i];
244 for (
idx i = 0; i < n; ++i)
245 v_[i] += d1 * dt * acc_[i];
246 for (
idx i = 0; i < n; ++i)
247 q_[i] += c2 * dt * v_[i];
249 for (
idx i = 0; i < n; ++i)
250 v_[i] += d2 * dt * acc_[i];
251 for (
idx i = 0; i < n; ++i)
252 q_[i] += c2 * dt * v_[i];
254 for (
idx i = 0; i < n; ++i)
255 v_[i] += d1 * dt * acc_[i];
256 for (
idx i = 0; i < n; ++i)
257 q_[i] += c1 * dt * v_[i];
266 return {std::move(q_), std::move(v_), t_, steps_};
270 : accel_(std::move(accel)), q_(std::move(q0)), v_(std::move(v0)),
271 a1_(q_.size()), a2_(q_.size()), a3_(q_.size()), a4_(q_.size()),
272 qtmp_(q_.size()), t_(p.t0), t1_(p.tf), h_(p.h) {}
274void RK4_2ndSteps::advance() {
275 if (t_ >= t1_ - eps_guard(t1_)) {
280 const real dt = std::min(h_, t1_ - t_);
283 for (
idx i = 0; i < n; ++i)
284 qtmp_[i] = q_[i] + 0.5 * dt * v_[i];
286 for (
idx i = 0; i < n; ++i)
287 qtmp_[i] = q_[i] + 0.5 * dt * (v_[i] + 0.5 * dt * a1_[i]);
289 for (
idx i = 0; i < n; ++i)
290 qtmp_[i] = q_[i] + dt * (v_[i] + 0.5 * dt * a2_[i]);
293 for (
idx i = 0; i < n; ++i) {
294 q_[i] += dt * v_[i] + (dt * dt / 6.0) * (a1_[i] + a2_[i] + a3_[i]);
295 v_[i] += (dt / 6.0) * (a1_[i] + 2.0 * a2_[i] + 2.0 * a3_[i] + a4_[i]);
304 return {std::move(q_), std::move(v_), t_, steps_};
310 return EulerSteps(std::move(f), std::move(y0), p);
313 return RK4Steps(std::move(f), std::move(y0), p);
316 return RK45Steps(std::move(f), std::move(y0), p);
320 return VerletSteps(std::move(a), std::move(q0), std::move(v0), p);
323 return Yoshida4Steps(std::move(a), std::move(q0), std::move(v0), p);
326 return RK4_2ndSteps(std::move(a), std::move(q0), std::move(v0), p);
333 auto s =
euler(std::move(f), std::move(y0), p);
340 auto s =
rk4(std::move(f), std::move(y0), p);
347 auto s =
rk45(std::move(f), std::move(y0), p);
355 auto s =
verlet(std::move(a), std::move(q0), std::move(v0), p);
358 obs(step.t, step.q, step.v);
363 auto s =
yoshida4(std::move(a), std::move(q0), std::move(v0), p);
366 obs(step.t, step.q, step.v);
371 auto s =
rk4_2nd(std::move(a), std::move(q0), std::move(v0), p);
374 obs(step.t, step.q, step.v);
constexpr idx size() const noexcept
EulerSteps(ODERhsFn f, Vector y0, ODEParams p)
RK45Steps(ODERhsFn f, Vector y0, ODEParams p)
RK4Steps(ODERhsFn f, Vector y0, ODEParams p)
RK4_2ndSteps(AccelFn accel, Vector q0, Vector v0, ODEParams p)
VerletSteps(AccelFn accel, Vector q0, Vector v0, ODEParams p)
Yoshida4Steps(AccelFn accel, Vector q0, Vector v0, ODEParams p)
EulerSteps euler(ODERhsFn f, Vector y0, ODEParams p={})
ODEResult ode_rk4(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Classic 4th-order Runge-Kutta, fixed step.
Yoshida4Steps yoshida4(AccelFn accel, Vector q0, Vector v0, ODEParams p={})
std::function< void(real t, const Vector &q, const Vector &v)> SympObserverFn
std::function< void(real t, const Vector &y, Vector &dydt)> ODERhsFn
SymplecticResult ode_verlet(AccelFn accel, Vector q0, Vector v0, ODEParams p={}, SympObserverFn obs=nullptr)
Velocity Verlet, 2nd-order symplectic, 1 force evaluation per step.
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.
std::function< void(real t, const Vector &y)> ObserverFn
RK4Steps rk4(ODERhsFn f, Vector y0, ODEParams p={})
BasicVector< real > Vector
Real-valued dense vector with full backend dispatch (CPU + GPU)
std::function< void(const Vector &q, Vector &acc)> AccelFn
RK4_2ndSteps rk4_2nd(AccelFn accel, Vector q0, Vector v0, ODEParams p={})
ODEResult ode_euler(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Forward Euler, 1st-order, fixed step.
SymplecticResult ode_yoshida4(AccelFn accel, Vector q0, Vector v0, ODEParams p={}, SympObserverFn obs=nullptr)
Yoshida 4th-order symplectic, 3 force evaluations per step.
RK45Steps rk45(ODERhsFn f, Vector y0, ODEParams p={})
VerletSteps verlet(AccelFn accel, Vector q0, Vector v0, ODEParams p={})
ODEResult ode_rk45(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Adaptive Dormand-Prince RK45 with FSAL and PI step-size control.
ODE integrators: Euler, RK4, adaptive RK45 (Dormand-Prince), and symplectic Velocity Verlet / Yoshida...
Parameters for all ODE integrators. Set only the fields you need.