Skip to content

Commit 44b83b5

Browse files
committed
added HINIT method for initial step guess
1 parent 0401afd commit 44b83b5

File tree

1 file changed

+66
-1
lines changed

1 file changed

+66
-1
lines changed

include/integrators/ode/dop853.hpp

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,64 @@ namespace diffeq::integrators::ode {
1414
*/
1515
template<system_state S, can_be_time T = double>
1616
class DOP853Integrator : public AdaptiveIntegrator<S, T> {
17+
1718
public:
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

Comments
 (0)