Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <cstddef>
7 : #include <cstdint>
8 : #include <optional>
9 : #include <pup.h>
10 : #include <type_traits>
11 :
12 : #include "DataStructures/MathWrapper.hpp"
13 : #include "DataStructures/TaggedVariant.hpp"
14 : #include "Time/History.hpp"
15 : #include "Time/StepperErrorEstimate.hpp"
16 : #include "Utilities/GenerateInstantiations.hpp"
17 : #include "Utilities/Gsl.hpp"
18 : #include "Utilities/Serialization/CharmPupable.hpp"
19 :
20 : /// \cond
21 : struct StepperErrorTolerances;
22 : class TimeDelta;
23 : class TimeStepId;
24 : /// \endcond
25 :
26 : /// \ingroup TimeSteppersGroup
27 : ///
28 : /// Holds classes that take time steps.
29 : namespace TimeSteppers {}
30 :
31 : /// \cond
32 : #define TIME_STEPPER_WRAPPED_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)
33 : #define TIME_STEPPER_DERIVED_CLASS(data) BOOST_PP_TUPLE_ELEM(1, data)
34 : #define TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data) BOOST_PP_TUPLE_ELEM(2, data)
35 : /// \endcond
36 :
37 : namespace TimeSteppers {
38 : /// Minimum and maximum orders of a variable-order TimeStepper.
39 1 : struct VariableOrder {
40 0 : size_t minimum;
41 0 : size_t maximum;
42 :
43 0 : VariableOrder(const size_t minimum_in, const size_t maximum_in)
44 : : minimum(minimum_in), maximum(maximum_in) {}
45 : };
46 :
47 0 : bool operator==(const VariableOrder& a, const VariableOrder& b);
48 0 : bool operator!=(const VariableOrder& a, const VariableOrder& b);
49 :
50 0 : namespace Tags {
51 : /// Order of a fixed-order TimeStepper.
52 1 : struct FixedOrder {
53 0 : using type = size_t;
54 : };
55 :
56 : /// Minimum and maximum orders of a variable-order TimeStepper.
57 1 : struct VariableOrder {
58 0 : using type = TimeSteppers::VariableOrder;
59 : };
60 : } // namespace Tags
61 : } // namespace TimeSteppers
62 :
63 : /// \ingroup TimeSteppersGroup
64 : ///
65 : /// Abstract base class for TimeSteppers.
66 : ///
67 : /// Several of the member functions of this class are templated and
68 : /// perform type erasure before forwarding their arguments to the
69 : /// derived classes. This is implemented using the macros \ref
70 : /// TIME_STEPPER_DECLARE_OVERLOADS, which must be placed in a private
71 : /// section of the class body, and
72 : /// TIME_STEPPER_DEFINE_OVERLOADS(derived_class), which must be placed
73 : /// in the cpp file.
74 1 : class TimeStepper : public PUP::able {
75 : public:
76 0 : static constexpr bool local_time_stepping = false;
77 0 : static constexpr bool imex = false;
78 0 : using provided_time_stepper_interfaces = tmpl::list<TimeStepper>;
79 :
80 0 : WRAPPED_PUPable_abstract(TimeStepper); // NOLINT
81 :
82 : /// \cond
83 : #define TIME_STEPPER_DECLARE_VIRTUALS_IMPL(_, data) \
84 : virtual void update_u_forward( \
85 : const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
86 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
87 : data)>& history, \
88 : const TimeDelta& time_step) const = 0; \
89 : virtual std::optional<StepperErrorEstimate> update_u_forward( \
90 : const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
91 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
92 : data)>& history, \
93 : const TimeDelta& time_step, \
94 : const StepperErrorTolerances& tolerances) const = 0; \
95 : virtual void clean_history_forward( \
96 : const TimeSteppers::MutableUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
97 : data)>& history) const = 0; \
98 : virtual bool dense_update_u_forward( \
99 : const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
100 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
101 : data)>& history, \
102 : double time) const = 0; \
103 : virtual bool can_change_step_size_forward( \
104 : const TimeStepId& time_id, \
105 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
106 : data)>& history) const = 0;
107 :
108 : GENERATE_INSTANTIATIONS(TIME_STEPPER_DECLARE_VIRTUALS_IMPL,
109 : (MATH_WRAPPER_TYPES))
110 : #undef TIME_STEPPER_DECLARE_VIRTUALS_IMPL
111 : /// \endcond
112 :
113 : /// Set \p u to the value at the end of the current substep.
114 : ///
115 : /// Derived classes must implement this as a function with signature
116 : ///
117 : /// ```
118 : /// template <typename T>
119 : /// void update_u_impl(gsl::not_null<T*> u,
120 : /// const ConstUntypedHistory<T>& history,
121 : /// const TimeDelta& time_step) const;
122 : /// ```
123 : template <typename Vars>
124 1 : void update_u(const gsl::not_null<Vars*> u,
125 : const TimeSteppers::History<Vars>& history,
126 : const TimeDelta& time_step) const {
127 : return update_u_forward(&*make_math_wrapper(u), history.untyped(),
128 : time_step);
129 : }
130 :
131 : /// Set \p u to the value at the end of the current substep; report the error
132 : /// measure when available.
133 : ///
134 : /// For a substep method, the error measure will only be available on full
135 : /// steps. For a multistep method, the error measure will only be available
136 : /// when a sufficient number of steps are available in the `history` to
137 : /// compare two orders of step. Whenever the error measure is unavailable,
138 : /// the return value is empty
139 : ///
140 : /// If \p tolerances requests no estimates (e.g., a
141 : /// default-constructed object), no error measures are calculated,
142 : /// but any additional substeps necessary for error estimation are
143 : /// still taken. This is useful when a system integrates multiple
144 : /// variables in separate calls to the TimeStepper, but only uses
145 : /// estimates from some of them.
146 : ///
147 : /// Derived classes must implement this as a function with signature
148 : ///
149 : /// ```
150 : /// template <typename T>
151 : /// std::optional<StepperErrorEstimate> update_u_impl(
152 : /// gsl::not_null<T*> u,
153 : /// const ConstUntypedHistory<T>& history,
154 : /// const TimeDelta& time_step,
155 : /// const StepperErrorTolerances& tolerances) const;
156 : /// ```
157 : template <typename Vars>
158 1 : std::optional<StepperErrorEstimate> update_u(
159 : const gsl::not_null<Vars*> u, const TimeSteppers::History<Vars>& history,
160 : const TimeDelta& time_step,
161 : const StepperErrorTolerances& tolerances) const {
162 : return update_u_forward(&*make_math_wrapper(u), history.untyped(),
163 : time_step, tolerances);
164 : }
165 :
166 : /// Remove old entries from the history.
167 : ///
168 : /// This should be called after update_u and dense output.
169 : /// Afterward, the history will generally require a new entry to be
170 : /// added before it can be used by the TimeStepper.
171 : ///
172 : /// Derived classes must implement this as a function with signature
173 : ///
174 : /// ```
175 : /// template <typename T>
176 : /// void clean_history_impl(const MutableUntypedHistory<T>& history) const;
177 : /// ```
178 : template <typename Vars>
179 1 : void clean_history(
180 : const gsl::not_null<TimeSteppers::History<Vars>*> history) const {
181 : return clean_history_forward(history->untyped());
182 : }
183 :
184 : /// Compute the solution value at a time between steps. To evaluate
185 : /// at a time within a given step, call this method at the start of
186 : /// the step containing the time. The function returns true on
187 : /// success, otherwise the call should be retried after the next
188 : /// substep.
189 : ///
190 : /// The change from the partial step will be added to the initial
191 : /// value, so \p u should generally be initialized to
192 : /// `*history.step_start(time).value`. (TimeStepper
193 : /// implementations are required to keep this value in the history.)
194 : ///
195 : /// Derived classes must implement this as a function with signature
196 : ///
197 : /// ```
198 : /// template <typename T>
199 : /// bool dense_update_u_impl(
200 : /// gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,
201 : /// double time) const;
202 : /// ```
203 : template <typename Vars>
204 1 : bool dense_update_u(const gsl::not_null<Vars*> u,
205 : const TimeSteppers::History<Vars>& history,
206 : const double time) const {
207 : return dense_update_u_forward(&*make_math_wrapper(u), history.untyped(),
208 : time);
209 : }
210 :
211 : /// The convergence order of the stepper
212 : virtual variants::TaggedVariant<TimeSteppers::Tags::FixedOrder,
213 : TimeSteppers::Tags::VariableOrder>
214 1 : order() const = 0;
215 :
216 : /// Number of substeps in this TimeStepper
217 1 : virtual uint64_t number_of_substeps() const = 0;
218 :
219 : /// Number of substeps in this TimeStepper when providing an error measure for
220 : /// adaptive time-stepping
221 : ///
222 : /// \details Certain substep methods (e.g. embedded RK4(3)) require additional
223 : /// steps when providing an error measure of the integration.
224 1 : virtual uint64_t number_of_substeps_for_error() const = 0;
225 :
226 : /// Number of past time steps needed for multi-step method
227 1 : virtual size_t number_of_past_steps() const = 0;
228 :
229 : /// Rough estimate of the maximum step size this method can take
230 : /// stably as a multiple of the step for Euler's method.
231 1 : virtual double stable_step() const = 0;
232 :
233 : /// Whether computational and temporal orderings of operations
234 : /// match.
235 : ///
236 : /// If this method returns true, then, for two time-stepper
237 : /// operations occurring at different simulation times, the
238 : /// temporally earlier operation will be performed first. These
239 : /// operations include RHS evaluation, dense output, and neighbor
240 : /// communication. In particular, dense output never requires
241 : /// performing a RHS evaluation later than the output time, so
242 : /// control systems measurements cannot cause deadlocks.
243 : ///
244 : /// \warning This guarantee only holds if the time steps themselves
245 : /// are monotonic, which can be violated during initialization.
246 1 : virtual bool monotonic() const = 0;
247 :
248 : /// The TimeStepId after the current substep
249 1 : virtual TimeStepId next_time_id(const TimeStepId& current_id,
250 : const TimeDelta& time_step) const = 0;
251 :
252 : /// The TimeStepId after the current substep when providing an error measure
253 : /// for adaptive time-stepping.
254 : ///
255 : /// Certain substep methods (e.g. embedded RK4(3)) require additional
256 : /// steps when providing an error measure of the integration.
257 1 : virtual TimeStepId next_time_id_for_error(
258 : const TimeStepId& current_id, const TimeDelta& time_step) const = 0;
259 :
260 : /// Whether a change in the step size is allowed before taking
261 : /// a step. Step sizes can never be changed on a substep.
262 : ///
263 : /// Derived classes must implement this as a function with signature
264 : ///
265 : /// ```
266 : /// template <typename T>
267 : /// bool can_change_step_size_impl(
268 : /// const TimeStepId& time_id,
269 : /// const ConstUntypedHistory<T>& history) const;
270 : /// ```
271 : template <typename Vars>
272 1 : bool can_change_step_size(const TimeStepId& time_id,
273 : const TimeSteppers::History<Vars>& history) const {
274 : return can_change_step_size_forward(time_id, history.untyped());
275 : }
276 : };
277 :
278 : /// \cond
279 : #define TIME_STEPPER_DECLARE_OVERLOADS_IMPL(_, data) \
280 : void update_u_forward( \
281 : gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
282 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
283 : data)>& history, \
284 : const TimeDelta& time_step) const override; \
285 : std::optional<StepperErrorEstimate> update_u_forward( \
286 : gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
287 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
288 : data)>& history, \
289 : const TimeDelta& time_step, \
290 : const StepperErrorTolerances& tolerances) const override; \
291 : void clean_history_forward( \
292 : const TimeSteppers::MutableUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
293 : data)>& history) const override; \
294 : bool dense_update_u_forward( \
295 : gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
296 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
297 : data)>& history, \
298 : double time) const override; \
299 : bool can_change_step_size_forward( \
300 : const TimeStepId& time_id, \
301 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
302 : data)>& history) const override;
303 :
304 : #define TIME_STEPPER_DEFINE_OVERLOADS_IMPL(_, data) \
305 : TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data) \
306 : void TIME_STEPPER_DERIVED_CLASS(data)::update_u_forward( \
307 : const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
308 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
309 : data)>& history, \
310 : const TimeDelta& time_step) const { \
311 : update_u_impl(u, history, time_step); \
312 : } \
313 : TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data) \
314 : std::optional<StepperErrorEstimate> \
315 : TIME_STEPPER_DERIVED_CLASS(data)::update_u_forward( \
316 : const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
317 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
318 : data)>& history, \
319 : const TimeDelta& time_step, \
320 : const StepperErrorTolerances& tolerances) const { \
321 : return update_u_impl(u, history, time_step, tolerances); \
322 : } \
323 : TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data) \
324 : void TIME_STEPPER_DERIVED_CLASS(data)::clean_history_forward( \
325 : const TimeSteppers::MutableUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
326 : data)>& history) const { \
327 : return clean_history_impl(history); \
328 : } \
329 : TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data) \
330 : bool TIME_STEPPER_DERIVED_CLASS(data)::dense_update_u_forward( \
331 : const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u, \
332 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
333 : data)>& history, \
334 : const double time) const { \
335 : return dense_update_u_impl(u, history, time); \
336 : } \
337 : TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data) \
338 : bool TIME_STEPPER_DERIVED_CLASS(data)::can_change_step_size_forward( \
339 : const TimeStepId& time_id, \
340 : const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \
341 : data)>& history) const { \
342 : return can_change_step_size_impl(time_id, history); \
343 : }
344 : /// \endcond
345 :
346 : /// \ingroup TimeSteppersGroup
347 : /// Macro declaring overloaded detail methods in classes derived from
348 : /// TimeStepper. Must be placed in a private section of the class
349 : /// body.
350 1 : #define TIME_STEPPER_DECLARE_OVERLOADS \
351 : GENERATE_INSTANTIATIONS(TIME_STEPPER_DECLARE_OVERLOADS_IMPL, \
352 : (MATH_WRAPPER_TYPES))
353 :
354 : /// \ingroup TimeSteppersGroup
355 : /// Macro defining overloaded detail methods in classes derived from
356 : /// TimeStepper. Must be placed in the cpp file for the derived
357 : /// class.
358 : /// @{
359 1 : #define TIME_STEPPER_DEFINE_OVERLOADS(derived_class) \
360 : GENERATE_INSTANTIATIONS(TIME_STEPPER_DEFINE_OVERLOADS_IMPL, \
361 : (MATH_WRAPPER_TYPES), (derived_class), ())
362 1 : #define TIME_STEPPER_DEFINE_OVERLOADS_TEMPLATED(derived_class, template_args) \
363 : GENERATE_INSTANTIATIONS(TIME_STEPPER_DEFINE_OVERLOADS_IMPL, \
364 : (MATH_WRAPPER_TYPES), (derived_class), \
365 : (template <template_args>))
366 : /// @}
|