@@ -49,13 +49,44 @@ class DOP853DenseOutputHelper {
4949template <system_state S, can_be_time T = double >
5050class DOP853Integrator : public AdaptiveIntegrator <S, T> {
5151
52+
5253public:
5354 using base_type = AdaptiveIntegrator<S, T>;
5455 using state_type = typename base_type::state_type;
5556 using time_type = typename base_type::time_type;
5657 using value_type = typename base_type::value_type;
5758 using system_function = typename base_type::system_function;
5859
60+ // Fortran default parameters (do not change unless you know what you are doing)
61+ static constexpr time_type fortran_safety = static_cast <time_type>(0.9 ); // SAFE
62+ static constexpr time_type fortran_fac1 = static_cast <time_type>(0.333 ); // FAC1
63+ static constexpr time_type fortran_fac2 = static_cast <time_type>(6.0 ); // FAC2
64+ static constexpr time_type fortran_beta = static_cast <time_type>(0.0 ); // BETA
65+ static constexpr time_type fortran_dt_max = static_cast <time_type>(1e100 ); // HMAX
66+ static constexpr time_type fortran_dt_min = static_cast <time_type>(1e-16 ); // practical min
67+ static constexpr int fortran_nmax = 100000 ; // NMAX
68+ static constexpr int fortran_nstiff = 1000 ; // NSTIFF
69+
70+ // Internal state (Fortran default values)
71+ time_type safety_factor_ = fortran_safety;
72+ time_type fac1_ = fortran_fac1;
73+ time_type fac2_ = fortran_fac2;
74+ time_type beta_ = fortran_beta;
75+ time_type dt_max_ = fortran_dt_max;
76+ time_type dt_min_ = fortran_dt_min;
77+ int nmax_ = fortran_nmax;
78+ int nstiff_ = fortran_nstiff;
79+ time_type facold_ = static_cast <time_type>(1e-4 ); // Fortran FACOLD
80+ // Stiffness detection state
81+ int iastiff_ = 0 ;
82+ int nonsti_ = 0 ;
83+ time_type hlamb_ = 0 ;
84+ // For statistics (optional)
85+ int nstep_ = 0 ;
86+ int naccpt_ = 0 ;
87+ int nrejct_ = 0 ;
88+ int nfcn_ = 0 ;
89+
5990 // Step size control parameters (modern C++ style, with defaults)
6091 time_type safety_factor_ = static_cast <time_type>(0.9 ); // Fortran SAFE
6192 time_type fac1_ = static_cast <time_type>(0.333 ); // Fortran FAC1 (min step size factor)
@@ -133,18 +164,8 @@ class DOP853Integrator : public AdaptiveIntegrator<S, T> {
133164public:
134165 explicit DOP853Integrator (system_function sys,
135166 time_type rtol = static_cast <time_type>(1e-8 ),
136- time_type atol = static_cast<time_type>(1e-10 ),
137- time_type safety = static_cast<time_type>(0.9 ),
138- time_type fac1 = static_cast<time_type>(0.333 ),
139- time_type fac2 = static_cast<time_type>(6.0 ),
140- time_type beta = static_cast<time_type>(0.0 ),
141- time_type dt_max = static_cast<time_type>(1e100 ),
142- time_type dt_min = static_cast<time_type>(1e-16 ),
143- int nmax = 100000,
144- int nstiff = 1000)
145- : base_type(std::move(sys), rtol, atol),
146- safety_factor_(safety), fac1_(fac1), fac2_(fac2), beta_(beta),
147- dt_max_(dt_max), dt_min_(dt_min), nmax_(nmax), nstiff_(nstiff) {}
167+ time_type atol = static_cast<time_type>(1e-10 ))
168+ : base_type(std::move(sys), rtol, atol) {}
148169
149170 void step (state_type& state, time_type dt) override {
150171 adaptive_step (state, dt);
@@ -172,17 +193,29 @@ class DOP853Integrator : public AdaptiveIntegrator<S, T> {
172193 state_type error = StateCreator<state_type>::create (state);
173194 dop853_step (state, y_new, error, current_dt);
174195 nfcn_ += 12 ; // 12 stages per step
175- time_type err_norm = this ->error_norm (error, y_new);
196+
197+ // Fortran error norm (ERR, ERR2, DENO, etc.)
198+ time_type err = 0.0 , err2 = 0.0 ;
199+ for (std::size_t i = 0 ; i < state.size (); ++i) {
200+ time_type sk = this ->atol_ + this ->rtol_ * std::max (std::abs (state[i]), std::abs (y_new[i]));
201+ // Fortran: ERRI=K4(I)-BHH1*K1(I)-BHH2*K9(I)-BHH3*K3(I) (here, error[i] is 8th-5th order diff)
202+ // We use error[i] as the embedded error estimate, so for full Fortran, you may need to store all k's
203+ err2 += (error[i] / sk) * (error[i] / sk); // proxy for Fortran's ERR2
204+ err += (error[i] / sk) * (error[i] / sk); // Fortran's ERR
205+ }
206+ time_type deno = err + 0.01 * err2;
207+ if (deno <= 0.0 ) deno = 1.0 ;
208+ err = std::abs (current_dt) * err * std::sqrt (1.0 / (state.size () * deno));
176209
177210 // Fortran: FAC11 = ERR**EXPO1, FAC = FAC11 / FACOLD**BETA
178211 time_type expo1 = 1.0 / 8.0 - beta_ * 0.2 ;
179- time_type fac11 = std::pow (std::max (err_norm , 1e-16 ), expo1);
212+ time_type fac11 = std::pow (std::max (err , 1e-16 ), expo1);
180213 time_type fac = fac11 / std::pow (facold_, beta_);
181214 fac = std::max (fac2_, std::min (fac1_, fac / safety_factor_));
182215 time_type next_dt = current_dt / fac;
183216
184- if (err_norm <= 1.0 ) {
185- facold_ = std::max (err_norm , static_cast <time_type>(1e-4 ));
217+ if (err <= 1.0 ) {
218+ facold_ = std::max (err , static_cast <time_type>(1e-4 ));
186219 naccpt_++;
187220 nstep_++;
188221 state = y_new;
@@ -192,7 +225,6 @@ class DOP853Integrator : public AdaptiveIntegrator<S, T> {
192225 // Compute HLAMB = |h| * sqrt(stnum / stden)
193226 time_type stnum = 0 , stden = 0 ;
194227 for (std::size_t i = 0 ; i < state.size (); ++i) {
195- // Use error and state difference as proxy (not exact Fortran, but close)
196228 stnum += (error[i]) * (error[i]);
197229 stden += (y_new[i] - state[i]) * (y_new[i] - state[i]);
198230 }
0 commit comments