SpECTRE Documentation Coverage Report
Current view: top level - Parallel/PhaseControl - ExecutePhaseChange.hpp Hit Total Coverage
Commit: 990bc653376fc4a4068db06123ec02659d814816 Lines: 4 8 50.0 %
Date: 2021-05-16 17:17:32
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <tuple>
       7             : 
       8             : #include "DataStructures/DataBox/DataBox.hpp"
       9             : #include "Parallel/AlgorithmMetafunctions.hpp"
      10             : #include "Parallel/GlobalCache.hpp"
      11             : #include "Parallel/Main.hpp"
      12             : #include "Parallel/PhaseControl/PhaseChange.hpp"
      13             : #include "Parallel/PhaseControl/PhaseControlTags.hpp"
      14             : #include "ParallelAlgorithms/EventsAndTriggers/Trigger.hpp"
      15             : #include "Utilities/TaggedTuple.hpp"
      16             : 
      17           1 : namespace PhaseControl {
      18           0 : namespace Actions {
      19             : 
      20             : /*!
      21             :  * \ingroup ActionsGroup
      22             :  * \brief Check if any triggers are activated, and perform phase changes as
      23             :  * needed.
      24             :  *
      25             :  * This action is intended to be executed on every component that repeatedly
      26             :  * runs iterable actions that would need to halt during a phase change. This
      27             :  * action sends data to the Main chare via a reduction.
      28             :  *
      29             :  * This action iterates over the `Tags::PhaseChangeAndTriggers`, sending
      30             :  * reduction data for the phase decision for each triggered `PhaseChange`, then
      31             :  * halts the algorithm execution so that the `Main` chare can make a phase
      32             :  * decision if any were triggered.
      33             :  *
      34             :  * Uses:
      35             :  * - GlobalCache: `Tags::PhaseChangeAndTriggers`
      36             :  * - DataBox: As specified by the `PhaseChange` option-created objects.
      37             :  *   - `PhaseChange` objects are permitted to perform mutations on the
      38             :  *     \ref DataBoxGroup "DataBox" to store persistent state information.
      39             :  *
      40             :  * \warning This action should almost always be placed at the end of the action
      41             :  * list for a given phase, because the record of the index through the action
      42             :  * list will not be retained during a phase change. Therefore, to avoid
      43             :  * unexpected behavior, the phase changes should happen at the end of a phase,
      44             :  * so that if control returns to the same phase it may continue looping as
      45             :  * usual.
      46             :  * If this suggestion is ignored, the typical pathology is an infinite loop of
      47             :  * repeatedly triggering a phase change, because the trigger is examined too
      48             :  * early in the action list for any alteration to have occurred before being
      49             :  * interrupted again for a phase change.
      50             :  */
      51             : template <typename PhaseChangeRegistrars>
      52           1 : struct ExecutePhaseChange {
      53             :   template <typename DbTags, typename... InboxTags, typename Metavariables,
      54             :             typename ArrayIndex, typename ActionList,
      55             :             typename ParallelComponent>
      56           0 :   static std::tuple<db::DataBox<DbTags>&&, Parallel::AlgorithmExecution> apply(
      57             :       db::DataBox<DbTags>& box,
      58             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
      59             :       Parallel::GlobalCache<Metavariables>& cache,
      60             :       const ArrayIndex& array_index, const ActionList /*meta*/,
      61             :       const ParallelComponent* const /*component*/) noexcept {
      62             :     const auto& phase_change_and_triggers =
      63             :         Parallel::get<Tags::PhaseChangeAndTriggers<PhaseChangeRegistrars>>(
      64             :             cache);
      65             :     bool should_halt = false;
      66             :     for (const auto& [trigger, phase_changes] : phase_change_and_triggers) {
      67             :       if (trigger->is_triggered(box)) {
      68             :         for (const auto& phase_change : phase_changes) {
      69             :           phase_change->template contribute_phase_data<ParallelComponent>(
      70             :               make_not_null(&box), cache, array_index);
      71             :         }
      72             :         should_halt = true;
      73             :       }
      74             :     }
      75             :     // if we halt, we need to make sure that the Main chare knows that it is
      76             :     // because we are requesting phase change arbitration, regardless of what
      77             :     // data was actually sent to make that decision.
      78             :     if (should_halt) {
      79             :       if constexpr (std::is_same_v<typename ParallelComponent::chare_type,
      80             :                     Parallel::Algorithms::Array>) {
      81             :         Parallel::contribute_to_phase_change_reduction<ParallelComponent>(
      82             :             tuples::TaggedTuple<TagsAndCombines::UsePhaseChangeArbitration>{
      83             :                 true},
      84             :             cache, array_index);
      85             :       } else {
      86             :         Parallel::contribute_to_phase_change_reduction<ParallelComponent>(
      87             :             tuples::TaggedTuple<TagsAndCombines::UsePhaseChangeArbitration>{
      88             :                 true},
      89             :             cache);
      90             :       }
      91             :     }
      92             :     return {std::move(box), should_halt
      93             :                                 ? Parallel::AlgorithmExecution::Halt
      94             :                                 : Parallel::AlgorithmExecution::Continue};
      95             :   }
      96             : };
      97             : }  // namespace Actions
      98             : 
      99             : /*!
     100             :  * \brief Use the runtime data aggregated in `phase_change_decision_data` to
     101             :  * decide which phase to execute next.
     102             :  *
     103             :  * \details This function will iterate through each of the option-created pairs
     104             :  * of `PhaseChange`s, and obtain from each a
     105             :  * `std::optional<std::pair<Metavariables::Phase,
     106             :  * PhaseControl::ArbitrationStrategy>`. Any `std::nullopt` is skipped. If all
     107             :  * `PhaseChange`s provide `std::nullopt`, the phase will either keep its
     108             :  * current value (if the halt was caused by one of the triggers associated with
     109             :  * an  option-created `PhaseChange`), or this function will return a
     110             :  * `std::nullopt` as well (otherwise), indicating that the phase should proceed
     111             :  * according to other information, such as global ordering.
     112             :  *
     113             :  * In the case of a `PhaseControl::ArbitrationStrategy::RunPhaseImmediately`,
     114             :  * the first such return value is immediately run, and no further `PhaseChange`s
     115             :  * are queried for their input.
     116             :  *
     117             :  * \note There can be cases where multiple triggers activate, and/or multiple
     118             :  * `PhaseChange` objects have data in a state for which they would request a
     119             :  * specific phase. When multiple phases are requested, arbitration will
     120             :  * proceed in order of appearance in the `PhaseChangeAndTriggers`, determined
     121             :  * from the input file options. Therefore, if that order of execution is
     122             :  * important for the logic of the executable, the input file ordering and
     123             :  * `ArbitrationStrategy` must be chosen carefully.
     124             :  */
     125             : template <typename PhaseChangeRegistrars, typename... DecisionTags,
     126             :           typename Metavariables>
     127           1 : typename std::optional<typename Metavariables::Phase> arbitrate_phase_change(
     128             :     const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>
     129             :         phase_change_decision_data,
     130             :     typename Metavariables::Phase current_phase,
     131             :     const Parallel::GlobalCache<Metavariables>& cache) noexcept {
     132             :   const auto& phase_change_and_triggers =
     133             :       Parallel::get<Tags::PhaseChangeAndTriggers<PhaseChangeRegistrars>>(
     134             :           cache);
     135             :   bool phase_chosen = false;
     136             :   for (const auto& [trigger, phase_changes] : phase_change_and_triggers) {
     137             :     // avoid unused variable warning
     138             :     (void)trigger;
     139             :     for (const auto& phase_change : phase_changes) {
     140             :       const auto phase_result = phase_change->arbitrate_phase_change(
     141             :           phase_change_decision_data, current_phase, cache);
     142             :       if (phase_result.has_value()) {
     143             :         if (phase_result.value().second ==
     144             :             ArbitrationStrategy::RunPhaseImmediately) {
     145             :           tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
     146             :               *phase_change_decision_data) = false;
     147             :           return phase_result.value().first;
     148             :         }
     149             :         current_phase = phase_result.value().first;
     150             :         phase_chosen = true;
     151             :       }
     152             :     }
     153             :   }
     154             :   if (tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
     155             :           *phase_change_decision_data) == false and
     156             :       not phase_chosen) {
     157             :     return std::nullopt;
     158             :   }
     159             :   // if no phase change object suggests a specific phase, return to execution
     160             :   // in the current phase.
     161             :   tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
     162             :       *phase_change_decision_data) = false;
     163             :   return current_phase;
     164             : }
     165             : 
     166             : /*!
     167             :  * \brief Initialize the Main chare's `phase_change_decision_data` for the
     168             :  * option-selected `PhaseChange`s.
     169             :  *
     170             :  * \details This struct provides a convenient method of specifying the
     171             :  * initialization of the `phase_change_decision_data`. To instruct the Main
     172             :  * chare to use this initialization routine, define the type alias in the
     173             :  * `Metavariables`:
     174             :  * ```
     175             :  * using initialize_phase_data =
     176             :  *   PhaseControl::InitializePhaseChangeDecisionData<phase_change_registrars>;
     177             :  * ```
     178             :  */
     179             : template <typename PhaseChangeRegistrars>
     180           1 : struct InitializePhaseChangeDecisionData {
     181             :   template <typename... DecisionTags, typename Metavariables>
     182           0 :   static void apply(
     183             :       const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>
     184             :           phase_change_decision_data,
     185             :       const Parallel::GlobalCache<Metavariables>& cache) noexcept {
     186             :     tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(
     187             :         *phase_change_decision_data) = false;
     188             :     const auto& phase_change_and_triggers =
     189             :         Parallel::get<Tags::PhaseChangeAndTriggers<PhaseChangeRegistrars>>(
     190             :             cache);
     191             :     for (const auto& [trigger, phase_changes] : phase_change_and_triggers) {
     192             :       // avoid unused variable warning
     193             :       (void)trigger;
     194             :       for (const auto& phase_change : phase_changes) {
     195             :         phase_change->initialize_phase_data(phase_change_decision_data);
     196             :       }
     197             :     }
     198             :   }
     199             : };
     200             : }  // namespace PhaseControl

Generated by: LCOV version 1.14