stepper_wrapper.h Source File

CPP API: stepper_wrapper.h Source File
stepper_wrapper.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Rene Schmieding
5 *
6 * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 #ifndef MIO_MATH_STEPPER_WRAPPER_H
21 #define MIO_MATH_STEPPER_WRAPPER_H
22 
24 #include "memilio/utils/logging.h"
25 
26 #include "boost/numeric/odeint/external/eigen/eigen_algebra.hpp"
27 #include "boost/numeric/odeint/external/eigen/eigen_resize.hpp"
28 #include "boost/numeric/odeint/stepper/controlled_runge_kutta.hpp"
29 #include "boost/numeric/odeint/stepper/runge_kutta_fehlberg78.hpp"
30 #include "boost/numeric/odeint/stepper/runge_kutta_cash_karp54.hpp"
31 #include <boost/core/ref.hpp>
32 // #include "boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp" // TODO: reenable once boost bug is fixed
33 
34 namespace mio
35 {
36 
37 namespace details
38 {
39 
41 template <class Value, class Time>
42 struct step_adjuster : public boost::numeric::odeint::default_step_adjuster<Value, Time> {
43  using boost::numeric::odeint::default_step_adjuster<Value, Time>::default_step_adjuster;
44  void set_dt_max(const Time& dt_max)
45  {
46  this->m_max_dt = dt_max;
47  }
48 };
49 
50 } // namespace details
51 
56 template <typename FP,
57  template <class State, class Value, class Deriv, class Time, class Algebra, class Operations, class Resizer>
58  class ControlledStepper>
60 {
61  using Algebra = boost::numeric::odeint::vector_space_algebra;
62  using Operations = typename boost::numeric::odeint::operations_dispatcher<Eigen::VectorX<FP>>::operations_type;
63  using Resizer = boost::numeric::odeint::initially_resizer;
64  using ErrorChecker = boost::numeric::odeint::default_error_checker<FP, Algebra, Operations>;
66  // Note: use a reference_wrapper so we can both update dt_max, and replace the stepper to change tolerances
67  using Stepper = boost::numeric::odeint::controlled_runge_kutta<
68  ControlledStepper<Eigen::VectorX<FP>, FP, Eigen::VectorX<FP>, FP, Algebra, Operations, Resizer>, ErrorChecker,
69  boost::reference_wrapper<StepAdjuster>>;
70  static constexpr bool is_fsal_stepper = std::is_same_v<typename Stepper::stepper_type::stepper_category,
71  boost::numeric::odeint::explicit_error_stepper_fsal_tag>;
72  static_assert(!is_fsal_stepper,
73  "FSAL steppers cannot be used until https://github.com/boostorg/odeint/issues/72 is resolved.");
74 
75 public:
83  ControlledStepperWrapper(FP abs_tol = 1e-10, FP rel_tol = 1e-5, FP dt_min = std::numeric_limits<FP>::min(),
84  FP dt_max = std::numeric_limits<FP>::max())
85  : OdeIntegratorCore<FP>(dt_min, dt_max)
86  , m_abs_tol(abs_tol)
87  , m_rel_tol(rel_tol)
90  {
91  }
92 
93  // if needed, make sure to create a new m_stepper
98 
99  std::unique_ptr<OdeIntegratorCore<FP>> clone() const override
100  {
101  return std::make_unique<ControlledStepperWrapper>(m_abs_tol, m_rel_tol, this->get_dt_min(), this->get_dt_max());
102  }
103 
112  bool step(const mio::DerivFunction<FP>& f, Eigen::Ref<const Eigen::VectorX<FP>> yt, FP& t, FP& dt,
113  Eigen::Ref<Eigen::VectorX<FP>> ytp1) const override
114  {
115  using boost::numeric::odeint::fail;
116  using std::max;
117  assert(0 <= this->get_dt_min());
118  assert(this->get_dt_min() <= this->get_dt_max());
119  // synchronise dt_max of the base class with the stepper
121  // warn about (upcoming) restrictions to dt
122  if (dt < this->get_dt_min() || dt > this->get_dt_max()) {
123  mio::log_warning("IntegratorCore: Restricting given step size dt = {} to [{}, {}].", dt, this->get_dt_min(),
124  this->get_dt_max());
125  }
126  // set initial values for exit conditions
127  auto step_result = fail;
128  bool is_dt_valid = true;
129  // make a integration step, adapting dt to a possibly larger value on success,
130  // or a strictly smaller value on fail.
131  // stop only on a successful step or a failed step size adaption (w.r.t. the minimal step size dt_min)
132  while (step_result == fail && is_dt_valid) {
133  if (dt < this->get_dt_min()) {
134  is_dt_valid = false;
135  dt = this->get_dt_min();
136  }
137  // we use the scheme try_step(sys, in, t, out, dt) with sys=f, in=y(t), out=y(t').
138  // this is similiar to do_step, but it can adapt the step size dt. If successful, it also updates t.
139  // Note: the resizer used by m_stepper restricts dt to dt_max (via making a failed step)
140  if constexpr (!is_fsal_stepper) { // prevent compile time errors with fsal steppers
141  step_result = m_stepper.try_step(
142  // reorder arguments of the DerivFunction f for the stepper
143  [&](const Eigen::VectorX<FP>& x, Eigen::VectorX<FP>& dxds, FP s) {
144  f(x, s, dxds);
145  },
146  yt, t, ytp1, dt);
147  }
148  }
149  // bound dt from below
150  // the last adaptive step (successful or not) may have calculated a new step size smaller than m_dt_min
151 
152  dt = max<FP>(dt, this->get_dt_min());
153  // check whether the last step failed (which means that m_dt_min was still too large to suffice tolerances)
154  if (step_result == fail) {
155  // adaptive stepping failed, but we still return the result of the last attempt
156  t += this->get_dt_min();
157  return false;
158  }
159  else {
160  // successfully made an integration step
161  return true;
162  }
163  }
164 
166  void set_abs_tolerance(FP abs_tol)
167  {
168  m_abs_tol = abs_tol;
170  }
171 
173  void set_rel_tolerance(FP rel_tol)
174  {
175  m_rel_tol = rel_tol;
177  }
178 
180  void set_dt_min(FP dt_min)
181  {
182  this->get_dt_min() = dt_min;
183  }
184 
186  void set_dt_max(FP dt_max)
187  {
188  this->get_dt_max() = dt_max;
189  }
190 
191 private:
194  {
195  // for more options see: boost/boost/numeric/odeint/stepper/controlled_runge_kutta.hpp
196  return Stepper(ErrorChecker(m_abs_tol, m_rel_tol), boost::ref(m_step_adjuster));
197  }
198 
201  mutable Stepper m_stepper;
202 };
203 
208 template <typename FP,
209  template <class State, class Value, class Deriv, class Time, class Algebra, class Operations, class Resizer>
210  class ExplicitStepper>
212 {
213 public:
214  using Stepper =
215  ExplicitStepper<Eigen::VectorX<FP>, FP, Eigen::VectorX<FP>, FP, boost::numeric::odeint::vector_space_algebra,
216  typename boost::numeric::odeint::operations_dispatcher<Eigen::VectorX<FP>>::operations_type,
217  boost::numeric::odeint::initially_resizer>;
218 
223  : mio::OdeIntegratorCore<FP>(FP{}, FP{})
224  {
225  }
226 
227  std::unique_ptr<OdeIntegratorCore<FP>> clone() const override
228  {
229  return std::make_unique<ExplicitStepperWrapper>(*this);
230  }
231 
240  bool step(const mio::DerivFunction<FP>& f, Eigen::Ref<const Eigen::VectorX<FP>> yt, FP& t, FP& dt,
241  Eigen::Ref<Eigen::VectorX<FP>> ytp1) const override
242  {
243  // copy the values from y(t) to ytp1, since we use the scheme do_step(sys, inout, t, dt) with
244  // sys=f, inout=y(t) for in-place computation - also, this form is shared by several steppers in boost
245  ytp1 = yt;
246  m_stepper.do_step(
247  // reorder arguments of the DerivFunction f for the stepper
248  [&](const Eigen::VectorX<FP>& x, Eigen::VectorX<FP>& dxds, FP s) {
249  f(x, s, dxds);
250  },
251  ytp1, t, dt);
252  // update time (it is not modified by do_step)
253  t += dt;
254  return true; // no step size adaption
255  }
256 
257 private:
258  mutable Stepper m_stepper;
259 };
260 
261 } // namespace mio
262 
263 #endif // MIO_MATH_STEPPER_WRAPPER_H
This is an adaptive IntegratorCore.
Definition: stepper_wrapper.h:60
void set_dt_max(FP dt_max)
Definition: stepper_wrapper.h:186
Stepper m_stepper
A stepper instance used for integration.
Definition: stepper_wrapper.h:201
bool step(const mio::DerivFunction< FP > &f, Eigen::Ref< const Eigen::VectorX< FP >> yt, FP &t, FP &dt, Eigen::Ref< Eigen::VectorX< FP >> ytp1) const override
Make a single integration step on a system of ODEs and adapt the step size dt.
Definition: stepper_wrapper.h:112
boost::numeric::odeint::default_error_checker< FP, Algebra, Operations > ErrorChecker
Definition: stepper_wrapper.h:64
FP m_abs_tol
Definition: stepper_wrapper.h:199
typename boost::numeric::odeint::operations_dispatcher< Eigen::VectorX< FP > >::operations_type Operations
Definition: stepper_wrapper.h:62
boost::numeric::odeint::controlled_runge_kutta< ControlledStepper< Eigen::VectorX< FP >, FP, Eigen::VectorX< FP >, FP, Algebra, Operations, Resizer >, ErrorChecker, boost::reference_wrapper< StepAdjuster > > Stepper
Definition: stepper_wrapper.h:69
static constexpr bool is_fsal_stepper
Definition: stepper_wrapper.h:70
ControlledStepperWrapper & operator=(ControlledStepperWrapper &&other)=delete
boost::numeric::odeint::initially_resizer Resizer
Definition: stepper_wrapper.h:63
void set_abs_tolerance(FP abs_tol)
Definition: stepper_wrapper.h:166
ControlledStepperWrapper & operator=(const ControlledStepperWrapper &other)=delete
Stepper create_stepper()
(Re)initialize the internal stepper.
Definition: stepper_wrapper.h:193
ControlledStepperWrapper(ControlledStepperWrapper &&other)=delete
void set_dt_min(FP dt_min)
Definition: stepper_wrapper.h:180
void set_rel_tolerance(FP rel_tol)
Definition: stepper_wrapper.h:173
std::unique_ptr< OdeIntegratorCore< FP > > clone() const override
Definition: stepper_wrapper.h:99
ControlledStepperWrapper(const ControlledStepperWrapper &other)=delete
boost::numeric::odeint::vector_space_algebra Algebra
Definition: stepper_wrapper.h:61
FP m_rel_tol
Absolute and relative tolerances for integration.
Definition: stepper_wrapper.h:199
StepAdjuster m_step_adjuster
Defines step sizing. Holds a copy of dt_max that has to be updated.
Definition: stepper_wrapper.h:200
ControlledStepperWrapper(FP abs_tol=1e-10, FP rel_tol=1e-5, FP dt_min=std::numeric_limits< FP >::min(), FP dt_max=std::numeric_limits< FP >::max())
Set up the integrator.
Definition: stepper_wrapper.h:83
This is a non-adaptive IntegratorCore.
Definition: stepper_wrapper.h:212
bool step(const mio::DerivFunction< FP > &f, Eigen::Ref< const Eigen::VectorX< FP >> yt, FP &t, FP &dt, Eigen::Ref< Eigen::VectorX< FP >> ytp1) const override
Make a single integration step on a system of ODEs with fixed step size dt.
Definition: stepper_wrapper.h:240
Stepper m_stepper
A stepper instance used for integration.
Definition: stepper_wrapper.h:258
ExplicitStepper< Eigen::VectorX< FP >, FP, Eigen::VectorX< FP >, FP, boost::numeric::odeint::vector_space_algebra, typename boost::numeric::odeint::operations_dispatcher< Eigen::VectorX< FP > >::operations_type, boost::numeric::odeint::initially_resizer > Stepper
Definition: stepper_wrapper.h:217
std::unique_ptr< OdeIntegratorCore< FP > > clone() const override
Definition: stepper_wrapper.h:227
ExplicitStepperWrapper()
Set up the integrator.
Definition: stepper_wrapper.h:222
Interface class defining the integration step used in a SystemIntegrator.
Definition: integrator.h:48
FP & get_dt_min()
Access lower bound to the step size dt.
Definition: integrator.h:101
FP & get_dt_max()
Access upper bound to the step size dt.
Definition: integrator.h:117
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type min(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2599
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type max(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2596
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
void log_warning(spdlog::string_view_t fmt, const Args &... args)
Definition: logging.h:126
std::function< void(Eigen::Ref< const Eigen::VectorX< FP > > y, FP t, Eigen::Ref< Eigen::VectorX< FP > > dydt)> DerivFunction
Function template to be integrated.
Definition: integrator.h:39
Extends the default_step_adjuster with a setter for dt_max.
Definition: stepper_wrapper.h:42
void set_dt_max(const Time &dt_max)
Definition: stepper_wrapper.h:44