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