@@ -14,13 +14,64 @@ namespace diffeq::integrators::ode {
1414 */
1515template <system_state S, can_be_time T = double >
1616class DOP853Integrator : public AdaptiveIntegrator <S, T> {
17+
1718public:
1819 using base_type = AdaptiveIntegrator<S, T>;
1920 using state_type = typename base_type::state_type;
2021 using time_type = typename base_type::time_type;
2122 using value_type = typename base_type::value_type;
2223 using system_function = typename base_type::system_function;
2324
25+ private:
26+ // Compute a good initial step size (HINIT from Fortran)
27+ time_type compute_initial_step (const state_type& y, time_type t, const system_function& sys, time_type t_end) const {
28+ // Compute f0 = f(t, y)
29+ state_type f0 = StateCreator<state_type>::create (y);
30+ sys (t, y, f0);
31+
32+ // Compute a norm for y and f0
33+ time_type dnf = 0.0 , dny = 0.0 ;
34+ for (std::size_t i = 0 ; i < y.size (); ++i) {
35+ time_type sk = this ->atol_ + this ->rtol_ * std::abs (y[i]);
36+ dnf += (f0[i] / sk) * (f0[i] / sk);
37+ dny += (y[i] / sk) * (y[i] / sk);
38+ }
39+ time_type h = 1e-6 ;
40+ if (dnf > 1e-10 && dny > 1e-10 ) {
41+ h = std::sqrt (dny / dnf) * 0.01 ;
42+ }
43+ h = std::min (h, std::abs (t_end - t));
44+ h = std::copysign (h, t_end - t);
45+
46+ // Perform an explicit Euler step
47+ state_type y1 = StateCreator<state_type>::create (y);
48+ for (std::size_t i = 0 ; i < y.size (); ++i)
49+ y1[i] = y[i] + h * f0[i];
50+ state_type f1 = StateCreator<state_type>::create (y);
51+ sys (t + h, y1, f1);
52+
53+ // Estimate the second derivative
54+ time_type der2 = 0.0 ;
55+ for (std::size_t i = 0 ; i < y.size (); ++i) {
56+ time_type sk = this ->atol_ + this ->rtol_ * std::abs (y[i]);
57+ der2 += ((f1[i] - f0[i]) / sk) * ((f1[i] - f0[i]) / sk);
58+ }
59+ der2 = std::sqrt (der2) / h;
60+
61+ // Step size is computed such that h^order * max(norm(f0), norm(der2)) = 0.01
62+ time_type der12 = std::max (std::abs (der2), std::sqrt (dnf));
63+ time_type h1 = h;
64+ if (der12 > 1e-15 ) {
65+ h1 = std::pow (0.01 / der12, 1.0 / 8.0 );
66+ } else {
67+ h1 = std::max (1e-6 , std::abs (h) * 1e-3 );
68+ }
69+ h = std::min (100 * std::abs (h), h1, std::abs (t_end - t));
70+ h = std::copysign (h, t_end - t);
71+ return h;
72+ }
73+
74+ public:
2475 explicit DOP853Integrator (system_function sys,
2576 time_type rtol = static_cast <time_type>(1e-8 ),
2677 time_type atol = static_cast<time_type>(1e-10 ))
@@ -30,9 +81,23 @@ class DOP853Integrator : public AdaptiveIntegrator<S, T> {
3081 adaptive_step (state, dt);
3182 }
3283
84+ // To match Fortran DOP853, we need to know the integration target time for HINIT
85+ // This version assumes you set target_time_ before calling adaptive_step
86+ time_type target_time_ = 0 ; // User must set this before integration
87+
3388 time_type adaptive_step (state_type& state, time_type dt) override {
34- const int max_attempts = 10 ;
89+ // Fortran DOP853: if dt <= 0, estimate initial step size using HINIT (compute_initial_step)
90+ time_type t = this ->current_time_ ;
91+ time_type t_end = target_time_;
92+ if (t_end == t) t_end = t + 1.0 ; // fallback if not set
3593 time_type current_dt = dt;
94+ if (current_dt <= 0 ) {
95+ // Use the system function and current state to estimate initial step
96+ current_dt = compute_initial_step (state, t, this ->sys_ , t_end);
97+ // Clamp to allowed min/max
98+ current_dt = std::max (this ->dt_min_ , std::min (this ->dt_max_ , current_dt));
99+ }
100+ const int max_attempts = 10 ;
36101 for (int attempt = 0 ; attempt < max_attempts; ++attempt) {
37102 state_type y_new = StateCreator<state_type>::create (state);
38103 state_type error = StateCreator<state_type>::create (state);
0 commit comments