ExecutePhaseChange.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <tuple>
7 
9 #include "Parallel/AlgorithmMetafunctions.hpp"
10 #include "Parallel/GlobalCache.hpp"
11 #include "Parallel/Main.hpp"
12 #include "Parallel/PhaseControl/PhaseChange.hpp"
13 #include "Parallel/PhaseControl/PhaseControlTags.hpp"
14 #include "ParallelAlgorithms/EventsAndTriggers/Trigger.hpp"
15 #include "Utilities/TaggedTuple.hpp"
16 
17 namespace PhaseControl {
18 namespace Actions {
19 
20 /*!
21  * \ingroup ActionsGroup
22  * \brief Check if any triggers are activated, and perform phase changes as
23  * needed.
24  *
25  * This action is intended to be executed on every component that repeatedly
26  * runs iterable actions that would need to halt during a phase change. This
27  * action sends data to the Main chare via a reduction.
28  *
29  * This action iterates over the `Tags::PhaseChangeAndTriggers`, sending
30  * reduction data for the phase decision for each triggered `PhaseChange`, then
31  * halts the algorithm execution so that the `Main` chare can make a phase
32  * decision if any were triggered.
33  *
34  * Uses:
35  * - GlobalCache: `Tags::PhaseChangeAndTriggers`
36  * - DataBox: As specified by the `PhaseChange` option-created objects.
37  * - `PhaseChange` objects are permitted to perform mutations on the
38  * \ref DataBoxGroup "DataBox" to store persistent state information.
39  *
40  * \warning This action should almost always be placed at the end of the action
41  * list for a given phase, because the record of the index through the action
42  * list will not be retained during a phase change. Therefore, to avoid
43  * unexpected behavior, the phase changes should happen at the end of a phase,
44  * so that if control returns to the same phase it may continue looping as
45  * usual.
46  * If this suggestion is ignored, the typical pathology is an infinite loop of
47  * repeatedly triggering a phase change, because the trigger is examined too
48  * early in the action list for any alteration to have occurred before being
49  * interrupted again for a phase change.
50  */
51 template <typename PhaseChangeRegistrars, typename TriggerRegistrars>
53  template <typename DbTags, typename... InboxTags, typename Metavariables,
54  typename ArrayIndex, typename ActionList,
55  typename ParallelComponent>
57  db::DataBox<DbTags>& box,
58  const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
60  const ArrayIndex& array_index, const ActionList /*meta*/,
61  const ParallelComponent* const /*component*/) noexcept {
62  const auto& phase_change_and_triggers = Parallel::get<
64  cache);
65  bool should_halt = false;
66  for (const auto& [trigger, phase_changes] : phase_change_and_triggers) {
67  if (trigger->is_triggered(box)) {
68  for (const auto& phase_change : phase_changes) {
69  phase_change->template contribute_phase_data<ParallelComponent>(
70  make_not_null(&box), cache, array_index);
71  }
72  should_halt = true;
73  }
74  }
75  // if we halt, we need to make sure that the Main chare knows that it is
76  // because we are requesting phase change arbitration, regardless of what
77  // data was actually sent to make that decision.
78  if (should_halt) {
79  if constexpr (std::is_same_v<typename ParallelComponent::chare_type,
81  Parallel::contribute_to_phase_change_reduction<ParallelComponent>(
83  true},
84  cache, array_index);
85  } else {
86  Parallel::contribute_to_phase_change_reduction<ParallelComponent>(
88  true},
89  cache);
90  }
91  }
92  return {std::move(box), should_halt
93  ? Parallel::AlgorithmExecution::Halt
94  : Parallel::AlgorithmExecution::Continue};
95  }
96 };
97 } // namespace Actions
98 
99 /*!
100  * \brief Use the runtime data aggregated in `phase_change_decision_data` to
101  * decide which phase to execute next.
102  *
103  * \details This function will iterate through each of the option-created pairs
104  * of `PhaseChange`s, and obtain from each a
105  * `std::optional<std::pair<Metavariables::Phase,
106  * PhaseControl::ArbitrationStrategy>`. Any `std::nullopt` is skipped. If all
107  * `PhaseChange`s provide `std::nullopt`, the phase will either keep its
108  * current value (if the halt was caused by one of the triggers associated with
109  * an option-created `PhaseChange`), or this function will return a
110  * `std::nullopt` as well (otherwise), indicating that the phase should proceed
111  * according to other information, such as global ordering.
112  *
113  * In the case of a `PhaseControl::ArbitrationStrategy::RunPhaseImmediately`,
114  * the first such return value is immediately run, and no further `PhaseChange`s
115  * are queried for their input.
116  *
117  * \note There can be cases where multiple triggers activate, and/or multiple
118  * `PhaseChange` objects have data in a state for which they would request a
119  * specific phase. When multiple phases are requested, arbitration will
120  * proceed in order of appearance in the `PhaseChangeAndTriggers`, determined
121  * from the input file options. Therefore, if that order of execution is
122  * important for the logic of the executable, the input file ordering and
123  * `ArbitrationStrategy` must be chosen carefully.
124  */
125 template <typename PhaseChangeRegistrars, typename TriggerRegistrars,
126  typename... DecisionTags, typename Metavariables>
129  phase_change_decision_data,
130  typename Metavariables::Phase current_phase,
132  const auto& phase_change_and_triggers = Parallel::get<
134  cache);
135  bool phase_chosen = false;
136  for (const auto& [trigger, phase_changes] : phase_change_and_triggers) {
137  // avoid unused variable warning
138  (void)trigger;
139  for (const auto& phase_change : phase_changes) {
140  const auto phase_result = phase_change->arbitrate_phase_change(
141  phase_change_decision_data, current_phase, cache);
142  if (phase_result.has_value()) {
143  if (phase_result.value().second ==
144  ArbitrationStrategy::RunPhaseImmediately) {
145  tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
146  *phase_change_decision_data) = false;
147  return phase_result.value().first;
148  }
149  current_phase = phase_result.value().first;
150  phase_chosen = true;
151  }
152  }
153  }
154  if (tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
155  *phase_change_decision_data) == false and
156  not phase_chosen) {
157  return std::nullopt;
158  }
159  // if no phase change object suggests a specific phase, return to execution
160  // in the current phase.
161  tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
162  *phase_change_decision_data) = false;
163  return current_phase;
164 }
165 
166 /*!
167  * \brief Initialize the Main chare's `phase_change_decision_data` for the
168  * option-selected `PhaseChange`s.
169  *
170  * \details This struct provides a convenient method of specifying the
171  * initialization of the `phase_change_decision_data`. To instruct the Main
172  * chare to use this initialization routine, define the type alias in the
173  * `Metavariables`:
174  * ```
175  * using initialize_phase_data =
176  * PhaseControl::InitializePhaseChangeDecisionData<
177  * phase_change_registrars, trigger_registrars>;
178  * ```
179  */
180 template <typename PhaseChangeRegistrars, typename TriggerRegistrars>
182  template <typename... DecisionTags, typename Metavariables>
183  static void apply(
185  phase_change_decision_data,
187  tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
188  *phase_change_decision_data) = false;
189  const auto& phase_change_and_triggers = Parallel::get<
191  cache);
192  for (const auto& [trigger, phase_changes] : phase_change_and_triggers) {
193  // avoid unused variable warning
194  (void)trigger;
195  for (const auto& phase_change : phase_changes) {
196  phase_change->initialize_phase_data(phase_change_decision_data);
197  }
198  }
199  }
200 };
201 } // namespace PhaseControl
PhaseControl
Contains utilities for determining control-flow among phases.
Definition: ExecutePhaseChange.hpp:17
PhaseControl::InitializePhaseChangeDecisionData
Initialize the Main chare's phase_change_decision_data for the option-selected PhaseChanges.
Definition: ExecutePhaseChange.hpp:181
Main.hpp
Parallel::GlobalCache
Definition: ElementReceiveInterpPoints.hpp:15
GlobalCache.hpp
tuple
PhaseControl::Tags::PhaseChangeAndTriggers
Tag for the collection of triggers that indicate synchronization points at which phase changes should...
Definition: PhaseControlTags.hpp:139
PhaseControl::Actions::ExecutePhaseChange
Check if any triggers are activated, and perform phase changes as needed.
Definition: ExecutePhaseChange.hpp:52
DataBox.hpp
tuples::TaggedTuple
An associative container that is indexed by structs.
Definition: TaggedTuple.hpp:271
Parallel::AlgorithmExecution
AlgorithmExecution
The possible options for altering the current execution of the algorithm, used in the return type of ...
Definition: AlgorithmMetafunctions.hpp:31
ActionTesting::cache
Parallel::GlobalCache< Metavariables > & cache(MockRuntimeSystem< Metavariables > &runner, const ArrayIndex &array_index) noexcept
Returns the GlobalCache of Component with index array_index.
Definition: MockRuntimeSystemFreeFunctions.hpp:380
Parallel::Algorithms::Array
A struct that stores the charm++ types relevant for a particular array component.
Definition: AlgorithmArrayDeclarations.hpp:29
PhaseControl::arbitrate_phase_change
std::optional< typename Metavariables::Phase > arbitrate_phase_change(const gsl::not_null< tuples::TaggedTuple< DecisionTags... > * > phase_change_decision_data, typename Metavariables::Phase current_phase, const Parallel::GlobalCache< Metavariables > &cache) noexcept
Use the runtime data aggregated in phase_change_decision_data to decide which phase to execute next.
Definition: ExecutePhaseChange.hpp:127
std::optional
make_not_null
gsl::not_null< T * > make_not_null(T *ptr) noexcept
Construct a not_null from a pointer. Often this will be done as an implicit conversion,...
Definition: Gsl.hpp:880
Parallel::get
auto get(const GlobalCache< Metavariables > &cache) noexcept -> const GlobalCache_detail::type_for_get< GlobalCacheTag, Metavariables > &
Access data in the cache.
Definition: GlobalCache.hpp:566
gsl::not_null
Require a pointer to not be a nullptr
Definition: ReadSpecThirdOrderPiecewisePolynomial.hpp:13