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 "Parallel/AlgorithmExecution.hpp" 12 : #include "Parallel/GlobalCache.hpp" 13 : #include "Parallel/Phase.hpp" 14 : #include "Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp" 15 : #include "Parallel/PhaseControl/PhaseChange.hpp" 16 : #include "Parallel/PhaseControl/PhaseControlTags.hpp" 17 : #include "Utilities/Gsl.hpp" 18 : #include "Utilities/TMPL.hpp" 19 : #include "Utilities/TaggedTuple.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