DiffEq - Modern C++ ODE Integration Library 1.0.0
High-performance C++ library for solving ODEs with async signal processing
Loading...
Searching...
No Matches
rk45.hpp
1#pragma once
2#include <core/adaptive_integrator.hpp>
3#include <core/state_creator.hpp>
4#include <cmath>
5#include <stdexcept>
6
7namespace diffeq {
8
19template<system_state S>
21public:
23 using state_type = typename base_type::state_type;
24 using time_type = typename base_type::time_type;
25 using value_type = typename base_type::value_type;
26 using system_function = typename base_type::system_function;
27
28 explicit RK45Integrator(system_function sys,
29 time_type rtol = static_cast<time_type>(1e-6),
30 time_type atol = static_cast<time_type>(1e-9))
31 : base_type(std::move(sys), rtol, atol) {}
32
33 void step(state_type& state, time_type dt) override {
34 adaptive_step(state, dt);
35 }
36
37 time_type adaptive_step(state_type& state, time_type dt) override {
38 // Create temporary states for RK45 calculations
39 state_type k1 = StateCreator<state_type>::create(state);
40 state_type k2 = StateCreator<state_type>::create(state);
41 state_type k3 = StateCreator<state_type>::create(state);
42 state_type k4 = StateCreator<state_type>::create(state);
43 state_type k5 = StateCreator<state_type>::create(state);
44 state_type k6 = StateCreator<state_type>::create(state);
45 state_type temp_state = StateCreator<state_type>::create(state);
46 state_type y_new = StateCreator<state_type>::create(state);
47 state_type error = StateCreator<state_type>::create(state);
48
49 // RK45 coefficients (Fehlberg coefficients)
50 constexpr time_type a2 = static_cast<time_type>(1.0/5.0);
51 constexpr time_type a3 = static_cast<time_type>(3.0/10.0);
52 constexpr time_type a4 = static_cast<time_type>(4.0/5.0);
53 constexpr time_type a5 = static_cast<time_type>(8.0/9.0);
54
55 constexpr time_type b21 = static_cast<time_type>(1.0/5.0);
56 constexpr time_type b31 = static_cast<time_type>(3.0/40.0);
57 constexpr time_type b32 = static_cast<time_type>(9.0/40.0);
58 constexpr time_type b41 = static_cast<time_type>(44.0/45.0);
59 constexpr time_type b42 = static_cast<time_type>(-56.0/15.0);
60 constexpr time_type b43 = static_cast<time_type>(32.0/9.0);
61 constexpr time_type b51 = static_cast<time_type>(19372.0/6561.0);
62 constexpr time_type b52 = static_cast<time_type>(-25360.0/2187.0);
63 constexpr time_type b53 = static_cast<time_type>(64448.0/6561.0);
64 constexpr time_type b54 = static_cast<time_type>(-212.0/729.0);
65 constexpr time_type b61 = static_cast<time_type>(9017.0/3168.0);
66 constexpr time_type b62 = static_cast<time_type>(-355.0/33.0);
67 constexpr time_type b63 = static_cast<time_type>(46732.0/5247.0);
68 constexpr time_type b64 = static_cast<time_type>(49.0/176.0);
69 constexpr time_type b65 = static_cast<time_type>(-5103.0/18656.0);
70
71 // 5th order solution coefficients
72 constexpr time_type c1 = static_cast<time_type>(35.0/384.0);
73 constexpr time_type c3 = static_cast<time_type>(500.0/1113.0);
74 constexpr time_type c4 = static_cast<time_type>(125.0/192.0);
75 constexpr time_type c5 = static_cast<time_type>(-2187.0/6784.0);
76 constexpr time_type c6 = static_cast<time_type>(11.0/84.0);
77
78 // 4th order solution coefficients (for error estimation)
79 constexpr time_type c1_4 = static_cast<time_type>(5179.0/57600.0);
80 constexpr time_type c3_4 = static_cast<time_type>(7571.0/16695.0);
81 constexpr time_type c4_4 = static_cast<time_type>(393.0/640.0);
82 constexpr time_type c5_4 = static_cast<time_type>(-92097.0/339200.0);
83 constexpr time_type c6_4 = static_cast<time_type>(187.0/2100.0);
84 // Note: c7_4 = 1.0/40.0 is not used in RK45 (only in RK45 with FSAL)
85
86 // k1 = f(t, y)
87 this->sys_(this->current_time_, state, k1);
88
89 // k2 = f(t + a2*dt, y + dt*(b21*k1))
90 for (std::size_t i = 0; i < state.size(); ++i) {
91 temp_state[i] = state[i] + dt * b21 * k1[i];
92 }
93 this->sys_(this->current_time_ + a2 * dt, temp_state, k2);
94
95 // k3 = f(t + a3*dt, y + dt*(b31*k1 + b32*k2))
96 for (std::size_t i = 0; i < state.size(); ++i) {
97 temp_state[i] = state[i] + dt * (b31 * k1[i] + b32 * k2[i]);
98 }
99 this->sys_(this->current_time_ + a3 * dt, temp_state, k3);
100
101 // k4 = f(t + a4*dt, y + dt*(b41*k1 + b42*k2 + b43*k3))
102 for (std::size_t i = 0; i < state.size(); ++i) {
103 temp_state[i] = state[i] + dt * (b41 * k1[i] + b42 * k2[i] + b43 * k3[i]);
104 }
105 this->sys_(this->current_time_ + a4 * dt, temp_state, k4);
106
107 // k5 = f(t + a5*dt, y + dt*(b51*k1 + b52*k2 + b53*k3 + b54*k4))
108 for (std::size_t i = 0; i < state.size(); ++i) {
109 temp_state[i] = state[i] + dt * (b51 * k1[i] + b52 * k2[i] + b53 * k3[i] + b54 * k4[i]);
110 }
111 this->sys_(this->current_time_ + a5 * dt, temp_state, k5);
112
113 // k6 = f(t + dt, y + dt*(b61*k1 + b62*k2 + b63*k3 + b64*k4 + b65*k5))
114 for (std::size_t i = 0; i < state.size(); ++i) {
115 temp_state[i] = state[i] + dt * (b61 * k1[i] + b62 * k2[i] + b63 * k3[i] + b64 * k4[i] + b65 * k5[i]);
116 }
117 this->sys_(this->current_time_ + dt, temp_state, k6);
118
119 // 5th order solution: y_new = y + dt*(c1*k1 + c3*k3 + c4*k4 + c5*k5 + c6*k6)
120 for (std::size_t i = 0; i < state.size(); ++i) {
121 y_new[i] = state[i] + dt * (c1 * k1[i] + c3 * k3[i] + c4 * k4[i] + c5 * k5[i] + c6 * k6[i]);
122 }
123
124 // 4th order solution for error estimation
125 for (std::size_t i = 0; i < state.size(); ++i) {
126 error[i] = dt * ((c1 - c1_4) * k1[i] + (c3 - c3_4) * k3[i] + (c4 - c4_4) * k4[i] +
127 (c5 - c5_4) * k5[i] + (c6 - c6_4) * k6[i]);
128 }
129
130 // Calculate error norm
131 time_type error_norm = this->error_norm_scipy_style(error, state, y_new);
132
133 // Accept or reject step
134 if (error_norm <= static_cast<time_type>(1.0)) {
135 // Accept step
136 state = y_new;
137 this->advance_time(dt);
138
139 // Suggest next step size
140 time_type new_dt = this->suggest_step_size(dt, error_norm, 5);
141 return new_dt;
142 } else {
143 // Reject step, suggest smaller step size
144 time_type new_dt = this->suggest_step_size(dt, error_norm, 5);
145 return new_dt;
146 }
147 }
148};
149
150} // namespace diffeq
RK45 (Runge-Kutta-Fehlberg 4(5)) adaptive integrator.
Definition rk45.hpp:20