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 <pup.h> 8 : #include <string> 9 : #include <utility> 10 : #include <type_traits> 11 : 12 : #include "Options/String.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 "Utilities/ErrorHandling/Error.hpp" 18 : #include "Utilities/Functional.hpp" 19 : #include "Utilities/MakeString.hpp" 20 : #include "Utilities/Serialization/CharmPupable.hpp" 21 : #include "Utilities/TMPL.hpp" 22 : #include "Utilities/TaggedTuple.hpp" 23 : 24 : namespace PhaseControl { 25 : namespace Tags { 26 : /// Storage in the phase change decision tuple so that the Main chare can record 27 : /// the phase to return to after a temporary phase. 28 : /// 29 : /// \note This tag is not intended to participate in any of the reduction 30 : /// procedures, so will error if the combine method is called. 31 : template <Parallel::Phase Phase> 32 1 : struct ReturnPhase { 33 0 : using type = std::optional<Parallel::Phase>; 34 : 35 0 : struct combine_method { 36 0 : std::optional<Parallel::Phase> operator()( 37 : const std::optional<Parallel::Phase> /*first_phase*/, 38 : const std::optional<Parallel::Phase>& /*second_phase*/) { 39 : ERROR( 40 : "The return phase should only be altered by the phase change " 41 : "arbitration in the Main chare, so no reduction data should be " 42 : "provided."); 43 : } 44 : }; 45 : 46 0 : using main_combine_method = combine_method; 47 : }; 48 : 49 : /// Stores whether the phase in question has been requested. 50 : /// 51 : /// Combinations are performed via `funcl::Or`, as the phase in question should 52 : /// be chosen if any component requests the jump. 53 : template <Parallel::Phase Phase> 54 1 : struct TemporaryPhaseRequested { 55 0 : using type = bool; 56 : 57 0 : using combine_method = funcl::Or<>; 58 0 : using main_combine_method = funcl::Or<>; 59 : }; 60 : } // namespace Tags 61 : 62 : /*! 63 : * \brief Phase control object for temporarily visiting `TargetPhase`, until the 64 : * algorithm halts again, then returning to the original phase. 65 : * 66 : * The motivation for this type of procedure is e.g. load balancing, 67 : * checkpointing, and other maintenance tasks that should be performed 68 : * periodically during a lengthy evolution. 69 : * Once triggered, this will cause a change to `TargetPhase`, but store the 70 : * current phase to resume execution when the tasks in `TargetPhase` are 71 : * completed. 72 : * 73 : * Any parallel component can participate in the associated phase change 74 : * reduction data contribution, and if any component requests the temporary 75 : * phase, it will execute. 76 : * 77 : * \note If multiple such methods are specified (with different 78 : * `TargetPhase`s), then the order of phase jumps depends on their order in the 79 : * list. 80 : * - If multiple `VisitAndReturn`s trigger simultaneously, then they will visit 81 : * in sequence specified by the input file: first going to the first 82 : * `TargetPhase` until that phase resolves, then immediately entering the 83 : * second `TargetPhase` (without yet returning to the original phase), then 84 : * finally returning to the original phase. 85 : * - If a `VisitAndReturn` is triggered in a phase that is already a 86 : * `TargetPhase` of another `VisitAndReturn`, it will be executed, and 87 : * following completion, control will return to the original phase from before 88 : * the first `VisitAndReturn`. 89 : */ 90 : template <Parallel::Phase TargetPhase> 91 1 : struct VisitAndReturn : public PhaseChange { 92 : /// \cond 93 : VisitAndReturn() = default; 94 : explicit VisitAndReturn(CkMigrateMessage* /*unused*/) {} 95 : using PUP::able::register_constructor; 96 : WRAPPED_PUPable_decl_template(VisitAndReturn); // NOLINT 97 : /// \endcond 98 : 99 0 : static std::string name() { 100 : return MakeString{} << "VisitAndReturn(" << TargetPhase << ")"; 101 : } 102 0 : using options = tmpl::list<>; 103 0 : static constexpr Options::String help{ 104 : "Temporarily jump to the phase given by `TargetPhase`, returning to the " 105 : "previously executing phase when complete."}; 106 : 107 0 : using argument_tags = tmpl::list<>; 108 0 : using return_tags = tmpl::list<>; 109 : 110 0 : using phase_change_tags_and_combines = 111 : tmpl::list<Tags::ReturnPhase<TargetPhase>, 112 : Tags::TemporaryPhaseRequested<TargetPhase>>; 113 : 114 : template <typename Metavariables> 115 0 : using participating_components = typename Metavariables::component_list; 116 : 117 : template <typename... DecisionTags> 118 0 : void initialize_phase_data_impl( 119 : const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*> 120 : phase_change_decision_data) const { 121 : tuples::get<Tags::ReturnPhase<TargetPhase>>(*phase_change_decision_data) = 122 : std::nullopt; 123 : tuples::get<Tags::TemporaryPhaseRequested<TargetPhase>>( 124 : *phase_change_decision_data) = false; 125 : } 126 : 127 : template <typename ParallelComponent, typename ArrayIndex, 128 : typename Metavariables> 129 0 : void contribute_phase_data_impl(Parallel::GlobalCache<Metavariables>& cache, 130 : const ArrayIndex& array_index) const { 131 : if constexpr (std::is_same_v<typename ParallelComponent::chare_type, 132 : Parallel::Algorithms::Array>) { 133 : Parallel::contribute_to_phase_change_reduction<ParallelComponent>( 134 : tuples::TaggedTuple<Tags::TemporaryPhaseRequested<TargetPhase>>{true}, 135 : cache, array_index); 136 : } else { 137 : Parallel::contribute_to_phase_change_reduction<ParallelComponent>( 138 : tuples::TaggedTuple<Tags::TemporaryPhaseRequested<TargetPhase>>{true}, 139 : cache); 140 : } 141 : } 142 : 143 : template <typename... DecisionTags, typename Metavariables> 144 : typename std::optional<std::pair<Parallel::Phase, ArbitrationStrategy>> 145 0 : arbitrate_phase_change_impl( 146 : const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*> 147 : phase_change_decision_data, 148 : const Parallel::Phase current_phase, 149 : const Parallel::GlobalCache<Metavariables>& /*cache*/) const { 150 : auto& return_phase = tuples::get<Tags::ReturnPhase<TargetPhase>>( 151 : *phase_change_decision_data); 152 : if (return_phase.has_value()) { 153 : const auto result = return_phase; 154 : return_phase.reset(); 155 : return std::make_pair(result.value(), 156 : ArbitrationStrategy::PermitAdditionalJumps); 157 : } 158 : auto& temporary_phase_requested = 159 : tuples::get<Tags::TemporaryPhaseRequested<TargetPhase>>( 160 : *phase_change_decision_data); 161 : if (temporary_phase_requested) { 162 : return_phase = current_phase; 163 : temporary_phase_requested = false; 164 : return std::make_pair(TargetPhase, 165 : ArbitrationStrategy::RunPhaseImmediately); 166 : } 167 : return std::nullopt; 168 : } 169 : 170 0 : void pup(PUP::er& /*p*/) override {} 171 : }; 172 : } // namespace PhaseControl 173 : 174 : /// \cond 175 : template <Parallel::Phase TargetPhase> 176 : PUP::able::PUP_ID PhaseControl::VisitAndReturn<TargetPhase>::my_PUP_ID = 0; 177 : /// \endcond