GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/common/timeloop.hh
Date: 2024-05-04 19:09:25
Exec Total Coverage
Lines: 172 187 92.0%
Functions: 29 38 76.3%
Branches: 243 456 53.3%

Line Branch Exec Source
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
3 //
4 // SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 /*!
8 * \file
9 * \ingroup Core
10 * \brief Manages the handling of time dependent problems
11 */
12 #ifndef DUMUX_TIME_LOOP_HH
13 #define DUMUX_TIME_LOOP_HH
14
15 #include <algorithm>
16 #include <queue>
17 #include <iomanip>
18 #include <chrono>
19 #include <type_traits>
20 #include <initializer_list>
21 #include <optional>
22
23 #include <dune/common/float_cmp.hh>
24 #include <dune/common/timer.hh>
25
26 #include <dune/common/parallel/communication.hh>
27
28 #include <dune/common/parallel/mpihelper.hh>
29 #include <dune/common/exceptions.hh>
30
31 #include <dumux/common/typetraits/typetraits.hh>
32 #include <dumux/common/parameters.hh>
33 #include <dumux/io/format.hh>
34
35 namespace Dumux {
36
37
38 #ifndef DOXYGEN
39 namespace Detail::TimeLoop {
40
41 template<typename Scalar, typename R, typename P>
42 Scalar toSeconds(std::chrono::duration<R, P> duration)
43 {
44 using Second = std::chrono::duration<Scalar, std::ratio<1>>;
45 106 return std::chrono::duration_cast<Second>(duration).count();
46 }
47
48 template<typename Scalar, typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
49 Scalar toSeconds(T duration)
50 { return static_cast<Scalar>(duration); }
51
52 // overload for nice static_assert message in case of unuspported type
53 template<typename Scalar, typename T, std::enable_if_t<!std::is_floating_point_v<T>, bool> = true>
54 Scalar toSeconds(T duration)
55 { static_assert(AlwaysFalse<T>::value, "Given type not supported for representation of time values"); }
56
57 } // namespace Detail::TimeLoop
58 #endif // DOXYGEN
59
60 /*!
61 * \ingroup Core
62 * \brief Manages the handling of time dependent problems.
63 *
64 * This class facilitates the time management of the simulation.
65 * It doesn't manage any user data, but keeps track of what the
66 * current time, time step size and "episode" of the
67 * simulation is. It triggers the initialization of the problem and
68 * is responsible for the time control of a simulation run.
69 *
70 * The time manager allows to specify a sequence of "episodes" which
71 * determine the boundary conditions of a problem. This approach
72 * is handy if the problem is not static, i.e. the boundary
73 * conditions change over time.
74 *
75 * An episode is a span of simulated time in which
76 * the problem behaves in a specific way. It is characterized by
77 * the (simulation) time it starts, its length and a consecutive
78 * index starting at 0.
79 *
80 * \note Time and time step sizes are in units of seconds
81 */
82 template<class S>
83 class TimeLoopBase
84 {
85 public:
86 using Scalar = S;
87
88 //! Abstract base class needs virtual constructor
89 virtual ~TimeLoopBase() {};
90
91 /*!
92 * \brief Return the time \f$\mathrm{[s]}\f$ before the time integration.
93 * To get the time after the time integration you have to add timeStepSize() to
94 * time().
95 */
96 virtual Scalar time() const = 0;
97
98 /*!
99 * \brief Returns the suggested time step length \f$\mathrm{[s]}\f$
100 */
101 virtual Scalar timeStepSize() const = 0;
102
103 /*!
104 * \brief Get the maximum possible time step size \f$\mathrm{[s]}\f$
105 */
106 virtual Scalar maxTimeStepSize() const = 0;
107
108 /*!
109 * \brief Advance to the next time step.
110 */
111 virtual void advanceTimeStep() = 0;
112
113 /*!
114 * \brief Set the current time step size to a given value.
115 * \param dt The new value for the time step size \f$\mathrm{[s]}\f$
116 */
117 virtual void setTimeStepSize(Scalar dt) = 0;
118
119 /*!
120 * \brief Set the current time step size to a given value.
121 * \param dt The new value for the time step size \f$\mathrm{[s]}\f$
122 */
123 template<class Rep, class Period>
124 void setTimeStepSize(std::chrono::duration<Rep, Period> dt)
125
0/8
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
8 { setTimeStepSize(Detail::TimeLoop::toSeconds<Scalar>(dt)); }
126
127 /*!
128 * \brief Returns true if the simulation is finished.
129 */
130 virtual bool finished() const = 0;
131 };
132
133 /*!
134 * \ingroup Core
135 * \brief The default time loop for instationary simulations
136 */
137 template <class Scalar>
138
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
10 class TimeLoop : public TimeLoopBase<Scalar>
139 {
140 public:
141 364 TimeLoop(Scalar startTime, Scalar dt, Scalar tEnd, bool verbose = true)
142 364 : timer_(false)
143 {
144 364 reset(startTime, dt, tEnd, verbose);
145 364 }
146
147 template<class Rep1, class Period1,
148 class Rep2, class Period2,
149 class Rep3, class Period3>
150 8 TimeLoop(std::chrono::duration<Rep1, Period1> startTime,
151 std::chrono::duration<Rep2, Period2> dt,
152 std::chrono::duration<Rep3, Period3> tEnd,
153 bool verbose = true)
154 : TimeLoop(
155 Detail::TimeLoop::toSeconds<Scalar>(startTime),
156 Detail::TimeLoop::toSeconds<Scalar>(dt),
157 Detail::TimeLoop::toSeconds<Scalar>(tEnd),
158 verbose
159
8/16
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
26 ){}
160
161 /*!
162 * \name Simulated time and time step management
163 * @{
164 */
165
166 /*!
167 * \brief Tells the time loop to start tracking the time.
168 */
169 void start()
170 {
171
6/12
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 4 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 4 times.
✗ Branch 15 not taken.
357 timer_.start();
172 }
173
174 /*!
175 * \brief Tells the time loop to stop tracking the time.
176 * \return the wall clock time (CPU time) spent until now
177 */
178 double stop()
179 {
180 4 return timer_.stop();
181 }
182
183 /*!
184 * \brief Reset the timer
185 */
186 void resetTimer()
187 {
188
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 timer_.reset();
189 }
190
191 /*!
192 * \brief Reset the time loop
193 */
194 template<class Rep1, class Period1,
195 class Rep2, class Period2,
196 class Rep3, class Period3>
197 void reset(std::chrono::duration<Rep1, Period1> startTime,
198 std::chrono::duration<Rep2, Period2> dt,
199 std::chrono::duration<Rep3, Period3> tEnd,
200 bool verbose = true)
201 {
202
0/16
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
16 reset(
203 Detail::TimeLoop::toSeconds<Scalar>(startTime),
204 Detail::TimeLoop::toSeconds<Scalar>(dt),
205 Detail::TimeLoop::toSeconds<Scalar>(tEnd)
206 );
207 }
208
209 /*!
210 * \brief Reset the time loop
211 */
212 368 void reset(Scalar startTime, Scalar dt, Scalar tEnd, bool verbose = true)
213 {
214 368 verbose_ =
215
3/4
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 348 times.
736 verbose &&
216
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 348 times.
368 Dune::MPIHelper::getCommunication().rank() == 0;
217
218 368 startTime_ = startTime;
219 368 time_ = startTime;
220 368 endTime_ = tEnd;
221
222 368 previousTimeStepSize_ = 0.0;
223 368 userSetMaxTimeStepSize_ = std::numeric_limits<Scalar>::max();
224 368 timeStepIdx_ = 0;
225 368 finished_ = false;
226 368 timeAfterLastTimeStep_ = 0.0;
227 368 timeStepWallClockTime_ = 0.0;
228
229 // ensure that dt is not greater than tEnd-startTime
230 368 setTimeStepSize(dt);
231
232 368 timer_.stop();
233 368 timer_.reset();
234 368 }
235
236 /*!
237 * \brief Advance time step.
238 */
239 20655 void advanceTimeStep() override
240 {
241 20655 timeStepIdx_++;
242 20655 time_ += timeStepSize_;
243 20655 previousTimeStepSize_ = timeStepSize_;
244
245 // compute how long the last time step took
246 41310 const auto cpuTime = wallClockTime();
247 20655 timeStepWallClockTime_ = cpuTime - timeAfterLastTimeStep_;
248 20655 timeAfterLastTimeStep_ = cpuTime;
249
250 // ensure that using current dt we don't exceed tEnd in next time step
251 20655 setTimeStepSize(timeStepSize_);
252 20655 }
253
254 /*!
255 * \brief Set the current simulated time, don't change the current
256 * time step index.
257 *
258 * \param t The time \f$\mathrm{[s]}\f$ which should be jumped to
259 */
260 template<class ScalarOrDuration>
261 void setTime(ScalarOrDuration t)
262
0/8
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
8 { time_ = Detail::TimeLoop::toSeconds<Scalar>(t); }
263
264 /*!
265 * \brief Set the current simulated time and the time step index.
266 *
267 * \param t The time \f$\mathrm{[s]}\f$ which should be jumped to
268 * \param stepIdx The new time step index
269 */
270 template<class ScalarOrDuration>
271 void setTime(ScalarOrDuration t, int stepIdx)
272 {
273 time_ = Detail::TimeLoop::toSeconds<Scalar>(t);
274 timeStepIdx_ = stepIdx;
275 }
276
277 /*!
278 * \brief Return the time \f$\mathrm{[s]}\f$ before the time integration.
279 * To get the time after the time integration you have to add timeStepSize() to
280 * time().
281 */
282 Scalar time() const final
283
17/27
✓ Branch 0 taken 98629505 times.
✓ Branch 1 taken 4798463 times.
✓ Branch 2 taken 459518 times.
✓ Branch 3 taken 735612 times.
✓ Branch 4 taken 99940 times.
✓ Branch 5 taken 87199 times.
✓ Branch 6 taken 40 times.
✓ Branch 7 taken 266 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 935 times.
✓ Branch 10 taken 137 times.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 9 times.
✓ Branch 13 taken 61 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 10 times.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 2 times.
✓ Branch 25 taken 64 times.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 30 taken 4 times.
104812266 { return time_; }
284
285 /*!
286 * \brief Returns the number of (simulated) seconds which the simulation runs.
287 */
288 Scalar endTime() const
289 { return endTime_; }
290
291 /*!
292 * \brief Set the time of simulated seconds at which the simulation runs.
293 *
294 * \param t The time \f$\mathrm{[s]}\f$ at which the simulation is finished
295 */
296 4 void setEndTime(Scalar t)
297 {
298 4 endTime_ = t;
299
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (verbose_)
300
2/6
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
8 std::cout << Fmt::format("Set new end time to t = {:.5g} seconds.\n", t);
301 4 }
302
303 /*!
304 * \brief Returns the current wall clock time (cpu time) spend in this time loop
305 */
306 double wallClockTime() const
307 20655 { return timer_.elapsed(); }
308
309 using TimeLoopBase<Scalar>::setTimeStepSize;
310 /*!
311 * \brief Set the current time step size to a given value.
312 *
313 * If the step size would exceed the length of the current
314 * episode, the timeStep() method will take care that the step
315 * size won't exceed the episode or the end of the simulation,
316 * though.
317 *
318 * \param dt The new value for the time step size \f$\mathrm{[s]}\f$
319 */
320 58361 void setTimeStepSize(Scalar dt) final
321 {
322 using std::min;
323
2/2
✓ Branch 1 taken 11018 times.
✓ Branch 2 taken 47343 times.
58361 timeStepSize_ = min(dt, maxTimeStepSize());
324 // Warn if dt is so small w.r.t. current time that it renders float addition meaningless
325 // For instance, consider (may depend on architecture):
326 // double cien = 100;
327 // double mil = 1000;
328 // if (cien + 1e-14 == cien) std::cout << "Will not be printed" << std::endl;
329 // if (mil + 1e-14 == mil) std::cout << "Will be printed" << std::endl;
330
4/4
✓ Branch 1 taken 57687 times.
✓ Branch 2 taken 674 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 57683 times.
58361 if (!finished() && (time_ + timeStepSize_ == time_))
331 4 std::cerr << Fmt::format("You have set a very small timestep size (dt = {:.5g}).", timeStepSize_)
332
3/8
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
8 << " This might lead to numerical problems!\n";
333 58361 }
334
335 /*!
336 * \brief Set the maximum time step size to a given value.
337 *
338 * \param maxDt The new value for the maximum time step size \f$\mathrm{[s]}\f$
339 * \note This may also reduce the currently set timestep size if needed to comply with the set maximum
340 */
341 template<class ScalarOrDuration>
342 void setMaxTimeStepSize(ScalarOrDuration maxDt)
343 {
344
2/8
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
307 userSetMaxTimeStepSize_ = Detail::TimeLoop::toSeconds<Scalar>(maxDt);
345
2/8
✓ Branch 1 taken 302 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
999 setTimeStepSize(timeStepSize_);
346 }
347
348 /*!
349 * \brief Returns the suggested time step length \f$\mathrm{[s]}\f$ so that we
350 * don't miss the beginning of the next episode or cross
351 * the end of the simulation.
352 */
353 709817143 Scalar timeStepSize() const final
354
26/58
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 16360 times.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 3876 times.
✓ Branch 5 taken 52 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 484 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
✓ Branch 10 taken 44 times.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 10 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✓ Branch 25 taken 4 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 4 times.
✓ Branch 28 taken 22 times.
✓ Branch 29 taken 666 times.
✓ Branch 30 taken 22 times.
✓ Branch 31 taken 666 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 4 times.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 4 times.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✓ Branch 43 taken 4 times.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✓ Branch 48 taken 4 times.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✓ Branch 52 taken 66 times.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✓ Branch 58 taken 4 times.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✓ Branch 63 taken 4 times.
29574440 { return timeStepSize_; }
355
356 /*!
357 * \brief Returns number of time steps which have been
358 * executed since the beginning of the simulation.
359 */
360 int timeStepIndex() const
361 { return timeStepIdx_; }
362
363 /*!
364 * \brief The previous time step size
365 */
366 Scalar previousTimeStepSize() const
367 { return previousTimeStepSize_; }
368
369 /*!
370 * \brief Specify whether the simulation is finished
371 *
372 * \param finished If true the simulation is considered finished
373 * before the end time is reached, else it is only
374 * considered finished if the end time is reached.
375 */
376 void setFinished(bool finished = true)
377 { finished_ = finished; }
378
379 /*!
380 * \brief Returns true if the simulation is finished.
381 *
382 * This is the case if either setFinished(true) has been called or
383 * if the end time is reached.
384 */
385 160001 bool finished() const override
386 {
387
9/12
✓ Branch 0 taken 160001 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 158143 times.
✓ Branch 3 taken 1858 times.
✓ Branch 4 taken 692 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 688 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 70 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 66 times.
✓ Branch 11 taken 4 times.
160763 return finished_ || (endTime_ - time_) < baseEps_*(time_ - startTime_);
388 }
389
390 /*!
391 * \brief Returns true if the simulation is finished after the
392 * time level is incremented by the current time step size.
393 */
394 bool willBeFinished() const
395 {
396
4/4
✓ Branch 0 taken 14620 times.
✓ Branch 1 taken 125 times.
✓ Branch 2 taken 14511 times.
✓ Branch 3 taken 109 times.
14745 return finished() || (endTime_ - time_ - timeStepSize_) < baseEps_*timeStepSize_;
397 }
398
399 /*!
400 * \brief The current maximum time step size
401 * \note This gets aligned on every setTimeStepSize call to end time
402 * and other possible check points
403 */
404 58366 Scalar maxTimeStepSize() const override
405 {
406
2/2
✓ Branch 1 taken 57692 times.
✓ Branch 2 taken 674 times.
58366 if (finished())
407 return 0.0;
408
409 using std::min; using std::max;
410
3/4
✓ Branch 0 taken 57692 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20112 times.
✓ Branch 3 taken 37580 times.
135496 return min(userSetMaxTimeStepSize_, max<Scalar>(0.0, endTime_ - time_));
411 }
412
413 /*!
414 * \brief State info on cpu time.
415 * \note Always call this after TimeLoop::advanceTimeStep()
416 */
417 20655 void reportTimeStep() const
418 {
419
2/2
✓ Branch 0 taken 19928 times.
✓ Branch 1 taken 727 times.
20655 if (verbose_)
420 {
421 19928 const auto cpuTime = wallClockTime();
422 using std::round;
423 19928 const auto percent = round( (time_ - startTime_) / (endTime_ - startTime_) * 100 );
424 std::cout << Fmt::format("[{:3.0f}%] ", percent)
425 19928 << Fmt::format("Time step {} done in {:.2g} seconds. ", timeStepIdx_, timeStepWallClockTime_)
426
8/22
✓ Branch 3 taken 19928 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 19928 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 19928 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 19928 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 19928 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 19928 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 19928 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 19928 times.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
59784 << Fmt::format("Wall clock time: {:.5g}, time: {:.5g}, time step size: {:.5g}\n", cpuTime, time_, previousTimeStepSize_);
427 }
428 20655 }
429
430 /*!
431 * \brief Print final status and stops tracking the time.
432 */
433 template< class Communicator = Dune::Communication<typename Dune::MPIHelper::MPICommunicator> >
434
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
336 void finalize(const Communicator& comm = Dune::MPIHelper::getCommunication())
435 {
436 336 auto cpuTime = timer_.stop();
437
438
2/2
✓ Branch 0 taken 317 times.
✓ Branch 1 taken 19 times.
336 if (verbose_)
439
4/7
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 286 times.
✓ Branch 5 taken 31 times.
✓ Branch 6 taken 286 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
920 std::cout << Fmt::format("Simulation took {:.5g} seconds on {} processes.\n", cpuTime, comm.size());
440
441
4/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 267 times.
✓ Branch 2 taken 38 times.
✓ Branch 3 taken 267 times.
641 if (comm.size() > 1)
442 38 cpuTime = comm.sum(cpuTime);
443
444
2/2
✓ Branch 0 taken 317 times.
✓ Branch 1 taken 19 times.
336 if (verbose_)
445
2/6
✓ Branch 3 taken 317 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 317 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
634 std::cout << Fmt::format("The cumulative CPU time was {:.5g} seconds.\n", cpuTime);
446 336 }
447
448 //! If the time loop has verbose output
449 bool verbose() const
450 { return verbose_; }
451
452 //! Sets time loop verbosity
453 void setVerbose(bool verbose = true)
454 { verbose_ = verbose; }
455
456 /*
457 * @}
458 */
459
460 protected:
461 static constexpr Scalar baseEps_ = 1e-10;
462
463 Dune::Timer timer_;
464 Scalar time_;
465 Scalar endTime_;
466 Scalar startTime_;
467
468 Scalar timeStepSize_;
469 Scalar previousTimeStepSize_;
470 Scalar userSetMaxTimeStepSize_;
471 Scalar timeAfterLastTimeStep_, timeStepWallClockTime_;
472 int timeStepIdx_;
473 bool finished_;
474 bool verbose_;
475 };
476
477 // always fall back to floating-point representation
478 template<class Rep1, class Period1,
479 class Rep2, class Period2,
480 class Rep3, class Period3>
481 TimeLoop(std::chrono::duration<Rep1, Period1>,
482 std::chrono::duration<Rep2, Period2>,
483 std::chrono::duration<Rep3, Period3>,
484 bool verbose = true) -> TimeLoop<std::conditional_t<
485 std::is_floating_point_v<std::common_type_t<Rep1, Rep2, Rep3>>,
486 std::common_type_t<Rep1, Rep2, Rep3>,
487 double
488 >>;
489
490 /*!
491 * \ingroup Core
492 * \brief A time loop with a check point mechanism
493 */
494 template <class Scalar>
495
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
15 class CheckPointTimeLoop : public TimeLoop<Scalar>
496 {
497 29600 class CheckPointType {
498 static constexpr std::size_t manualIdx = 0;
499 static constexpr std::size_t periodicIdx = 1;
500
501 public:
502 29600 bool isPeriodic() const { return set_[periodicIdx]; }
503 29490 bool isManual() const { return set_[manualIdx]; }
504 29490 bool isAny() const { return set_.any(); }
505
506 14800 CheckPointType& withPeriodic(bool value) { set_[periodicIdx] = value; return *this; }
507 14800 CheckPointType& withManual(bool value) { set_[manualIdx] = value; return *this; }
508
509 private:
510 std::bitset<2> set_;
511 };
512
513 public:
514 template<class... Args>
515 152 CheckPointTimeLoop(Args&&... args)
516 152 : TimeLoop<Scalar>(std::forward<Args>(args)...)
517 {
518 152 periodicCheckPoints_ = false;
519 152 deltaPeriodicCheckPoint_ = 0.0;
520 152 lastPeriodicCheckPoint_ = this->startTime_;
521 152 isCheckPoint_ = false;
522 152 }
523
524 /*!
525 * \brief Advance time step.
526 */
527 14745 void advanceTimeStep() override
528 {
529 29490 const auto dt = this->timeStepSize();
530 29490 const auto newTime = this->time()+dt;
531
532 //! Check point management, TimeLoop::isCheckPoint() has to be called after this!
533 14745 const auto cpType = nextCheckPointType_(newTime);
534
4/4
✓ Branch 0 taken 79 times.
✓ Branch 1 taken 14666 times.
✓ Branch 2 taken 79 times.
✓ Branch 3 taken 14666 times.
29490 if (cpType.isManual()) checkPoints_.pop();
535
4/4
✓ Branch 0 taken 917 times.
✓ Branch 1 taken 13828 times.
✓ Branch 2 taken 917 times.
✓ Branch 3 taken 13828 times.
29490 if (cpType.isPeriodic()) lastPeriodicCheckPoint_ += deltaPeriodicCheckPoint_;
536 14745 isCheckPoint_ = cpType.isAny();
537
538 14745 const auto previousTimeStepSize = this->previousTimeStepSize();
539
540 // advance the time step like in the parent class
541 14745 TimeLoop<Scalar>::advanceTimeStep();
542
543 // if this is a check point we might have reduced the time step to reach this check point
544 // reset the time step size to the time step size before this time step
545 14745 if (!this->willBeFinished())
546 {
547 using std::max;
548
2/2
✓ Branch 0 taken 926 times.
✓ Branch 1 taken 13585 times.
14511 if (isCheckPoint_)
549
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 824 times.
1028 this->setTimeStepSize(max(dt, previousTimeStepSize));
550
551 // if there is a check point soon check if the time step after the next time step would be smaller
552 // than 20% of the next time step, if yes increase the suggested next time step to exactly reach the check point
553 // (in the limits of the maximum time step size)
554 29022 auto nextDt = this->timeStepSize();
555 14511 const auto threshold = 0.2*nextDt;
556 29022 const auto nextTime = this->time() + nextDt;
557
558 14511 const auto nextDtToCheckPoint = maxDtToCheckPoint_(nextTime);
559
4/4
✓ Branch 0 taken 13588 times.
✓ Branch 1 taken 923 times.
✓ Branch 2 taken 13571 times.
✓ Branch 3 taken 17 times.
14511 if (nextDtToCheckPoint > Scalar{0} && Dune::FloatCmp::le(nextDtToCheckPoint, threshold))
560 17 nextDt += nextDtToCheckPoint;
561
562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14511 times.
14511 assert(nextDt > 0.0);
563 14511 this->setTimeStepSize(nextDt);
564 }
565 14745 }
566
567 /*!
568 * \brief The current maximum time step size
569 * \note This gets aligned on every setTimeStepSize call to end time
570 * and other possible check points
571 */
572 45176 Scalar maxTimeStepSize() const override
573 {
574 using std::min;
575 90352 const auto maxCheckPointDt = computeStepSizeRespectingCheckPoints_();
576 45176 const auto maxDtParent = TimeLoop<Scalar>::maxTimeStepSize();
577
2/2
✓ Branch 0 taken 10567 times.
✓ Branch 1 taken 34609 times.
45176 return min(maxDtParent, maxCheckPointDt);
578 }
579
580 /*!
581 * \brief Set a periodic check point
582 * \note You can query if we are at a time check point with isCheckPoint()
583 * \param interval Set a periodic checkout every [interval] seconds
584 * \param offset time from which the periodic check points are supposed to start (simulation time)
585 * the first checkpoint will be at time = offset.
586 * \note If offset is in the past the first check point will be at the next
587 * periodic check point greater or equal than time
588 * \note This also updates the time step size and potentially reduces the time step size to meet the next check point
589 */
590 template<class ScalarOrDuration1, class ScalarOrDuration2 = Scalar>
591 void setPeriodicCheckPoint(ScalarOrDuration1 interval, ScalarOrDuration2 offset = 0.0)
592 {
593
6/12
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 4 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
56 setPeriodicCheckPoint_(
594 Detail::TimeLoop::toSeconds<Scalar>(interval),
595 Detail::TimeLoop::toSeconds<Scalar>(offset)
596 );
597 }
598
599 //! disable periodic check points
600 void disablePeriodicCheckPoints()
601 { periodicCheckPoints_ = false; }
602
603 //! remove all check points
604 void removeAllCheckPoints()
605 {
606 4 periodicCheckPoints_ = false;
607
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
8 while (!checkPoints_.empty())
608 checkPoints_.pop();
609 }
610
611 /*!
612 * \brief Whether now is a time checkpoint
613 * \note has to be called after TimeLoop::advanceTimeStep()
614 */
615 bool isCheckPoint() const
616 { return isCheckPoint_; }
617
618 /*!
619 * \brief add a checkpoint to the queue
620 * \note checkpoints have to be provided in ascending order
621 * \param t the check point (in seconds)
622 * \note This also updates the time step size and potentially reduces the time step size to meet the next check point
623 */
624 template<class ScalarOrDuration>
625 void setCheckPoint(ScalarOrDuration t)
626 {
627 // set the check point
628
4/8
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
30 setCheckPoint_(Detail::TimeLoop::toSeconds<Scalar>(t));
629
630 // make sure we respect this check point on the next time step
631
4/8
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
30 this->setTimeStepSize(this->timeStepSize());
632 }
633
634 /*!
635 * \brief add checkpoints to the queue from a list of time points
636 * \note checkpoints have to be provided in ascending order
637 * \param checkPoints the list of check points
638 * \note This also updates the time step size and potentially reduces the time step size to meet the next check point
639 */
640 template<class ScalarOrDuration>
641 void setCheckPoint(const std::initializer_list<ScalarOrDuration>& checkPoints)
642
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 { setCheckPoint(checkPoints.begin(), checkPoints.end()); }
643
644 template<class ScalarOrDuration>
645 [[deprecated("Use setCheckpoint(begin, end) instead. Will be removed after release 3.9.")]]
646 void setCheckPoint(const std::vector<ScalarOrDuration>& checkPoints)
647 { setCheckPoint(checkPoints.begin(), checkPoints.end()); }
648
649 /*!
650 * \brief add checkpoints to the queue from a container from the first iterator to the last iterator
651 * \note checkpoints have to be provided in ascending order
652 * \param first iterator to the first element to be inserted
653 * \param last iterator to the one-after-last element to be inserted
654 * \note This also updates the time step size and potentially reduces the time step size to meet the next check point
655 */
656 template<class ForwardIterator>
657 19 void setCheckPoint(ForwardIterator first, ForwardIterator last)
658 {
659 // set the check points
660
4/4
✓ Branch 0 taken 277 times.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 267 times.
✓ Branch 3 taken 14 times.
310 for (; first != last; ++first)
661 277 setCheckPoint_(Detail::TimeLoop::toSeconds<Scalar>(*first));
662
663 // make sure we respect this check point on the next time step
664 19 this->setTimeStepSize(this->timeStepSize());
665 19 }
666
667 private:
668 bool fuzzyEqual_(const Scalar t0, const Scalar t1) const
669 37002 { return Dune::FloatCmp::eq(t0, t1, this->baseEps_*this->timeStepSize()); }
670
671 55 void setPeriodicCheckPoint_(Scalar interval, Scalar offset = 0.0)
672 {
673 using std::signbit;
674
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 55 times.
110 if (signbit(interval))
675 DUNE_THROW(Dune::InvalidStateException, "Interval has to be positive!");
676
677 55 periodicCheckPoints_ = true;
678 55 deltaPeriodicCheckPoint_ = interval;
679 55 lastPeriodicCheckPoint_ = offset;
680
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 55 times.
63 while (lastPeriodicCheckPoint_ + interval - this->time() < 1e-14*interval)
681 8 lastPeriodicCheckPoint_ += interval;
682
683
1/2
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
55 if (this->verbose())
684 std::cout << Fmt::format("Enabled periodic check points every {:.5g} seconds ", interval)
685
5/14
✓ Branch 3 taken 55 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 55 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 55 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 55 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 55 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
165 << Fmt::format("with the next check point at {:.5g} seconds.\n", lastPeriodicCheckPoint_ + interval);
686
687 // check if the current time point is a periodic check point
688
4/4
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 49 times.
✓ Branch 4 taken 6 times.
55 if (nextCheckPointType_(this->time() + deltaPeriodicCheckPoint_).isPeriodic())
689 49 isCheckPoint_ = true;
690
691 // make sure we respect this check point on the next time step
692 55 this->setTimeStepSize(this->timeStepSize());
693 55 }
694
695 //! Adds a check point to the queue
696 307 void setCheckPoint_(Scalar t)
697 {
698
2/2
✓ Branch 0 taken 303 times.
✓ Branch 1 taken 4 times.
307 if (Dune::FloatCmp::le(t - this->time(), 0.0, this->timeStepSize()*this->baseEps_))
699 {
700
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (this->verbose())
701 std::cerr << Fmt::format("Couldn't insert checkpoint at t = {:.5g} ", t)
702
5/14
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 8 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
24 << Fmt::format("because that's not in the future! (current simulation time is {:.5g})\n", this->time());
703 8 return;
704 }
705
706
4/4
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 264 times.
✓ Branch 3 taken 35 times.
598 if (!checkPoints_.empty())
707 {
708
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 261 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 258 times.
267 if (t <= checkPoints_.back())
709 {
710
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (this->verbose())
711 std::cerr << Fmt::format("Couldn't insert checkpoint at t = {:.5g} ", t)
712 8 << Fmt::format("because it's earlier than or equal to the last check point (t = {:.5g}) in the queue.\n", checkPoints_.back())
713
7/18
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 4 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 4 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 4 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
16 << "Checkpoints can only be inserted in ascending order." << std::endl;
714 6 return;
715 }
716 }
717
718
2/2
✓ Branch 0 taken 290 times.
✓ Branch 1 taken 3 times.
293 checkPoints_.push(t);
719
2/2
✓ Branch 0 taken 278 times.
✓ Branch 1 taken 15 times.
293 if (this->verbose())
720
2/6
✓ Branch 3 taken 278 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 278 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
556 std::cout << Fmt::format("Set check point at t = {:.5g} seconds.\n", t);
721 }
722
723 /*!
724 * \brief Return the type of (next) check point at the given time
725 */
726 14800 CheckPointType nextCheckPointType_(Scalar t)
727 {
728 return CheckPointType{}
729
4/4
✓ Branch 0 taken 6580 times.
✓ Branch 1 taken 948 times.
✓ Branch 2 taken 966 times.
✓ Branch 3 taken 6562 times.
14108 .withPeriodic(periodicCheckPoints_ && fuzzyEqual_(t - lastPeriodicCheckPoint_, deltaPeriodicCheckPoint_))
730
14/14
✓ Branch 0 taken 7528 times.
✓ Branch 1 taken 7272 times.
✓ Branch 2 taken 7528 times.
✓ Branch 3 taken 7272 times.
✓ Branch 4 taken 4806 times.
✓ Branch 5 taken 9994 times.
✓ Branch 6 taken 4806 times.
✓ Branch 7 taken 9994 times.
✓ Branch 8 taken 4727 times.
✓ Branch 9 taken 79 times.
✓ Branch 10 taken 4727 times.
✓ Branch 11 taken 79 times.
✓ Branch 12 taken 4727 times.
✓ Branch 13 taken 79 times.
44656 .withManual(!checkPoints_.empty() && fuzzyEqual_(t - checkPoints_.front(), 0.0));
731 }
732
733 /*!
734 * \brief Aligns dt to the next check point
735 */
736 Scalar computeStepSizeRespectingCheckPoints_() const
737 45176 { return maxDtToCheckPoint_(this->time()); }
738
739 /*!
740 * \brief Compute a time step size respecting upcoming checkpoints, starting from the given time t.
741 */
742 59687 Scalar maxDtToCheckPoint_(Scalar t) const
743 {
744 static constexpr auto unset = std::numeric_limits<Scalar>::max();
745 119374 const auto dtToPeriodic = dtToNextPeriodicCheckPoint_(t);
746 119374 const auto dtToManual = dtToNextManualCheckPoint_(t);
747
748 using std::min;
749
6/6
✓ Branch 0 taken 19292 times.
✓ Branch 1 taken 40395 times.
✓ Branch 2 taken 30672 times.
✓ Branch 3 taken 29015 times.
✓ Branch 4 taken 19288 times.
✓ Branch 5 taken 40399 times.
109651 return min(
750 dtToPeriodic.value_or(unset),
751 dtToManual.value_or(unset)
752 119374 );
753 }
754
755 /*!
756 * \brief Compute the time step size to the next periodic check point.
757 */
758 std::optional<Scalar> dtToNextPeriodicCheckPoint_(Scalar t) const
759 {
760
2/2
✓ Branch 0 taken 30672 times.
✓ Branch 1 taken 29015 times.
59687 if (periodicCheckPoints_)
761 61344 return lastPeriodicCheckPoint_ + deltaPeriodicCheckPoint_ - t;
762 58030 return {};
763 }
764
765 /*!
766 * \brief Compute a time step size respecting the next manual check point.
767 */
768 std::optional<Scalar> dtToNextManualCheckPoint_(Scalar t) const
769 {
770
4/4
✓ Branch 0 taken 19292 times.
✓ Branch 1 taken 40395 times.
✓ Branch 2 taken 19292 times.
✓ Branch 3 taken 40395 times.
119374 if (!checkPoints_.empty())
771 57876 return checkPoints_.front() - t;
772 80790 return {};
773 }
774
775 bool periodicCheckPoints_;
776 Scalar deltaPeriodicCheckPoint_;
777 Scalar lastPeriodicCheckPoint_;
778 std::queue<Scalar> checkPoints_;
779 bool isCheckPoint_;
780 };
781
782 template<class... Args>
783 CheckPointTimeLoop(Args&&... args) -> CheckPointTimeLoop<
784 typename decltype(TimeLoop{std::forward<Args>(args)...})::Scalar
785 >;
786
787 } // end namespace Dumux
788
789 #endif
790