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 <optional>
8 : #include <tuple>
9 :
10 : #include "DataStructures/DataBox/DataBox.hpp"
11 : #include "DataStructures/DataBox/Subitems.hpp"
12 : #include "DataStructures/TaggedTuple.hpp"
13 : #include "DataStructures/Tensor/Tensor.hpp"
14 : #include "DataStructures/Variables.hpp"
15 : #include "Parallel/AlgorithmExecution.hpp"
16 : #include "ParallelAlgorithms/Amr/Protocols/Projector.hpp"
17 : #include "ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp"
18 : #include "ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp"
19 : #include "ParallelAlgorithms/Initialization/MutateAssign.hpp"
20 : #include "Time/EvolutionOrdering.hpp"
21 : #include "Time/Tags/Time.hpp"
22 : #include "Time/TimeSteppers/TimeStepper.hpp"
23 : #include "Utilities/Gsl.hpp"
24 : #include "Utilities/TMPL.hpp"
25 : #include "Utilities/TypeTraits/CreateIsCallable.hpp"
26 : #include "Utilities/TypeTraits/IsA.hpp"
27 :
28 : /// \cond
29 : class DataVector;
30 : template <size_t Dim>
31 : class Element;
32 : template <size_t Dim>
33 : class ElementId;
34 : template <size_t Dim>
35 : class Mesh;
36 : namespace Parallel {
37 : template <typename Metavariables>
38 : class GlobalCache;
39 : } // namespace Parallel
40 : namespace Tags {
41 : template <typename Tag>
42 : struct HistoryEvolvedVariables;
43 : struct TimeStep;
44 : struct TimeStepId;
45 : template <typename StepperInterface>
46 : struct TimeStepper;
47 : } // namespace Tags
48 : /// \endcond
49 :
50 0 : namespace evolution::Actions {
51 : /// \ingroup ActionsGroup
52 : /// \ingroup EventsAndTriggersGroup
53 : /// \brief Run the events and dense triggers
54 : ///
55 : /// If dense output is required, each `postprocessor` in the \p
56 : /// Postprocessors list will be called as
57 : /// `postprocessor::is_ready(make_not_null(&box),
58 : /// make_not_null(&inboxes), cache, array_index, component)`. If it
59 : /// returns false, the algorithm will be stopped to wait for more
60 : /// data. After performing dense output, each of the \p
61 : /// Postprocessors will be passed to `db::mutate_apply` on the
62 : /// DataBox. The wrapper struct `AlwaysReadyPostprocessor` is
63 : /// provided for convenience to provide an `is_ready` function when a
64 : /// pure mutate-apply is desired.
65 : ///
66 : /// At the end of the action, the values of the time, evolved
67 : /// variables, and anything appearing in the `return_tags` of the \p
68 : /// Postprocessors will be restored to their initial values.
69 : ///
70 : /// Uses:
71 : /// - DataBox: EventsAndDenseTriggers and as required by events,
72 : /// triggers, and postprocessors
73 : ///
74 : /// DataBox changes:
75 : /// - Adds: nothing
76 : /// - Removes: nothing
77 : /// - Modifies: as performed by the postprocessor `is_ready` functions
78 : template <typename Postprocessors>
79 1 : struct RunEventsAndDenseTriggers {
80 : private:
81 : static_assert(tt::is_a_v<tmpl::list, Postprocessors>);
82 :
83 : // RAII object to restore the time and variables changed by dense
84 : // output.
85 : template <typename DbTags, typename Tags>
86 0 : class StateRestorer {
87 : template <typename Tag, bool IsVariables>
88 0 : struct expand_variables_impl {
89 0 : using type = typename Tag::tags_list;
90 : };
91 :
92 : template <typename Tag>
93 0 : struct expand_variables_impl<Tag, false> {
94 0 : using type = tmpl::list<Tag>;
95 : };
96 :
97 : template <typename Tag>
98 0 : struct expand_variables
99 : : expand_variables_impl<Tag,
100 : tt::is_a_v<Variables, typename Tag::type>> {};
101 :
102 0 : using expanded_tags = tmpl::remove_duplicates<
103 : tmpl::join<tmpl::transform<Tags, expand_variables<tmpl::_1>>>>;
104 0 : using tensors_and_non_tensors = tmpl::partition<
105 : expanded_tags,
106 : tmpl::bind<
107 : tmpl::apply,
108 : tmpl::if_<tt::is_a<Tensor, tmpl::bind<tmpl::type_from, tmpl::_1>>,
109 : tmpl::defer<tmpl::parent<std::is_same<
110 : tmpl::bind<tmpl::type_from,
111 : tmpl::bind<tmpl::type_from, tmpl::_1>>,
112 : DataVector>>>,
113 : std::false_type>>>;
114 0 : using tensor_tags = tmpl::front<tensors_and_non_tensors>;
115 0 : using non_tensor_tags = tmpl::back<tensors_and_non_tensors>;
116 :
117 : public:
118 0 : StateRestorer(const gsl::not_null<db::DataBox<DbTags>*> box) : box_(box) {}
119 :
120 0 : void save() {
121 : // Only store the value the first time, because after that we
122 : // are seeing the value after the previous change instead of the
123 : // original.
124 : if (not non_tensors_.has_value()) {
125 : if constexpr (not std::is_same_v<tensor_tags, tmpl::list<>>) {
126 : tensors_.initialize(
127 : db::get<tmpl::front<tensor_tags>>(*box_).begin()->size());
128 : tmpl::for_each<tensor_tags>([this](auto tag_v) {
129 : using tag = tmpl::type_from<decltype(tag_v)>;
130 : get<tag>(tensors_) = db::get<tag>(*box_);
131 : });
132 : }
133 : tmpl::as_pack<non_tensor_tags>([this](auto... tags_v) {
134 : non_tensors_.emplace(
135 : db::get<tmpl::type_from<decltype(tags_v)>>(*box_)...);
136 : });
137 : }
138 : }
139 :
140 : // WARNING: Manually calling this if there are non_tensor_tags
141 : // will cause use-after-moves.
142 0 : void restore() {
143 : if (non_tensors_.has_value()) {
144 : tmpl::for_each<tensor_tags>([this](auto tag_v) {
145 : using tag = tmpl::type_from<decltype(tag_v)>;
146 : db::mutate<tag>(
147 : [this](const gsl::not_null<typename tag::type*> value) {
148 : *value = get<tag>(tensors_);
149 : },
150 : box_);
151 : });
152 : tmpl::for_each<non_tensor_tags>([this](auto tag_v) {
153 : using tag = tmpl::type_from<decltype(tag_v)>;
154 : db::mutate<tag>(
155 : [this](const gsl::not_null<typename tag::type*> value) {
156 : *value = std::move(tuples::get<tag>(*non_tensors_));
157 : },
158 : box_);
159 : });
160 : }
161 : }
162 :
163 0 : ~StateRestorer() { restore(); }
164 :
165 : private:
166 0 : gsl::not_null<db::DataBox<DbTags>*> box_ = nullptr;
167 : // Store all tensors in a single allocation.
168 0 : Variables<tensor_tags> tensors_{};
169 : std::optional<tuples::tagged_tuple_from_typelist<non_tensor_tags>>
170 0 : non_tensors_;
171 : };
172 :
173 : template <typename T>
174 0 : struct get_return_tags {
175 0 : using type = typename T::return_tags;
176 : };
177 :
178 : template <typename VariablesTags>
179 0 : struct DoDenseOutput;
180 :
181 : template <typename... VariablesTags>
182 0 : struct DoDenseOutput<tmpl::list<VariablesTags...>> {
183 0 : using return_tags = tmpl::list<VariablesTags...>;
184 0 : using argument_tags =
185 : tmpl::list<::Tags::TimeStepper<TimeStepper>,
186 : ::Tags::HistoryEvolvedVariables<VariablesTags>...>;
187 0 : static bool apply(
188 : const gsl::not_null<typename VariablesTags::type*>... vars,
189 : const TimeStepper& stepper,
190 : const TimeSteppers::History<typename VariablesTags::type>&... history,
191 : const double time) {
192 : return (... and ((void)(*vars = *history.step_start(time).value),
193 : stepper.dense_update_u(vars, history, time)));
194 : }
195 : };
196 :
197 : public:
198 : template <typename DbTags, typename... InboxTags, typename Metavariables,
199 : typename ArrayIndex, typename ActionList,
200 : typename ParallelComponent>
201 0 : static Parallel::iterable_action_return_t apply(
202 : db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
203 : Parallel::GlobalCache<Metavariables>& cache,
204 : const ArrayIndex& array_index, const ActionList /*meta*/,
205 : const ParallelComponent* const component) {
206 : using system = typename Metavariables::system;
207 : using variables_tags = tmpl::conditional_t<
208 : tt::is_a_v<tmpl::list, typename system::variables_tag>,
209 : typename system::variables_tag,
210 : tmpl::list<typename system::variables_tag>>;
211 :
212 : const auto& time_step_id = db::get<::Tags::TimeStepId>(box);
213 : if (time_step_id.slab_number() < 0) {
214 : // Skip dense output during self-start
215 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
216 : }
217 :
218 : auto& events_and_dense_triggers =
219 : db::get_mutable_reference<::Tags::EventsAndDenseTriggers>(
220 : make_not_null(&box));
221 :
222 : const auto step_end =
223 : time_step_id.step_time() + db::get<::Tags::TimeStep>(box);
224 : const evolution_less_equal<double> before_equal{
225 : time_step_id.time_runs_forward()};
226 :
227 : using postprocessor_return_tags =
228 : tmpl::join<tmpl::transform<Postprocessors, get_return_tags<tmpl::_1>>>;
229 : // The evolved variables will be restored anyway, so no reason to
230 : // copy them twice.
231 : using postprocessor_restore_tags = tmpl::list_difference<
232 : postprocessor_return_tags,
233 : tmpl::flatten<tmpl::list<
234 : variables_tags,
235 : tmpl::transform<variables_tags, db::Subitems<tmpl::_1>>>>>;
236 :
237 : StateRestorer<DbTags, tmpl::list<::Tags::Time>> time_restorer(
238 : make_not_null(&box));
239 : StateRestorer<DbTags, variables_tags> variables_restorer(
240 : make_not_null(&box));
241 : StateRestorer<DbTags, postprocessor_restore_tags> postprocessor_restorer(
242 : make_not_null(&box));
243 :
244 : for (;;) {
245 : const double next_trigger = events_and_dense_triggers.next_trigger(box);
246 : if (before_equal(step_end.value(), next_trigger)) {
247 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
248 : }
249 :
250 : // Avoid invalidating compute items unless necessary.
251 : if (db::get<::Tags::Time>(box) != next_trigger) {
252 : time_restorer.save();
253 : db::mutate<::Tags::Time>(
254 : [&next_trigger](const gsl::not_null<double*> time) {
255 : *time = next_trigger;
256 : },
257 : make_not_null(&box));
258 : }
259 :
260 : const auto triggered = events_and_dense_triggers.is_ready(
261 : make_not_null(&box), cache, array_index, component);
262 : using TriggeringState = std::decay_t<decltype(triggered)>;
263 : switch (triggered) {
264 : case TriggeringState::NotReady:
265 : return {Parallel::AlgorithmExecution::Retry, std::nullopt};
266 : case TriggeringState::NeedsEvolvedVariables: {
267 : variables_restorer.save();
268 : const bool dense_output_succeeded =
269 : db::mutate_apply<DoDenseOutput<variables_tags>>(
270 : make_not_null(&box), next_trigger);
271 : if (not dense_output_succeeded) {
272 : // Need to take another time step
273 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
274 : }
275 :
276 : bool ready = true;
277 : tmpl::for_each<Postprocessors>([&](auto postprocessor_v) {
278 : using postprocessor = tmpl::type_from<decltype(postprocessor_v)>;
279 : if (ready) {
280 : if (not postprocessor::is_ready(make_not_null(&box),
281 : make_not_null(&inboxes), cache,
282 : array_index, component)) {
283 : ready = false;
284 : }
285 : }
286 : });
287 : if (not ready) {
288 : return {Parallel::AlgorithmExecution::Retry, std::nullopt};
289 : }
290 :
291 : postprocessor_restorer.save();
292 : tmpl::for_each<Postprocessors>([&box](auto postprocessor_v) {
293 : using postprocessor = tmpl::type_from<decltype(postprocessor_v)>;
294 : db::mutate_apply<postprocessor>(make_not_null(&box));
295 : });
296 : }
297 : [[fallthrough]];
298 : default:
299 : break;
300 : }
301 :
302 : events_and_dense_triggers.run_events(box, cache, array_index, component);
303 : if (not events_and_dense_triggers.reschedule(make_not_null(&box), cache,
304 : array_index, component)) {
305 : return {Parallel::AlgorithmExecution::Retry, std::nullopt};
306 : }
307 : }
308 : }
309 : };
310 :
311 0 : struct InitializeRunEventsAndDenseTriggers {
312 0 : using simple_tags_from_options = tmpl::list<::Tags::EventsAndDenseTriggers>;
313 0 : using simple_tags = tmpl::list<::Tags::PreviousTriggerTime>;
314 :
315 : template <typename DbTags, typename... InboxTags, typename Metavariables,
316 : typename ArrayIndex, typename ActionList,
317 : typename ParallelComponent>
318 0 : static Parallel::iterable_action_return_t apply(
319 : db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
320 : Parallel::GlobalCache<Metavariables>& /*cache*/,
321 : const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
322 : const ParallelComponent* const /*component*/) {
323 : ::Initialization::mutate_assign<simple_tags>(make_not_null(&box),
324 : std::nullopt);
325 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
326 : }
327 : };
328 :
329 : /// \brief Initialize/update items related to events and dense triggers after an
330 : /// AMR change
331 : ///
332 : /// Mutates:
333 : /// - ::Tags::EventsAndDenseTriggers
334 : /// - Tags::PreviousTriggerTime
335 : ///
336 : /// For p-refinement:
337 : /// - Leaves both items unchanged
338 1 : struct ProjectRunEventsAndDenseTriggers
339 : : tt::ConformsTo<amr::protocols::Projector> {
340 0 : using return_tags =
341 : tmpl::list<::Tags::EventsAndDenseTriggers, ::Tags::PreviousTriggerTime>;
342 0 : using argument_tags = tmpl::list<>;
343 :
344 : template <size_t Dim>
345 0 : static void apply(
346 : const gsl::not_null<
347 : EventsAndDenseTriggers*> /*events_and_dense_triggers*/,
348 : const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,
349 : const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {
350 : // do not need to update anything
351 : }
352 :
353 : template <typename... Tags>
354 0 : static void apply(
355 : const gsl::not_null<EventsAndDenseTriggers*> events_and_dense_triggers,
356 : const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,
357 : const tuples::TaggedTuple<Tags...>& parent_items) {
358 : *events_and_dense_triggers =
359 : serialize_and_deserialize<EventsAndDenseTriggers>(
360 : get<::Tags::EventsAndDenseTriggers>(parent_items));
361 : }
362 :
363 : template <size_t Dim, typename... Tags>
364 0 : static void apply(
365 : const gsl::not_null<EventsAndDenseTriggers*> events_and_dense_triggers,
366 : const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,
367 : const std::unordered_map<ElementId<Dim>, tuples::TaggedTuple<Tags...>>&
368 : children_items) {
369 : // Serialization of equivalent Events and DenseTriggers is not
370 : // guaranteed to produce the same byte stream when there are
371 : // things like unordered containers involved, so we can't compare
372 : // with other children.
373 : *events_and_dense_triggers =
374 : serialize_and_deserialize<EventsAndDenseTriggers>(
375 : get<::Tags::EventsAndDenseTriggers>(
376 : children_items.begin()->second));
377 : }
378 : };
379 : } // namespace evolution::Actions
380 :
381 : /// A wrapper adding an always-true `is_ready` function for a
382 : /// `RunEventsAndDenseTriggers` postprocessor. This allows structs
383 : /// designed as mutate_apply arguments to be used without
384 : /// modification.
385 : template <typename T>
386 1 : struct AlwaysReadyPostprocessor : T {
387 : template <typename DbTagsList, typename... InboxTags, typename Metavariables,
388 : typename ArrayIndex, typename ParallelComponent>
389 0 : static bool is_ready(
390 : const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,
391 : const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> /*inboxes*/,
392 : Parallel::GlobalCache<Metavariables>& /*cache*/,
393 : const ArrayIndex& /*array_index*/,
394 : const ParallelComponent* const /*component*/) {
395 : return true;
396 : }
397 : };
|