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;
28 enum class MethodType {
34 time_type rtol =
static_cast<time_type
>(1e-6),
35 time_type atol =
static_cast<time_type
>(1e-9))
37 current_method_(MethodType::ADAMS),
38 stiffness_detection_frequency_(10),
40 consecutive_stiff_steps_(0),
41 consecutive_nonstiff_steps_(0),
42 stiffness_threshold_(
static_cast<time_type
>(3.25)),
43 has_previous_values_(
false) {
46 rk45_integrator_ = std::make_unique<RK45Integrator<S>>(
47 [
this](time_type t,
const state_type& y, state_type& dydt) {
48 this->sys_(t, y, dydt);
51 bdf_integrator_ = std::make_unique<BDFIntegrator<S>>(
52 [
this](time_type t,
const state_type& y, state_type& dydt) {
53 this->sys_(t, y, dydt);
57 void step(state_type& state, time_type dt)
override {
58 adaptive_step(state, dt);
61 time_type adaptive_step(state_type& state, time_type dt)
override {
65 if (!rk45_integrator_) {
66 rk45_integrator_ = std::make_unique<RK45Integrator<S>>(
67 [
this](time_type t,
const state_type& y, state_type& dydt) {
68 this->sys_(t, y, dydt);
69 }, this->rtol_, this->atol_);
72 rk45_integrator_->set_time(this->current_time_);
73 time_type result_dt = rk45_integrator_->adaptive_step(state, dt);
74 this->set_time(rk45_integrator_->current_time());
79 MethodType get_current_method()
const {
return current_method_; }
81 void set_stiffness_detection_frequency(
int frequency) {
82 stiffness_detection_frequency_ = frequency;
85 void set_stiffness_threshold(time_type threshold) {
86 stiffness_threshold_ = threshold;
89 void set_tolerances(time_type rtol, time_type atol) {
92 if (rk45_integrator_) {
93 rk45_integrator_->set_tolerances(rtol, atol);
95 if (bdf_integrator_) {
96 bdf_integrator_->set_tolerances(rtol, atol);
100 void integrate(state_type& state, time_type dt, time_type end_time)
override {
102 if (!has_previous_values_) {
105 has_previous_values_ =
false;
109 base_type::integrate(state, dt, end_time);
113 MethodType current_method_;
114 std::unique_ptr<RK45Integrator<S>> rk45_integrator_;
115 std::unique_ptr<BDFIntegrator<S>> bdf_integrator_;
117 int stiffness_detection_frequency_;
119 int consecutive_stiff_steps_;
120 int consecutive_nonstiff_steps_;
121 time_type stiffness_threshold_;
126 bool has_previous_values_;
128 void detect_stiffness(
const state_type& y, time_type dt) {
132 if (!has_previous_values_) {
136 this->sys_(this->current_time_, y, f_prev_);
137 has_previous_values_ =
true;
142 this->sys_(this->current_time_, y, f_current);
145 time_type stiffness_ratio = estimate_stiffness_ratio(y, f_current, dt);
147 if (stiffness_ratio > stiffness_threshold_) {
149 consecutive_stiff_steps_++;
150 consecutive_nonstiff_steps_ = 0;
152 if (consecutive_stiff_steps_ >= 3 && current_method_ == MethodType::ADAMS) {
157 consecutive_nonstiff_steps_++;
158 consecutive_stiff_steps_ = 0;
160 if (consecutive_nonstiff_steps_ >= 3 && current_method_ == MethodType::BDF) {
170 time_type estimate_stiffness_ratio(
const state_type& y,
const state_type& f, time_type dt) {
174 time_type epsilon =
static_cast<time_type
>(1e-8);
178 time_type jacobian_norm =
static_cast<time_type
>(0);
179 time_type f_norm =
static_cast<time_type
>(0);
181 for (std::size_t i = 0; i < y.size(); ++i) {
182 auto y_it = y.begin();
183 auto f_it = f.begin();
184 auto y_pert_it = y_pert.begin();
185 auto f_pert_it = f_pert.begin();
189 y_pert_it[i] += epsilon;
192 this->sys_(this->current_time_, y_pert, f_pert);
195 time_type df_dy = (f_pert_it[i] - f_it[i]) / epsilon;
198 jacobian_norm += std::abs(df_dy * dt);
199 f_norm += std::abs(f_it[i]);
202 if (f_norm <
static_cast<time_type
>(1e-12)) {
203 return static_cast<time_type
>(0);
206 return jacobian_norm / f_norm;
209 void switch_to_bdf(
const state_type& y) {
210 current_method_ = MethodType::BDF;
212 if (!bdf_integrator_) {
213 bdf_integrator_ = std::make_unique<BDFIntegrator<S>>(
214 [
this](time_type t,
const state_type& y, state_type& dydt) {
215 this->sys_(t, y, dydt);
216 }, this->rtol_, this->atol_);
219 bdf_integrator_->set_time(this->current_time_);
222 void switch_to_adams(
const state_type& y) {
223 current_method_ = MethodType::ADAMS;
225 if (!rk45_integrator_) {
226 rk45_integrator_ = std::make_unique<RK45Integrator<S>>(
227 [
this](time_type t,
const state_type& y, state_type& dydt) {
228 this->sys_(t, y, dydt);
229 }, this->rtol_, this->atol_);
232 rk45_integrator_->set_time(this->current_time_);