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