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 <type_traits>
10 : #include <utility>
11 :
12 : #include "DataStructures/TaggedTuple.hpp"
13 : #include "Options/String.hpp"
14 : #include "Parallel/GlobalCache.hpp"
15 : #include "Parallel/Phase.hpp"
16 : #include "Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp"
17 : #include "Parallel/PhaseControl/PhaseChange.hpp"
18 : #include "Utilities/ErrorHandling/Error.hpp"
19 : #include "Utilities/Functional.hpp"
20 : #include "Utilities/MakeString.hpp"
21 : #include "Utilities/Serialization/CharmPupable.hpp"
22 : #include "Utilities/TMPL.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
|