1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2019 - 2020 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii_discrete_time_h
17 #define dealii_discrete_time_h
18 
19 #include <deal.II/base/config.h>
20 
21 DEAL_II_NAMESPACE_OPEN
22 
23 /**
24  * This class provides a means to keep track of the simulation time in a
25  * time-dependent simulation. It manages stepping forward from a start time
26  * $T_{\text{start}}$ to an end time $T_{\text{end}}$. It also allows adjusting
27  * the time step size during the simulation. This class provides the necessary
28  * interface to be incorporated in any time-dependent simulation.
29  * The usage of this class is demonstrated in step-19 and step-21.
30  *
31  * This class provides a number of invariants that are guaranteed to be
32  * true at all times.
33  *
34  * * The current simulation time is within the closed interval between the
35  *   start time and the end time ($T_{\text{start}} \le t \le T_{\text{end}}$).
36  * * Whenever time is incremented, the step size is positive ($dt > 0$).
37  *   In other words, time advances in strictly ascending order
38  *   ($m < n \Leftrightarrow t_m < t_n$).
39  *
40  * The model this class follows is that one sets a *desired* time step length
41  * either through the constructor or using set_desired_next_step_size()
42  * function. This step size will then be used in all following calls to the
43  * advance_time() function, but may be adjusted slightly towards the end of the
44  * simulation to ensure that the simulation time hits the end time exactly. The
45  * adjustment is useful for the following reasons:
46  *
47  * Let's say that you loop over all of the time steps by using a `for` loop
48  * @code
49  *   for (DiscreteTime time(0., 1., 0.3);
50  *        time.is_at_end() == false;
51  *        time.advance_time())
52  *   {
53  *     // Insert simulation code here
54  *   }
55  * @endcode
56  * or, if you like this better, the equivalent `while` loop:
57  * @code
58  *   DiscreteTime time(0., 1., 0.3);
59  *   while (time.is_at_end() == false)
60  *   {
61  *     // Insert simulation code here
62  *
63  *     time.advance_time();
64  *   }
65  * @endcode
66  *
67  * In the above example the time starts at $T_{\text{start}} = 0$ until
68  * $T_{\text{end}}=1$. Assuming the time step $dt = 0.3$ is not modified inside
69  * the loop, the time is advanced from $t = 0$ to $t = 0.3$, $t = 0.6$, $t =
70  * 0.9$ and finally it reaches the end time at $t = 1.0$. Here, the final step
71  * size needs to be reduced from its desired value of 0.3 to $dt = 0.1$ in order
72  * to ensure that we finish the simulation exactly at the specified end time. In
73  * fact, you should assume that not only the last time step length may be
74  * adjusted, but also previously ones -- for example, this class may take the
75  * liberty to spread the decrease in time step size out over several time steps
76  * and increment time from $t=0$, to $0.3$, $0.6$, $0.8$, and finally
77  * $t=T_{\text{end}}=1$ to avoid too large a change in time step size from one
78  * step to another.
79  *
80  * The other situation in which the time step needs to be adjusted (this time to
81  * slightly larger values) is if a time increment falls just short of the final
82  * time. Imagine, for example, a similar situation as above, but with different
83  * end time:
84  * @code
85  *   for (DiscreteTime time(0., 1.21, 0.3);
86  *        time.is_at_end() == false;
87  *        time.advance_time())
88  *   {
89  *     // Insert simulation code here
90  *   }
91  * @endcode
92  * Here, the time step from $t=0.9$ to $t=1.2$ falls just short of the final
93  * time $T_{\text{end}}=1.21$. Instead of following up with a very small step of
94  * length $dt=0.01$, the class stretches the last time step (or last time steps)
95  * slightly to reach the desired end time.
96  *
97  * The examples above make clear that the time step size given to this class is
98  * only a *desired* step size. You can query the actual time step size using the
99  * get_next_step_size() function.
100  *
101  *
102  * ### Details of time-stepping
103  *
104  * Since time is marched forward in a discrete manner in our simulations, we
105  * need to discuss how we increment time. During time stepping we enter two
106  * separate alternating regimes in every step.
107  *
108  * * The **snapshot** stage (the **current** stage, the **consistent**
109  *   stage): In this part of the algorithm, we are at $t = t_n$ and all
110  *   quantities of the simulation (displacements, strains, temperatures, etc.)
111  *   are up-to-date for $t = t_n$. In this stage, *current time* refers to
112  *   $t_n$, *next time* refers to $t_{n+1}$, *previous time* refers to
113  *   $t_{n-1}$. The other useful notation quantities are the *next* time step
114  *   size $t_{n+1} - t_n$ and *previous* time step size $t_n - t_{n-1}$. In
115  *   this stage, it is a perfect occasion to generate text output using print
116  *   commands within the user's code. Additionally, post-processed outputs can
117  *   be prepared here, which can then later be viewed by visualization programs
118  *   such as `Tecplot`, `Paraview`, and `VisIt`. Additionally, during the
119  *   snapshot stage, the code can assess the quality of the previous step and
120  *   decide whether it wants to increase or decrease the time step size. The
121  *   step size for the next time step can be modified here, by calling
122  *   set_desired_next_step_size().
123  * * The **update** stage (the **transition** stage, the **inconsistent**
124  *   stage): In this section of the program, the internal state of the
125  *   simulation is getting updated from $t_n$ to $t_{n+1}$. All of the
126  *   variables need to be updated one by one, the step number is incremented,
127  *   the time is incremented by $dt = t_{n+1} - t_n$, and time-integration
128  *   algorithms are used to update the other simulation quantities. In the
129  *   middle of this stage, some variables have been updated to $t_{n+1}$ but
130  *   other variables still represent their value at $t_n$. Thus, we call this
131  *   the inconsistent stage, requiring that no post-processing output related
132  *   to the state variables take place within it. The state variables, namely
133  *   those related to time, the solution field and any internal variables, are
134  *   not synchronized and then get updated one by one. In general, the order of
135  *   updating variables is arbitrary, but some care should be taken if there
136  *   are interdependencies between them. For example, if some variable such as
137  *   $x$ depends on the calculation of another variable such as $y$, then $y$
138  *   must be updated before $x$ can be updated.
139  *
140  *   The question arises whether time should be incremented before updating
141  *   state quantities. Multiple possibilities exist, depending on program and
142  *   formulation requirements, and possibly the programmer's preferences:
143  *   * Time is incremented *before* the rest of the updates. In this case, even
144  *     though time is incremented to $t_{n+1}$, not all variables are updated
145  *     yet. During this update phase, $dt$ equals the *previous* time step
146  *     size. *Previous* means that it is referring to the $dt$ of the
147  *     `advance_time()` command that was performed previously. In the
148  *     following example code, we are assuming that `a` and `b` are two state
149  *     variables that need to be updated in this time step.
150  *     @code
151  *       time.advance_time();
152  *       new_a = update_a(a, b, time.get_previous_step_size());
153  *       b = update_b(a, b, time.get_previous_step_size());
154  *       a = new_a;
155  *     @endcode
156  *     Here, the code starts in a consistent state, but once advance_time()
157  *     is called, the time variable, `a`, and `b` are no longer consistent
158  *     with each other until after the last statement. At that point,
159  *     the variables are all consistent again.
160  *   * Time is incremented from $t_n$ to $t_{n+1}$ *after* all variables have
161  *     already been updated for $t_{n+1}$. During the update stage, $dt$ is
162  *     denoted as the *next* time step size. *Next* means that $dt$ of the
163  *     step corresponds to the `advance_time()` command that will happen
164  *     subsequently.
165  *     @code
166  *       new_a = update_a(a, b, time.get_next_step_size());
167  *       b = update_b(a, b, time.get_next_step_size());
168  *       a = new_a;
169  *       time.advance_time();
170  *     @endcode
171  *   * Time is incremented in the middle of the other updates: In this case
172  *     $dt$ would correspond to *next* or *previous* depending of whether it
173  *     is used before or after the call to `advance_time()`.
174  *     @code
175  *       new_a = update_a(a, b, time.get_next_step_size());
176  *       time.advance_time();
177  *       b = update_b(a, b, time.get_previous_step_size());
178  *       a = new_a;
179  *     @endcode
180  *
181  * One thing to note is that, during the update phase, $dt$ is referred to
182  * either **next** or **previous** time step size, depending on whether
183  * advance_time() has been called yet. The notion of *current* time
184  * step size is ill-defined. In fact, in the update stage the definition of
185  * every variable depends on whether it has been updated yet or not, hence the
186  * name **the inconsistent stage**.
187  *
188  * The following code snippet shows the code sections for the snapshot stage
189  * and the update stage in the context of a complete time-dependent
190  * simulation. This code follows the coding conventions incorporated in the
191  * tutorial examples. Note that even though this example is written in the
192  * format of a `for` loop, it can equivalently be written as a `while` or
193  * `do while` loop (as shown in step-21).
194  * @code
195  * // pre-processing/setup stage {
196  * make_grid();
197  * setup_system();
198  * for (DiscreteTime time(0., 1., 0.1);  // } end pre-processing/setup stage
199  *      time.is_at_end() == false;
200  *      time.advance_time())             // part of the update stage, runs at
201  *                                       // the end of the loop body
202  * {
203  *   // snapshot stage {
204  *   const double time_of_simulation = time.get_next_time();
205  *   const double timestep_size      = time.get_next_step_size();
206  *
207  *   std::cout
208  *     << "Timestep: " << time.get_step_number() << " -- "
209  *     << "Solving for the solution at "
210  *     << "t = " << time_of_simulation << " with "
211  *     << "dt = " << timestep_size << "." << std::endl;
212  *   // } end snapshot stage
213  *
214  *   // update stage {
215  *   assemble_system(time_of_simulation, timestep_size);
216  *   solve();
217  *   update_solutions();
218  *   // } end update stage
219  *
220  *   // snapshot stage {
221  *   output_results(time_of_solution);
222  *
223  *   // propose a new timestep size if need be
224  *   // time.set_desired_next_step_size(...);
225  *   // } end snapshot stage
226  * }
227  * @endcode
228  * The `run()` function in step-19 shows a very similar example where the call
229  * to advance_time() ends the update stage and is followed by generating
230  * graphical output with the then-current time.
231  */
232 class DiscreteTime
233 {
234 public:
235   /**
236    * Constructor.
237    *
238    * @param[in] start_time The time at the start of the simulation.
239    *
240    * @param[in] end_time The time at the end of the simulation.
241    *
242    * @param[in] desired_start_step_size A desired step size for incrementing
243    * time for the first step. It is not guaranteed that this value will be
244    * actually used as the size of the first step, as discussed in the
245    * introduction.
246    *
247    * @pre @p desired_start_step_size must be non-negative.
248    *
249    * @note @p desired_start_step_size is an optional parameter. If it is not
250    * provided or it is specified as zero, it indicates that the
251    * desired size for the time step will be calculated at a different location
252    * in the code. In this case, the created object cannot increment time until
253    * the step size is changed by calling set_desired_next_step_size().
254    */
255   DiscreteTime(const double start_time,
256                const double end_time,
257                const double desired_start_step_size = 0.);
258 
259   /**
260    * Return the current time.
261    */
262   double
263   get_current_time() const;
264 
265   /**
266    * Return the next time that we would reach if we were to advance the time
267    * by one step.
268    *
269    * @note If the simulation is at the end time, this method returns the
270    * end time.
271    */
272   double
273   get_next_time() const;
274 
275   /**
276    * Return the time we were at before `advance_time()` was called last time.
277    *
278    * @note If the simulation is at the start time, this method returns the
279    * start time.
280    */
281   double
282   get_previous_time() const;
283 
284   /**
285    * Return the start time.
286    */
287   double
288   get_start_time() const;
289 
290   /**
291    * Return the end of the time interval.
292    * The final time step ends exactly at this point. This exact floating-point
293    * equality is very important because it allows us to equality-compare
294    * current time with end time and decide whether we have reached the end of
295    * the simulation.
296    */
297   double
298   get_end_time() const;
299 
300   /**
301    * Return whether no step has taken place yet.
302    */
303   bool
304   is_at_start() const;
305 
306   /**
307    * Return whether time has reached the end time.
308    */
309   bool
310   is_at_end() const;
311 
312   /**
313    * Return the size of the step from current time step to the
314    * next. As discussed in the introduction to the class, this is the
315    * *actual* time step, and may differ from the *desired* time step
316    * set in the constructor or through the
317    * set_desired_next_step_size() function.
318    *
319    * @note If the simulation is at the end time, this method returns zero.
320    */
321   double
322   get_next_step_size() const;
323 
324   /**
325    * Return the step size of the previous step.
326    *
327    * @note If the simulation is at the start time, this method returns zero.
328    */
329   double
330   get_previous_step_size() const;
331 
332   /**
333    * Return the number of times the simulation time has been incremented.
334    * Return zero when the simulation is at the start time.
335    */
336   unsigned int
337   get_step_number() const;
338 
339   /**
340    * Set the *desired* value of the next time step size. By calling this
341    * method, we are indicating the next time advance_time() is called, we
342    * would like @p time_step_size to be used to advance the simulation time.
343    * However, if the step is too large such that the next
344    * simulation time exceeds the end time, the step size is truncated.
345    * Additionally, if the step size is such that the next simulation time
346    * approximates the end time (but falls just slightly short of it), the step
347    * size is adjusted such that the next simulation time exactly matches the
348    * end time.
349    */
350   void
351   set_desired_next_step_size(const double time_step_size);
352 
353   /**
354    * Set the *actual* value of the next time step size. By calling this
355    * method, we are indicating the next time advance_time() is called,
356    * @p time_step_size is to be used to advance the simulation time.
357    *
358    * @note The difference between set_next_step_size() and
359    * set_desired_next_step_size() is that the former uses the provided $dt$
360    * exactly without any adjustment, but produces an
361    * error (in debug mode) if $dt$ is not in the acceptable range.
362    * Generally, set_desired_next_step_size() is the preferred method because
363    * it can adjust the $dt$ intelligently, based on $T_{\text{end}}$.
364    * @pre $0 < dt \le T_{\text{end}} - t$.
365    */
366   void
367   set_next_step_size(const double time_step_size);
368 
369   /**
370    * Advance the current time based on the value of the current step.
371    * If you want to adjust the next time step size, call the method
372    * set_desired_next_step_size() before calling this method.
373    * If you call this function repeatedly, the time
374    * is increased with the same step size until it reaches the end
375    * time. See the documentation of set_desired_next_step_size() for
376    * explanation of the rules for automatic adjustment of the step size.
377    *
378    * @pre Current time must be smaller than the end time. The object cannot
379    * advance time if it is already at the end time. This rule is created to
380    * avoid the creation of an infinite loop when advance_time() is called
381    * inside a loop.
382    *
383    * @pre The time step size must be nonzero. If the step size is currently
384    * zero, change it by calling set_desired_next_step_size() before calling
385    * advance_time().
386    */
387   void
388   advance_time();
389 
390   /**
391    * Set the current time equal to start time and set the step size to the
392    * initial step size.
393    */
394   void
395   restart();
396 
397 private:
398   /**
399    * The beginning of the time interval.
400    */
401   double start_time;
402 
403   /**
404    *The end of the time interval.
405    */
406   double end_time;
407 
408   /**
409    * The current time.
410    */
411   double current_time;
412 
413   /**
414    * The time at the next step.
415    *
416    * @note Internally, the next simulation time is stored instead of the
417    * current step size. For example, when the method
418    * set_desired_next_step_size() is called, it computes the appropriate next
419    * simulation time and stores it. When advance_time() is called, the
420    * current_time is replaced by next_time. This choice for the internal state
421    * allows for simpler code and ensures than when we call advance_time() at
422    * the last step, the floating-point value of the time exactly matches the
423    * end time.
424    */
425   double next_time;
426 
427   /**
428    * The previous time.
429    */
430   double previous_time;
431 
432   /**
433    * The size of the first step.
434    */
435   double start_step_size;
436 
437   /**
438    * The step number i.e. the number of times the simulation time ha been
439    * incremented.
440    */
441   unsigned int step_number;
442 };
443 
444 
445 /*---------------------- Inline functions ------------------------------*/
446 
447 
448 inline double
get_start_time()449 DiscreteTime::get_start_time() const
450 {
451   return start_time;
452 }
453 
454 
455 
456 inline double
get_end_time()457 DiscreteTime::get_end_time() const
458 {
459   return end_time;
460 }
461 
462 
463 
464 inline bool
is_at_start()465 DiscreteTime::is_at_start() const
466 {
467   return step_number == 0;
468 }
469 
470 
471 
472 inline bool
is_at_end()473 DiscreteTime::is_at_end() const
474 {
475   return current_time == end_time;
476 }
477 
478 
479 
480 inline double
get_next_step_size()481 DiscreteTime::get_next_step_size() const
482 {
483   return next_time - current_time;
484 }
485 
486 
487 
488 inline double
get_previous_step_size()489 DiscreteTime::get_previous_step_size() const
490 {
491   return current_time - previous_time;
492 }
493 
494 
495 
496 inline double
get_current_time()497 DiscreteTime::get_current_time() const
498 {
499   return current_time;
500 }
501 
502 
503 
504 inline double
get_next_time()505 DiscreteTime::get_next_time() const
506 {
507   return next_time;
508 }
509 
510 
511 
512 inline double
get_previous_time()513 DiscreteTime::get_previous_time() const
514 {
515   return previous_time;
516 }
517 
518 
519 
520 inline unsigned int
get_step_number()521 DiscreteTime::get_step_number() const
522 {
523   return step_number;
524 }
525 
526 
527 DEAL_II_NAMESPACE_CLOSE
528 
529 #endif
530