Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <memory>
7 : #include <optional>
8 : #include <type_traits>
9 : #include <utility>
10 : #include <vector>
11 :
12 : #include "DataStructures/DataBox/DataBox.hpp"
13 : #include "DataStructures/TaggedTuple.hpp"
14 : #include "Parallel/GlobalCache.hpp"
15 : #include "Parallel/Phase.hpp"
16 : #include "ParallelAlgorithms/EventsAndTriggers/Trigger.hpp"
17 : #include "Utilities/CallWithDynamicType.hpp"
18 : #include "Utilities/Gsl.hpp"
19 : #include "Utilities/TMPL.hpp"
20 :
21 : /// Contains utilities for determining control-flow among phases
22 : namespace PhaseControl {
23 : /// The possible options for instructing the Main chare in deciding the next
24 : /// phase to jump to.
25 : ///
26 : /// An object of this enum type is packaged with a requested phase in the
27 : /// `PhaseChange::arbitrate_phase_change` function.
28 1 : enum ArbitrationStrategy {
29 : /// Jump to the requested phase immediately, before considering other
30 : /// requested phases.
31 : ///
32 : /// This will ensure that the requested phase is always run, where
33 : /// alternative methods could have 'double-jumps' where the Main chare
34 : /// replaces a requested phase immediately without actually entering the
35 : /// phase.
36 : RunPhaseImmediately,
37 : /// After the requested phase is considered, continue considering other
38 : /// requests, potentially replacing this request.
39 : ///
40 : /// This will permit reprocessing the phase-jumps to help with cases where
41 : /// multiple phases are simultaneously requested.
42 : /// The `PermitAdditionalJumps` permits 'double-jumps' where a requested phase
43 : /// is immediately replaced by another phase to jump to.
44 : PermitAdditionalJumps
45 : };
46 : } // namespace PhaseControl
47 :
48 : /*!
49 : * \brief `PhaseChange` objects determine the storage types and logic for
50 : * moving between phases based on runtime data.
51 : *
52 : * The phase control flow must have the cooperation of each parallel component,
53 : * but make phase decisions centrally so that at any point, all components are
54 : * in the same phase. The operations needed by the parallel components and by
55 : * the Main chare, are:
56 : *
57 : * 1. Parallel components must select and/or compute the runtime data necessary
58 : * for choosing the next phase, then contribute it to a global reduction to
59 : * the Main component.
60 : * The components must then halt at a globally-valid state for the phase
61 : * change.
62 : * The requirements for the state will vary depending on the phase choices,
63 : * so triggers must be selected appropriately for the `PhaseChange` object.
64 : * For instance, selecting a common slab will usually represent a globally
65 : * well-behaved state for a `DgElementArray`.
66 : * 2. On the Main chare, the `PhaseChange` objects must use the collected
67 : * reduction data, or other persistent data stored in
68 : * `phase_change_decision_data` to decide on a phase to request and an
69 : * `PhaseControl::ArbitrationStrategy` to determine how to resolve multiple
70 : * simultaneous requests.
71 : * Additionally, the `PhaseChange` objects must specify initialization
72 : * functions to set the starting state of the tags in
73 : * `phase_change_decision_data` for which they are responsible.
74 : *
75 : * In addition to the `options` type alias and `static constexpr Options::String
76 : * help` variable needed to be option-creatable, a derived class of
77 : * `PhaseChange` must specify the type aliases:
78 : * - `argument_tags`: A `tmpl::list` of tags from the
79 : * \ref DataBoxGroup "DataBox" to be passed to `contribute_phase_data_impl` as
80 : * const references.
81 : * - `return_tags`: A `tmpl::list` of mutable tags from the
82 : * \ref DataBoxGroup "DataBox" to be passed to `contribute_phase_data_impl` as
83 : * `gsl::not_null` pointers. This should be used only for tags that may be
84 : * altered during the `contribute_phase_data_impl` function.
85 : * - `phase_change_tags_and_combines`: A `tmpl::list` of tags for
86 : * populating the `phase_change_decision_data` in the Main chare. Each tag in
87 : * this list must also define a `combine_method` and a `main_combine_method`
88 : * for performing the aggregation during reduction.
89 : * - `participating_components` (templated on `Metavariables`): A `tmpl::list`
90 : * of components that contribute data during this reduction. This can be used
91 : * to screen out components that will not have the necessary information to
92 : * contribute to the reduction. If all components should participate, this
93 : * type alias can be set to simply `typename Metavariables::component_list`.
94 : *
95 : * And member functions with signatures:
96 : *
97 : * ```
98 : * template <typename... DecisionTags>
99 : * void initialize_phase_data_impl(
100 : * const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>
101 : * phase_change_decision_data) const;
102 : * ```
103 : * - Must set all tags in `phase_change_tags_and_combines` to useful
104 : * initial states in the `phase_change_decision_data`.
105 : *
106 : * ```
107 : * template <typename ParallelComponent, typename ArrayIndex>
108 : * void contribute_phase_data_impl(
109 : * [DataBox return tags...], [DataBox argument tags...],
110 : * Parallel::GlobalCache<Metavariables>& cache,
111 : * const ArrayIndex& array_index) const;
112 : * ```
113 : * - Should send any data relevant for the associated phase change decision made
114 : * in `arbitrate_phase_change_impl` to the Main chare via function
115 : * `Parallel::contribute_to_phase_change_reduction`.
116 : *
117 : * ```
118 : * template <typename... DecisionTags, typename Metavariables>
119 : * typename std::optional<
120 : * std::pair<Parallel::Phase, ArbitrationStrategy>>
121 : * arbitrate_phase_change_impl(
122 : * const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>
123 : * phase_change_decision_data,
124 : * const Parallel::Phase current_phase,
125 : * const Parallel::GlobalCache<Metavariables>& cache) const;
126 : * ```
127 : * - Should examine the collected data in `phase_change_decision_data` and
128 : * optionally return a `std::pair` with the desired `Parallel::Phase` and
129 : * an `PhaseControl::ArbitrationStrategy` indicating a method for arbitrating
130 : * multiple simultaneous requests. Alternatively, it may return `std::nullopt`
131 : * to abstain from the phase decision.
132 : * The `arbitrate_phase_change_impl` may (and often will) mutate the
133 : * `phase_change_decision_data`. For instance, it may be desirable to 'reset'
134 : * the data to allow for future jumps associated with the same `PhaseChange`,
135 : * or the `PhaseChange` will describe multiple changes in sequence, and the
136 : * state of that sequential process can be recorded in
137 : * `phase_change_decision_data`.
138 : */
139 1 : struct PhaseChange : public PUP::able {
140 : protected:
141 : /// \cond
142 : PhaseChange() = default;
143 : PhaseChange(const PhaseChange&) = default;
144 : PhaseChange(PhaseChange&&) = default;
145 : PhaseChange& operator=(const PhaseChange&) = default;
146 : PhaseChange& operator=(PhaseChange&&) = default;
147 : /// \endcond
148 :
149 : public:
150 0 : PhaseChange(CkMigrateMessage* msg) : PUP::able(msg){};
151 :
152 0 : ~PhaseChange() override = default;
153 :
154 0 : WRAPPED_PUPable_abstract(PhaseChange); // NOLINT
155 :
156 : /// Send data from all `participating_components` to the Main chare for
157 : /// determining the next phase.
158 : template <typename ParallelComponent, typename DbTags, typename Metavariables,
159 : typename ArrayIndex>
160 1 : void contribute_phase_data(const gsl::not_null<db::DataBox<DbTags>*> box,
161 : Parallel::GlobalCache<Metavariables>& cache,
162 : const ArrayIndex& array_index) const {
163 : using factory_classes =
164 : typename Metavariables::factory_creation::factory_classes;
165 : call_with_dynamic_type<void, tmpl::at<factory_classes, PhaseChange>>(
166 : this, [&box, &cache, &array_index](const auto* const phase_change) {
167 : using phase_change_t = typename std::decay_t<decltype(*phase_change)>;
168 : if constexpr (tmpl::list_contains_v<
169 : typename phase_change_t::
170 : template participating_components<
171 : Metavariables>,
172 : ParallelComponent>) {
173 : db::mutate_apply<typename phase_change_t::return_tags,
174 : typename phase_change_t::argument_tags>(
175 : [&phase_change, &cache, &array_index](auto&&... args) {
176 : phase_change
177 : ->template contribute_phase_data_impl<ParallelComponent>(
178 : args..., cache, array_index);
179 : },
180 : box);
181 : }
182 : });
183 : }
184 :
185 : /// Determine a phase request and `PhaseControl::ArbitrationStrategy` based on
186 : /// aggregated `phase_change_decision_data` on the Main Chare.
187 : template <typename... DecisionTags, typename Metavariables>
188 : std::optional<std::pair<Parallel::Phase, PhaseControl::ArbitrationStrategy>>
189 1 : arbitrate_phase_change(
190 : const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>
191 : phase_change_decision_data,
192 : const Parallel::Phase current_phase,
193 : const Parallel::GlobalCache<Metavariables>& cache) const {
194 : using factory_classes =
195 : typename Metavariables::factory_creation::factory_classes;
196 : return call_with_dynamic_type<
197 : std::optional<
198 : std::pair<Parallel::Phase, PhaseControl::ArbitrationStrategy>>,
199 : tmpl::at<factory_classes, PhaseChange>>(
200 : this, [¤t_phase, &phase_change_decision_data,
201 : &cache](const auto* const phase_change) {
202 : return phase_change->arbitrate_phase_change_impl(
203 : phase_change_decision_data, current_phase, cache);
204 : });
205 : }
206 :
207 : /// Initialize the `phase_change_decision_data` on the main chare to starting
208 : /// values.
209 : template <typename Metavariables, typename... Tags>
210 1 : void initialize_phase_data(const gsl::not_null<tuples::TaggedTuple<Tags...>*>
211 : phase_change_decision_data) const {
212 : using factory_classes =
213 : typename Metavariables::factory_creation::factory_classes;
214 : return call_with_dynamic_type<void, tmpl::at<factory_classes, PhaseChange>>(
215 : this, [&phase_change_decision_data](const auto* const phase_change) {
216 : return phase_change->initialize_phase_data_impl(
217 : phase_change_decision_data);
218 : });
219 : }
220 : };
|