Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <optional>
7 : #include <tuple>
8 : #include <type_traits>
9 : #include <utility>
10 :
11 : #include "DataStructures/TaggedTuple.hpp"
12 : #include "Parallel/AlgorithmExecution.hpp"
13 : #include "Parallel/GlobalCache.hpp"
14 : #include "Parallel/Phase.hpp"
15 : #include "Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp"
16 : #include "Parallel/PhaseControl/PhaseChange.hpp"
17 : #include "Parallel/PhaseControl/PhaseControlTags.hpp"
18 : #include "Utilities/Gsl.hpp"
19 : #include "Utilities/TMPL.hpp"
20 :
21 : /// \cond
22 : namespace Tags {
23 : struct TimeStepId;
24 : } // namespace Tags
25 : namespace db {
26 : template <typename TagsList>
27 : class DataBox;
28 : } // namespace db
29 : /// \endcond
30 :
31 : namespace PhaseControl {
32 0 : namespace Actions {
33 :
34 : /*!
35 : * \ingroup ActionsGroup
36 : * \brief Check if any triggers are activated, and perform phase changes as
37 : * needed.
38 : *
39 : * This action is intended to be executed on every component that repeatedly
40 : * runs iterable actions that would need to halt during a phase change. This
41 : * action sends data to the Main chare via a reduction.
42 : *
43 : * This action iterates over the `Tags::PhaseChangeAndTriggers`, sending
44 : * reduction data for the phase decision for each triggered `PhaseChange`, then
45 : * halts the algorithm execution so that the `Main` chare can make a phase
46 : * decision if any were triggered.
47 : *
48 : * Uses:
49 : * - GlobalCache: `Tags::PhaseChangeAndTriggers`
50 : * - DataBox: As specified by the `PhaseChange` option-created objects.
51 : * - `PhaseChange` objects are permitted to perform mutations on the
52 : * \ref DataBoxGroup "DataBox" to store persistent state information.
53 : */
54 1 : struct ExecutePhaseChange {
55 0 : using const_global_cache_tags =
56 : tmpl::list<PhaseControl::Tags::PhaseChangeAndTriggers>;
57 :
58 : template <typename DbTags, typename... InboxTags, typename Metavariables,
59 : typename ArrayIndex, typename ActionList,
60 : typename ParallelComponent>
61 0 : static Parallel::iterable_action_return_t apply(
62 : db::DataBox<DbTags>& box,
63 : const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
64 : Parallel::GlobalCache<Metavariables>& cache,
65 : const ArrayIndex& array_index, const ActionList /*meta*/,
66 : const ParallelComponent* const /*component*/) {
67 : if constexpr (db::tag_is_retrievable_v<::Tags::TimeStepId,
68 : db::DataBox<DbTags>>) {
69 : if (not db::get<::Tags::TimeStepId>(box).is_at_slab_boundary()) {
70 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
71 : }
72 : }
73 : const auto& phase_change_and_triggers =
74 : Parallel::get<Tags::PhaseChangeAndTriggers>(cache);
75 : bool should_halt = false;
76 : for (const auto& trigger_and_phase_changes : phase_change_and_triggers) {
77 : const auto& trigger = trigger_and_phase_changes.trigger;
78 : if (trigger->is_triggered(box)) {
79 : const auto& phase_changes = trigger_and_phase_changes.phase_changes;
80 : for (const auto& phase_change : phase_changes) {
81 : phase_change->template contribute_phase_data<ParallelComponent>(
82 : make_not_null(&box), cache, array_index);
83 : }
84 : should_halt = true;
85 : }
86 : }
87 : // if we halt, we need to make sure that the Main chare knows that it is
88 : // because we are requesting phase change arbitration, regardless of what
89 : // data was actually sent to make that decision.
90 : if (should_halt) {
91 : if constexpr (std::is_same_v<typename ParallelComponent::chare_type,
92 : Parallel::Algorithms::Array>) {
93 : Parallel::contribute_to_phase_change_reduction<ParallelComponent>(
94 : tuples::TaggedTuple<TagsAndCombines::UsePhaseChangeArbitration>{
95 : true},
96 : cache, array_index);
97 : } else {
98 : Parallel::contribute_to_phase_change_reduction<ParallelComponent>(
99 : tuples::TaggedTuple<TagsAndCombines::UsePhaseChangeArbitration>{
100 : true},
101 : cache);
102 : }
103 : }
104 : return {should_halt ? Parallel::AlgorithmExecution::Halt
105 : : Parallel::AlgorithmExecution::Continue,
106 : std::nullopt};
107 : }
108 : };
109 : } // namespace Actions
110 :
111 : /*!
112 : * \brief Use the runtime data aggregated in `phase_change_decision_data` to
113 : * decide which phase to execute next.
114 : *
115 : * \details This function will iterate through each of the option-created pairs
116 : * of `PhaseChange`s, and obtain from each a
117 : * `std::optional<std::pair<Parallel::Phase,
118 : * PhaseControl::ArbitrationStrategy>`. Any `std::nullopt` is skipped. If all
119 : * `PhaseChange`s provide `std::nullopt`, the phase will either keep its
120 : * current value (if the halt was caused by one of the triggers associated with
121 : * an option-created `PhaseChange`), or this function will return a
122 : * `std::nullopt` as well (otherwise), indicating that the phase should proceed
123 : * according to other information, such as global ordering.
124 : *
125 : * In the case of a `PhaseControl::ArbitrationStrategy::RunPhaseImmediately`,
126 : * the first such return value is immediately run, and no further `PhaseChange`s
127 : * are queried for their input.
128 : *
129 : * \note There can be cases where multiple triggers activate, and/or multiple
130 : * `PhaseChange` objects have data in a state for which they would request a
131 : * specific phase. When multiple phases are requested, arbitration will
132 : * proceed in order of appearance in the `PhaseChangeAndTriggers`, determined
133 : * from the input file options. Therefore, if that order of execution is
134 : * important for the logic of the executable, the input file ordering and
135 : * `ArbitrationStrategy` must be chosen carefully.
136 : */
137 : template <typename... DecisionTags, typename Metavariables>
138 1 : typename std::optional<Parallel::Phase> arbitrate_phase_change(
139 : const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>
140 : phase_change_decision_data,
141 : Parallel::Phase current_phase,
142 : const Parallel::GlobalCache<Metavariables>& cache) {
143 : if constexpr (tmpl::list_contains_v<typename Parallel::GlobalCache<
144 : Metavariables>::const_tags_list,
145 : Tags::PhaseChangeAndTriggers>) {
146 : const auto& phase_change_and_triggers =
147 : Parallel::get<Tags::PhaseChangeAndTriggers>(cache);
148 : bool phase_chosen = false;
149 : for (const auto& trigger_and_phase_changes : phase_change_and_triggers) {
150 : for (const auto& phase_change : trigger_and_phase_changes.phase_changes) {
151 : const auto phase_result = phase_change->arbitrate_phase_change(
152 : phase_change_decision_data, current_phase, cache);
153 : if (phase_result.has_value()) {
154 : if (phase_result.value().second ==
155 : ArbitrationStrategy::RunPhaseImmediately) {
156 : tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
157 : *phase_change_decision_data) = false;
158 : return phase_result.value().first;
159 : }
160 : current_phase = phase_result.value().first;
161 : phase_chosen = true;
162 : }
163 : }
164 : }
165 : if (tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
166 : *phase_change_decision_data) == false and
167 : not phase_chosen) {
168 : return std::nullopt;
169 : }
170 : // if no phase change object suggests a specific phase, return to execution
171 : // in the current phase.
172 : tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
173 : *phase_change_decision_data) = false;
174 : return current_phase;
175 : } else {
176 : (void)phase_change_decision_data;
177 : (void)current_phase;
178 : (void)cache;
179 : return std::nullopt;
180 : }
181 : }
182 : } // namespace PhaseControl
|