1#ifndef DUNE_COPASI_COMMON_STEPPERS_HH
2#define DUNE_COPASI_COMMON_STEPPERS_HH
7#include <dune/pdelab/common/convergence/reason.hh>
8#include <dune/pdelab/common/trace.hh>
9#include <dune/pdelab/operator/operator.hh>
11#include <dune/common/float_cmp.hh>
12#include <dune/common/parametertree.hh>
16#include <spdlog/spdlog.h>
36template<std::copyable State,
class TimeQuantity,
class DurationQuantity>
39 std::function<TimeQuantity&(State&)> _time_mut;
40 std::function<
const TimeQuantity&(
const State&)> _time_const;
52 std::function<
const TimeQuantity&(
const State&)> time_const)
53 : _time_mut(std::move(time_mut))
54 , _time_const(std::move(time_const))
59 template<std::same_as<
void> =
void>
60 requires requires(
const State& cstate, State& mstate) {
63 } -> std::convertible_to<const TimeQuantity&>;
66 } -> std::convertible_to<TimeQuantity&>;
95 virtual PDELab::ErrorCondition
do_step(PDELab::OneStep<State>& one_step,
98 DurationQuantity& dt)
const
100 [[maybe_unused]] uint64_t timestamp = perfetto::TrackEvent::GetTraceTimeNs();
101 TRACE_EVENT(
"dune",
"TimeStep", timestamp);
102 TRACE_COUNTER(
"dune",
"Stepper::ΔTime", timestamp, dt);
104 TimeQuantity time = _time_const(in);
105 TimeQuantity time_next = time + dt;
106 spdlog::info(
"Evaluating time step: {:.2e}s + {:.2e}s -> {:.2e}s",
112 one_step[
"duration"] = dt;
113 one_step[
"time"] = time;
116 auto error = one_step.apply(in, out);
119 _time_mut(out) = time_next;
121 spdlog::warn(
"Time step failed: {}", error.message());
123 TRACE_COUNTER(
"dune",
"Stepper::Converged", timestamp,
bool(not error));
146 PDELab::OneStep<State>& one_step,
149 DurationQuantity& dt,
150 TimeQuantity end_time,
151 std::function<
void(
const State&)> callable = [](
const State&) {},
152 bool snap_to_end_time =
true)
const
154 TimeQuantity time = _time_const(in);
155 spdlog::info(
"Evolving system: {:.2e}s -> {:.2e}s", time, end_time);
159 const double stop_dt = snap_to_end_time ? 2. : 1.;
160 PDELab::ErrorCondition error;
161 while (FloatCmp::le<TimeQuantity>(_time_const(out) + stop_dt * dt, end_time)) {
162 std::swap(prev_out, out);
163 error =
do_step(one_step, prev_out, out, dt);
165 spdlog::warn(
"Evolving system could not approach final time");
171 if (snap_to_end_time) {
172 std::swap(prev_out, out);
173 error =
snap_to_time(one_step, prev_out, out, dt, end_time, callable);
193 PDELab::OneStep<State>& one_step,
196 DurationQuantity& dt,
197 TimeQuantity snap_time,
198 std::function<
void(
const State&)> callable = [](
const State&) {})
const
200 spdlog::info(
"Snapping current time to {:.5e}", snap_time);
204 const std::size_t max_snap_count = 100;
205 std::size_t snap_count = 0;
211 PDELab::ErrorCondition error;
212 while (FloatCmp::lt(time = _time_const(out), snap_time)) {
214 auto timesteps_ratio = ((snap_time - time) / dt);
216 const auto timesteps_until_end =
static_cast<int>(ceil(timesteps_ratio));
217 if (timesteps_until_end <= 0) {
218 throw format_exception(MathError{},
"Timestep doesn't make advances towards snap step");
220 DurationQuantity new_dt = (snap_time - time) / timesteps_until_end;
221 spdlog::info(
"Snapping step size {:.5e}s -> {:.5e}s", dt, new_dt);
224 std::swap(prev_out, out);
225 error =
do_step(one_step, prev_out, out, dt);
231 if (max_snap_count == snap_count++) {
232 throw format_exception(RangeError{},
"Snapping time exceeded maximum iteration count");
234 spdlog::info(
"Reducing step size: {:.5e}s -> {:.5e}s", dt, dt * 0.5);
252template<
class State,
class TimeQuantity,
class DurationQuantity>
271 std::function<
const TimeQuantity&(
const State&)> time_const,
272 double decrease_factor = 0.5,
273 double increase_factor = 1.5,
274 std::optional<DurationQuantity> min_step = {},
275 std::optional<DurationQuantity> max_step = {})
276 : Base{ std::move(time_mut), std::move(time_const) }
277 , _min_step{ min_step }
278 , _max_step{ max_step }
279 , _decrease_factor{ decrease_factor }
280 , _increase_factor{ increase_factor }
282 if (FloatCmp::gt(_decrease_factor, 1.)) {
284 IOError{},
"Decrease factor {} must be in the range of (0,1]", _decrease_factor);
286 if (FloatCmp::lt(_decrease_factor, 0.)) {
288 IOError{},
"Decrease factor {} must be in the range of (0,1]", _decrease_factor);
290 if (FloatCmp::lt(_increase_factor, 1.)) {
292 IOError{},
"Increase factor {} must be in the range of (1,∞)", _increase_factor);
304 template<std::same_as<
void> =
void>
305 requires requires(
const State& cstate, State& mstate) {
308 } -> std::convertible_to<const TimeQuantity&>;
311 } -> std::convertible_to<TimeQuantity&>;
314 double increase_factor = 1.5,
315 std::optional<DurationQuantity> min_step = {},
316 std::optional<DurationQuantity> max_step = {})
318 &State::time, &State::time, decrease_factor, increase_factor, min_step, max_step,
337 PDELab::ErrorCondition
do_step(PDELab::OneStep<State>& one_step,
340 DurationQuantity& dt)
const override
342 PDELab::ErrorCondition error = check_dt(dt);
351 spdlog::warn(
"Reducing step size: {:.2e}s -> {:.2e}s", dt, dt * _decrease_factor);
352 error = check_dt(dt = dt * _decrease_factor);
360 auto new_step = dt * _increase_factor;
362 new_step = std::clamp(new_step, -std::abs(_max_step.value()), std::abs(_max_step.value()));
364 spdlog::info(
"Increasing step size: {:.2e}s -> {:.2e}s", dt, new_step);
371 PDELab::ErrorCondition check_dt(DurationQuantity dt)
const
373 if (_min_step and Dune::FloatCmp::lt(std::abs(dt), std::abs(*_min_step))) {
374 spdlog::warn(
"Time step '{}' lower limit '{}' was reached ", dt, *_min_step);
375 return make_error_condition(Dune::PDELab::Convergence::Reason::DivergedNull);
377 if (_max_step and Dune::FloatCmp::gt(std::abs(dt), std::abs(*_max_step))) {
378 spdlog::warn(
"Time step '{}' upper limit '{}' was reached ", dt, *_max_step);
379 return make_error_condition(Dune::PDELab::Convergence::Reason::DivergedNull);
384 std::optional<DurationQuantity> _min_step, _max_step;
385 double _decrease_factor{}, _increase_factor{};
Adapt an stepper into a simple adaptive time stepper.
Definition: stepper.hh:254
SimpleAdaptiveStepper(double decrease_factor=0.5, double increase_factor=1.5, std::optional< DurationQuantity > min_step={}, std::optional< DurationQuantity > max_step={})
Construct a new simple adaptive stepper.
Definition: stepper.hh:313
PDELab::ErrorCondition do_step(PDELab::OneStep< State > &one_step, const State &in, State &out, DurationQuantity &dt) const override
Perform one simple adaptive time step of the system.
Definition: stepper.hh:337
SimpleAdaptiveStepper(std::function< TimeQuantity &(State &)> time_mut, std::function< const TimeQuantity &(const State &)> time_const, double decrease_factor=0.5, double increase_factor=1.5, std::optional< DurationQuantity > min_step={}, std::optional< DurationQuantity > max_step={})
Construct a new simple adaptive stepper.
Definition: stepper.hh:270
Simple time stepper of one-step operators.
Definition: stepper.hh:38
TimeStepper(std::function< TimeQuantity &(State &)> time_mut, std::function< const TimeQuantity &(const State &)> time_const)
Construct a new time stepper.
Definition: stepper.hh:51
TimeStepper()
Construct a new time stepper.
Definition: stepper.hh:68
TimeStepper(const TimeStepper &)=delete
virtual ~TimeStepper()=default
Time stepper destructor.
TimeStepper & operator=(TimeStepper &&)=delete
virtual PDELab::ErrorCondition do_step(PDELab::OneStep< State > &one_step, const State &in, State &out, DurationQuantity &dt) const
Perform one step of the system.
Definition: stepper.hh:95
PDELab::ErrorCondition evolve(PDELab::OneStep< State > &one_step, const State &in, State &out, DurationQuantity &dt, TimeQuantity end_time, std::function< void(const State &)> callable=[](const State &) {}, bool snap_to_end_time=true) const
Evolve the system until end_time
Definition: stepper.hh:145
TimeStepper(TimeStepper &&)=delete
TimeStepper & operator=(const TimeStepper &)=delete
PDELab::ErrorCondition snap_to_time(PDELab::OneStep< State > &one_step, const State &in, State &out, DurationQuantity &dt, TimeQuantity snap_time, std::function< void(const State &)> callable=[](const State &) {}) const
Snap the output to a specific time.
Definition: stepper.hh:192
#define DUNE_COPASI_FMT_STYLED_BOLD(key)
Definition: fmt_style.hh:13
Definition: axis_names.hh:7
auto format_exception(Exception &&e, fmt::format_string< Args... > format, Args &&... args)
Definition: exceptions.hh:23