1717* See the License for the specific language governing permissions and
1818* limitations under the License.
1919*/
20- #ifndef STEPPER_WRAPPER_H_
21- #define STEPPER_WRAPPER_H_
20+ #ifndef MIO_MATH_STEPPER_WRAPPER_H
21+ #define MIO_MATH_STEPPER_WRAPPER_H
2222
2323#include " memilio/math/integrator.h"
2424#include " memilio/utils/logging.h"
2828#include " boost/numeric/odeint/stepper/controlled_runge_kutta.hpp"
2929#include " boost/numeric/odeint/stepper/runge_kutta_fehlberg78.hpp"
3030#include " boost/numeric/odeint/stepper/runge_kutta_cash_karp54.hpp"
31+ #include < boost/core/ref.hpp>
3132// #include "boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp" // TODO: reenable once boost bug is fixed
3233
3334namespace mio
3435{
3536
37+ namespace details
38+ {
39+
40+ // / @brief Extends the default_step_adjuster with a setter for dt_max.
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+
3652/* *
3753 * @brief This is an adaptive IntegratorCore. It creates and manages an instance of a
3854 * boost::numeric::odeint::controlled_runge_kutta integrator, wrapped as mio::IntegratorCore.
3955 */
40- template <typename FP, template <class State , class Value , class Deriv , class Time , class Algebra , class Operations ,
41- class Resizer > class ControlledStepper >
56+ template <typename FP,
57+ template <class State , class Value , class Deriv , class Time , class Algebra , class Operations , class Resizer >
58+ class ControlledStepper >
4259class ControlledStepperWrapper : public mio ::OdeIntegratorCore<FP>
4360{
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>;
65+ using StepAdjuster = details::step_adjuster<FP, FP>;
66+ // Note: use a reference_wrapper so we can both update dt_max, and replace the stepper to change tolerances
4467 using Stepper = boost::numeric::odeint::controlled_runge_kutta<
45- ControlledStepper<Eigen::VectorX<FP>, FP, Eigen::VectorX<FP>, FP, boost::numeric::odeint::vector_space_algebra,
46- typename boost::numeric::odeint::operations_dispatcher<Eigen::VectorX<FP>>::operations_type,
47- boost::numeric::odeint::initially_resizer>>;
68+ ControlledStepper<Eigen::VectorX<FP>, FP, Eigen::VectorX<FP>, FP, Algebra, Operations, Resizer>, ErrorChecker,
69+ boost::reference_wrapper<StepAdjuster>>;
4870 static constexpr bool is_fsal_stepper = std::is_same_v<typename Stepper::stepper_type::stepper_category,
4971 boost::numeric::odeint::explicit_error_stepper_fsal_tag>;
5072 static_assert (!is_fsal_stepper,
5173 " FSAL steppers cannot be used until https://github.com/boostorg/odeint/issues/72 is resolved." );
5274
5375public:
5476 /* *
55- * @brief Set up the integrator
56- * @param abs_tol absolute tolerance
57- * @param rel_tol relative tolerance
58- * @param dt_min lower bound for time step dt
59- * @param dt_max upper bound for time step dt
77+ * @brief Set up the integrator.
78+ * @param[in] abs_tol Absolute tolerance for convergence.
79+ * @param[in] rel_tol Relative tolerance for convergence.
80+ * @param[in] dt_min Lower bound for time step dt.
81+ * @param[in] dt_max Upper bound for time step dt.
6082 */
6183 ControlledStepperWrapper (FP abs_tol = 1e-10 , FP rel_tol = 1e-5 , FP dt_min = std::numeric_limits<FP>::min(),
6284 FP dt_max = std::numeric_limits<FP>::max())
6385 : OdeIntegratorCore<FP>(dt_min, dt_max)
6486 , m_abs_tol(abs_tol)
6587 , m_rel_tol(rel_tol)
88+ , m_step_adjuster(StepAdjuster{this ->get_dt_max ()})
6689 , m_stepper(create_stepper())
6790 {
6891 }
6992
93+ // if needed, make sure to create a new m_stepper
94+ ControlledStepperWrapper (ControlledStepperWrapper&& other) = delete ;
95+ ControlledStepperWrapper (const ControlledStepperWrapper& other) = delete ;
96+ ControlledStepperWrapper& operator =(ControlledStepperWrapper&& other) = delete ;
97+ ControlledStepperWrapper& operator =(const ControlledStepperWrapper& other) = delete ;
98+
7099 std::unique_ptr<OdeIntegratorCore<FP>> clone () const override
71100 {
72- return std::make_unique<ControlledStepperWrapper>(* this );
101+ return std::make_unique<ControlledStepperWrapper>(m_abs_tol, m_rel_tol, this -> get_dt_min (), this -> get_dt_max () );
73102 }
74103
75104 /* *
@@ -80,24 +109,23 @@ class ControlledStepperWrapper : public mio::OdeIntegratorCore<FP>
80109 * @param[in,out] dt Current time step size h=dt. Overwritten by an estimated optimal step size for the next step.
81110 * @param[out] ytp1 The approximated value of y(t').
82111 */
83- bool step (const DerivFunction<FP>& f, Eigen::Ref<Eigen::VectorX<FP> const > yt, FP& t, FP& dt,
112+ bool step (const mio:: DerivFunction<FP>& f, Eigen::Ref<const Eigen::VectorX<FP>> yt, FP& t, FP& dt,
84113 Eigen::Ref<Eigen::VectorX<FP>> ytp1) const override
85114 {
86115 using boost::numeric::odeint::fail;
87116 using std::max;
88117 assert (0 <= this ->get_dt_min ());
89118 assert (this ->get_dt_min () <= this ->get_dt_max ());
90-
119+ // synchronise dt_max of the base class with the stepper
120+ m_step_adjuster.set_dt_max (this ->get_dt_max ());
121+ // warn about (upcoming) restrictions to dt
91122 if (dt < this ->get_dt_min () || dt > this ->get_dt_max ()) {
92123 mio::log_warning (" IntegratorCore: Restricting given step size dt = {} to [{}, {}]." , dt, this ->get_dt_min (),
93124 this ->get_dt_max ());
94125 }
95126 // set initial values for exit conditions
96127 auto step_result = fail;
97128 bool is_dt_valid = true ;
98- // copy vectors from the references, since the stepper cannot (trivially) handle Eigen::Ref
99- m_ytp1 = ytp1; // y(t')
100- m_yt = yt; // y(t)
101129 // make a integration step, adapting dt to a possibly larger value on success,
102130 // or a strictly smaller value on fail.
103131 // stop only on a successful step or a failed step size adaption (w.r.t. the minimal step size dt_min)
@@ -115,11 +143,9 @@ class ControlledStepperWrapper : public mio::OdeIntegratorCore<FP>
115143 [&](const Eigen::VectorX<FP>& x, Eigen::VectorX<FP>& dxds, FP s) {
116144 f (x, s, dxds);
117145 },
118- m_yt , t, m_ytp1 , dt);
146+ yt , t, ytp1 , dt);
119147 }
120148 }
121- // output the new value by copying it back to the reference
122- ytp1 = m_ytp1;
123149 // bound dt from below
124150 // the last adaptive step (successful or not) may have calculated a new step size smaller than m_dt_min
125151
@@ -160,29 +186,28 @@ class ControlledStepperWrapper : public mio::OdeIntegratorCore<FP>
160186 void set_dt_max (FP dt_max)
161187 {
162188 this ->get_dt_max () = dt_max;
163- m_stepper = create_stepper ();
164189 }
165190
166191private:
167192 // / @brief (Re)initialize the internal stepper.
168193 Stepper create_stepper ()
169194 {
170195 // for more options see: boost/boost/numeric/odeint/stepper/controlled_runge_kutta.hpp
171- return Stepper (typename Stepper::error_checker_type (m_abs_tol, m_rel_tol),
172- typename Stepper::step_adjuster_type (this ->get_dt_max ()));
196+ return Stepper (ErrorChecker (m_abs_tol, m_rel_tol), boost::ref (m_step_adjuster));
173197 }
174198
175199 FP m_abs_tol, m_rel_tol; // /< Absolute and relative tolerances for integration.
176- mutable Eigen::VectorX<FP> m_ytp1, m_yt ; // /< Temporary storage to avoid allocations in step function .
200+ mutable StepAdjuster m_step_adjuster ; // /< Defines step sizing. Holds a copy of dt_max that has to be updated .
177201 mutable Stepper m_stepper; // /< A stepper instance used for integration.
178202};
179203
180204/* *
181205 * @brief This is a non-adaptive IntegratorCore. It creates and manages an instance of an explicit stepper from
182206 * boost::numeric::odeint, wrapped as mio::IntegratorCore.
183207 */
184- template <typename FP, template <class State , class Value , class Deriv , class Time , class Algebra , class Operations ,
185- class Resizer > class ExplicitStepper >
208+ template <typename FP,
209+ template <class State , class Value , class Deriv , class Time , class Algebra , class Operations , class Resizer >
210+ class ExplicitStepper >
186211class ExplicitStepperWrapper : public mio ::OdeIntegratorCore<FP>
187212{
188213public:
@@ -212,7 +237,7 @@ class ExplicitStepperWrapper : public mio::OdeIntegratorCore<FP>
212237 * @param[in] dt Current time step size h=dt.
213238 * @param[out] ytp1 The approximated value of y(t+dt).
214239 */
215- bool step (const mio::DerivFunction<FP>& f, Eigen::Ref<Eigen::VectorX<FP> const > yt, FP& t, FP& dt,
240+ bool step (const mio::DerivFunction<FP>& f, Eigen::Ref<const Eigen::VectorX<FP>> yt, FP& t, FP& dt,
216241 Eigen::Ref<Eigen::VectorX<FP>> ytp1) const override
217242 {
218243 // copy the values from y(t) to ytp1, since we use the scheme do_step(sys, inout, t, dt) with
@@ -235,4 +260,4 @@ class ExplicitStepperWrapper : public mio::OdeIntegratorCore<FP>
235260
236261} // namespace mio
237262
238- #endif
263+ #endif // MIO_MATH_STEPPER_WRAPPER_H
0 commit comments