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