Dune::Copasi
Loading...
Searching...
No Matches
stepper.hh
Go to the documentation of this file.
1#ifndef DUNE_COPASI_COMMON_STEPPERS_HH
2#define DUNE_COPASI_COMMON_STEPPERS_HH
3
6
7#include <dune/pdelab/common/convergence/reason.hh>
8#include <dune/pdelab/common/trace.hh>
9#include <dune/pdelab/operator/operator.hh>
10
11#include <dune/common/float_cmp.hh>
12#include <dune/common/parametertree.hh>
13
14#include <fmt/core.h>
15
16#include <spdlog/spdlog.h>
17
18#include <any>
19#include <concepts>
20#include <cstddef>
21#include <memory>
22#include <tuple>
23
24namespace Dune::Copasi {
25
36template<std::copyable State, class TimeQuantity, class DurationQuantity>
38{
39 std::function<TimeQuantity&(State&)> _time_mut;
40 std::function<const TimeQuantity&(const State&)> _time_const;
41
42public:
51 TimeStepper(std::function<TimeQuantity&(State&)> time_mut,
52 std::function<const TimeQuantity&(const State&)> time_const)
53 : _time_mut(std::move(time_mut))
54 , _time_const(std::move(time_const))
55 {
56 }
57
59 template<std::same_as<void> = void>
60 requires requires(const State& cstate, State& mstate) {
61 {
62 cstate.time
63 } -> std::convertible_to<const TimeQuantity&>;
64 {
65 mstate.time
66 } -> std::convertible_to<TimeQuantity&>;
67 }
69 : TimeStepper{ &State::time, &State::time }
70 {
71 }
72
73 TimeStepper(const TimeStepper&) = delete;
75
78
80 virtual ~TimeStepper() = default;
81
95 virtual PDELab::ErrorCondition do_step(PDELab::OneStep<State>& one_step,
96 const State& in,
97 State& out,
98 DurationQuantity& dt) const
99 {
100 [[maybe_unused]] uint64_t timestamp = perfetto::TrackEvent::GetTraceTimeNs();
101 TRACE_EVENT("dune", "TimeStep", timestamp);
102 TRACE_COUNTER("dune", "Stepper::ΔTime", timestamp, dt);
103
104 TimeQuantity time = _time_const(in);
105 TimeQuantity time_next = time + dt;
106 spdlog::info("Evaluating time step: {:.2e}s + {:.2e}s -> {:.2e}s",
108 dt,
110
111 // set stepper with time-stepping parameters
112 one_step["duration"] = dt;
113 one_step["time"] = time;
114
115 // advance one step from `in` and store result in `out`
116 auto error = one_step.apply(in, out);
117
118 if (not error) {
119 _time_mut(out) = time_next; // set new time in resulting state
120 } else {
121 spdlog::warn("Time step failed: {}", error.message());
122 }
123 TRACE_COUNTER("dune", "Stepper::Converged", timestamp, bool(not error));
124 return error;
125 }
126
145 PDELab::ErrorCondition evolve(
146 PDELab::OneStep<State>& one_step,
147 const State& in,
148 State& out,
150 TimeQuantity end_time,
151 std::function<void(const State&)> callable = [](const State&) {},
152 bool snap_to_end_time = true) const
153 {
154 TimeQuantity time = _time_const(in);
155 spdlog::info("Evolving system: {:.2e}s -> {:.2e}s", time, end_time);
156 out = in;
157 auto prev_out = in;
158 // if snap to end time, stop loop 2*dt before final time, otherwise 1*dt
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);
164 if (error) {
165 spdlog::warn("Evolving system could not approach final time");
166 return error;
167 }
168 callable(out);
169 }
170
171 if (snap_to_end_time) {
172 std::swap(prev_out, out);
174 }
175 return error;
176 }
177
192 PDELab::ErrorCondition snap_to_time(
193 PDELab::OneStep<State>& one_step,
194 const State& in,
195 State& out,
197 TimeQuantity snap_time,
198 std::function<void(const State&)> callable = [](const State&) {}) const
199 {
200 spdlog::info("Snapping current time to {:.5e}", snap_time);
201
202 // let's avoid getting into an infinite loop
203 // If max_snap_count is reached something is wrong
204 const std::size_t max_snap_count = 100;
205 std::size_t snap_count = 0;
206
207 // reduce last timestep adaptively until time is exactly reached
208 out = in;
209 auto prev_out = in;
210 TimeQuantity time;
211 PDELab::ErrorCondition error;
212 while (FloatCmp::lt(time = _time_const(out), snap_time)) {
213 // find number of time steps to fit in the (time,snap_time) range
214 auto timesteps_ratio = ((snap_time - time) / dt);
215 using std::ceil;
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");
219 }
221 spdlog::info("Snapping step size {:.5e}s -> {:.5e}s", dt, new_dt);
222 dt = new_dt;
223
224 std::swap(prev_out, out);
226
227 if (not error) {
228 callable(out);
229 } else {
230 out = prev_out;
231 if (max_snap_count == snap_count++) {
232 throw format_exception(RangeError{}, "Snapping time exceeded maximum iteration count");
233 }
234 spdlog::info("Reducing step size: {:.5e}s -> {:.5e}s", dt, dt * 0.5);
235 dt *= 0.5;
236 }
237 }
238 return error;
239 }
240};
241
252template<class State, class TimeQuantity, class DurationQuantity>
253class SimpleAdaptiveStepper : public TimeStepper<State, TimeQuantity, DurationQuantity>
254{
256
257public:
270 SimpleAdaptiveStepper(std::function<TimeQuantity&(State&)> time_mut,
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 }
281 {
282 if (FloatCmp::gt(_decrease_factor, 1.)) {
283 throw format_exception(
284 IOError{}, "Decrease factor {} must be in the range of (0,1]", _decrease_factor);
285 }
286 if (FloatCmp::lt(_decrease_factor, 0.)) {
287 throw format_exception(
288 IOError{}, "Decrease factor {} must be in the range of (0,1]", _decrease_factor);
289 }
290 if (FloatCmp::lt(_increase_factor, 1.)) {
291 throw format_exception(
292 IOError{}, "Increase factor {} must be in the range of (1,∞)", _increase_factor);
293 }
294 }
295
304 template<std::same_as<void> = void>
305 requires requires(const State& cstate, State& mstate) {
306 {
307 cstate.time
308 } -> std::convertible_to<const TimeQuantity&>;
309 {
310 mstate.time
311 } -> std::convertible_to<TimeQuantity&>;
312 }
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,
319 }
320 {
321 }
322
337 PDELab::ErrorCondition do_step(PDELab::OneStep<State>& one_step,
338 const State& in,
339 State& out,
340 DurationQuantity& dt) const override
341 {
342 PDELab::ErrorCondition error = check_dt(dt);
343 if (error) {
344 return error;
345 }
346 // try to do first step
348
349 // iterate until we succeed or reach a lower time step limit
350 while (error) {
351 spdlog::warn("Reducing step size: {:.2e}s -> {:.2e}s", dt, dt * _decrease_factor);
352 error = check_dt(dt = dt * _decrease_factor);
353 if (error) {
354 return error;
355 }
357 }
358
359 // incerease time step
360 auto new_step = dt * _increase_factor;
361 if (_max_step) {
362 new_step = std::clamp(new_step, -std::abs(_max_step.value()), std::abs(_max_step.value()));
363 }
364 spdlog::info("Increasing step size: {:.2e}s -> {:.2e}s", dt, new_step);
365
366 dt = new_step;
367 return error;
368 }
369
370private:
371 PDELab::ErrorCondition check_dt(DurationQuantity dt) const
372 {
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);
376 }
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);
380 }
381 return {};
382 }
383
384 std::optional<DurationQuantity> _min_step, _max_step;
385 double _decrease_factor{}, _increase_factor{};
386};
387
388} // namespace Dune::Copasi
389
390#endif // DUNE_COPASI_COMMON_STEPPERS_HH
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
constexpr bool is_bitflags_v
Alias for Bitflag indicator.
Definition bit_flags.hh:24