@@ -56,6 +56,28 @@ class DOP853Integrator : public AdaptiveIntegrator<S, T> {
5656 using value_type = typename base_type::value_type;
5757 using system_function = typename base_type::system_function;
5858
59+ // Step size control parameters (modern C++ style, with defaults)
60+ time_type safety_factor_ = static_cast <time_type>(0.9 ); // Fortran SAFE
61+ time_type fac1_ = static_cast <time_type>(0.333 ); // Fortran FAC1 (min step size factor)
62+ time_type fac2_ = static_cast <time_type>(6.0 ); // Fortran FAC2 (max step size factor)
63+ time_type beta_ = static_cast <time_type>(0.0 ); // Fortran BETA (step size stabilization)
64+ time_type dt_max_ = static_cast <time_type>(1e100 ); // Fortran HMAX (max step size)
65+ time_type dt_min_ = static_cast <time_type>(1e-16 ); // Min step size (not in Fortran, but practical)
66+ int nmax_ = 100000 ; // Fortran NMAX (max steps)
67+ int nstiff_ = 1000 ; // Fortran NSTIFF (stiffness test interval)
68+
69+ // Stiffness detection state
70+ int iastiff_ = 0 ;
71+ int nonsti_ = 0 ;
72+ time_type hlamb_ = 0 ;
73+ time_type facold_ = static_cast <time_type>(1e-4 );
74+
75+ // For statistics (optional)
76+ int nstep_ = 0 ;
77+ int naccpt_ = 0 ;
78+ int nrejct_ = 0 ;
79+ int nfcn_ = 0 ;
80+
5981private:
6082 // Compute a good initial step size (HINIT from Fortran)
6183 time_type compute_initial_step (const state_type& y, time_type t, const system_function& sys, time_type t_end) const {
@@ -111,8 +133,18 @@ class DOP853Integrator : public AdaptiveIntegrator<S, T> {
111133public:
112134 explicit DOP853Integrator (system_function sys,
113135 time_type rtol = static_cast <time_type>(1e-8 ),
114- time_type atol = static_cast<time_type>(1e-10 ))
115- : base_type(std::move(sys), rtol, atol) {}
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) {}
116148
117149 void step (state_type& state, time_type dt) override {
118150 adaptive_step (state, dt);
@@ -132,25 +164,63 @@ class DOP853Integrator : public AdaptiveIntegrator<S, T> {
132164 // Use the system function and current state to estimate initial step
133165 current_dt = compute_initial_step (state, t, this ->sys_ , t_end);
134166 // Clamp to allowed min/max
135- current_dt = std::max (this -> dt_min_ , std::min (this -> dt_max_ , current_dt));
167+ current_dt = std::max (dt_min_, std::min (dt_max_, current_dt));
136168 }
137- const int max_attempts = 10 ;
138- for (int attempt = 0 ; attempt < max_attempts ; ++attempt) {
169+ int attempt = 0 ;
170+ for (; attempt < nmax_ ; ++attempt) {
139171 state_type y_new = StateCreator<state_type>::create (state);
140172 state_type error = StateCreator<state_type>::create (state);
141173 dop853_step (state, y_new, error, current_dt);
174+ nfcn_ += 12 ; // 12 stages per step
142175 time_type err_norm = this ->error_norm (error, y_new);
176+
177+ // Fortran: FAC11 = ERR**EXPO1, FAC = FAC11 / FACOLD**BETA
178+ time_type expo1 = 1.0 / 8.0 - beta_ * 0.2 ;
179+ time_type fac11 = std::pow (std::max (err_norm, 1e-16 ), expo1);
180+ time_type fac = fac11 / std::pow (facold_, beta_);
181+ fac = std::max (fac2_, std::min (fac1_, fac / safety_factor_));
182+ time_type next_dt = current_dt / fac;
183+
143184 if (err_norm <= 1.0 ) {
185+ facold_ = std::max (err_norm, static_cast <time_type>(1e-4 ));
186+ naccpt_++;
187+ nstep_++;
144188 state = y_new;
145189 this ->advance_time (current_dt);
146- time_type next_dt = this ->suggest_step_size (current_dt, err_norm, 8 );
147- return std::max (this ->dt_min_ , std::min (this ->dt_max_ , next_dt));
190+ // stiffness detection (Fortran HLAMB)
191+ if (nstiff_ > 0 && (naccpt_ % nstiff_ == 0 || iastiff_ > 0 )) {
192+ // Compute HLAMB = |h| * sqrt(stnum / stden)
193+ time_type stnum = 0 , stden = 0 ;
194+ for (std::size_t i = 0 ; i < state.size (); ++i) {
195+ // Use error and state difference as proxy (not exact Fortran, but close)
196+ stnum += (error[i]) * (error[i]);
197+ stden += (y_new[i] - state[i]) * (y_new[i] - state[i]);
198+ }
199+ if (stden > 0 ) hlamb_ = std::abs (current_dt) * std::sqrt (stnum / stden);
200+ if (hlamb_ > 6.1 ) {
201+ nonsti_ = 0 ;
202+ iastiff_++;
203+ if (iastiff_ == 15 ) {
204+ throw std::runtime_error (" DOP853: Problem seems to become stiff" );
205+ }
206+ } else {
207+ nonsti_++;
208+ if (nonsti_ == 6 ) iastiff_ = 0 ;
209+ }
210+ }
211+ // Clamp next step size
212+ next_dt = std::max (dt_min_, std::min (dt_max_, next_dt));
213+ // Accept and return next step size
214+ return next_dt;
148215 } else {
149- current_dt *= std::max (this ->safety_factor_ * std::pow (err_norm, -1.0 /8.0 ), static_cast <time_type>(0.1 ));
150- current_dt = std::max (current_dt, this ->dt_min_ );
216+ // Step rejected
217+ nrejct_++;
218+ nstep_++;
219+ next_dt = current_dt / std::min (fac1_, fac11 / safety_factor_);
220+ current_dt = std::max (dt_min_, next_dt);
151221 }
152222 }
153- throw std::runtime_error (" DOP853: Maximum number of step size reductions exceeded" );
223+ throw std::runtime_error (" DOP853: Maximum number of step size reductions or steps exceeded" );
154224 }
155225
156226private:
0 commit comments