SpECTRE Documentation Coverage Report
Current view: top level - Evolution/Actions - RunEventsAndDenseTriggers.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 3 35 8.6 %
Date: 2025-12-05 05:03:31
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 <cstddef>
       7             : #include <optional>
       8             : #include <tuple>
       9             : 
      10             : #include "DataStructures/DataBox/DataBox.hpp"
      11             : #include "DataStructures/Tensor/Tensor.hpp"
      12             : #include "DataStructures/Variables.hpp"
      13             : #include "Parallel/AlgorithmExecution.hpp"
      14             : #include "ParallelAlgorithms/Amr/Protocols/Projector.hpp"
      15             : #include "ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp"
      16             : #include "ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp"
      17             : #include "ParallelAlgorithms/Initialization/MutateAssign.hpp"
      18             : #include "Time/EvolutionOrdering.hpp"
      19             : #include "Time/Tags/Time.hpp"
      20             : #include "Time/TimeSteppers/TimeStepper.hpp"
      21             : #include "Utilities/Gsl.hpp"
      22             : #include "Utilities/TMPL.hpp"
      23             : #include "Utilities/TaggedTuple.hpp"
      24             : #include "Utilities/TypeTraits/CreateIsCallable.hpp"
      25             : #include "Utilities/TypeTraits/IsA.hpp"
      26             : 
      27             : /// \cond
      28             : class DataVector;
      29             : template <size_t Dim>
      30             : class Element;
      31             : template <size_t Dim>
      32             : class ElementId;
      33             : template <size_t Dim>
      34             : class Mesh;
      35             : namespace Parallel {
      36             : template <typename Metavariables>
      37             : class GlobalCache;
      38             : }  // namespace Parallel
      39             : namespace Tags {
      40             : template <typename Tag>
      41             : struct HistoryEvolvedVariables;
      42             : struct TimeStep;
      43             : struct TimeStepId;
      44             : template <typename StepperInterface>
      45             : struct TimeStepper;
      46             : }  // namespace Tags
      47             : /// \endcond
      48             : 
      49           0 : namespace evolution::Actions {
      50             : /// \ingroup ActionsGroup
      51             : /// \ingroup EventsAndTriggersGroup
      52             : /// \brief Run the events and dense triggers
      53             : ///
      54             : /// If dense output is required, each `postprocessor` in the \p
      55             : /// Postprocessors list will be called as
      56             : /// `postprocessor::is_ready(make_not_null(&box),
      57             : /// make_not_null(&inboxes), cache, array_index, component)`.  If it
      58             : /// returns false, the algorithm will be stopped to wait for more
      59             : /// data.  After performing dense output, each of the \p
      60             : /// Postprocessors will be passed to `db::mutate_apply` on the
      61             : /// DataBox.  The wrapper struct `AlwaysReadyPostprocessor` is
      62             : /// provided for convenience to provide an `is_ready` function when a
      63             : /// pure mutate-apply is desired.
      64             : ///
      65             : /// At the end of the action, the values of the time, evolved
      66             : /// variables, and anything appearing in the `return_tags` of the \p
      67             : /// Postprocessors will be restored to their initial values.
      68             : ///
      69             : /// Uses:
      70             : /// - DataBox: EventsAndDenseTriggers and as required by events,
      71             : ///   triggers, and postprocessors
      72             : ///
      73             : /// DataBox changes:
      74             : /// - Adds: nothing
      75             : /// - Removes: nothing
      76             : /// - Modifies: as performed by the postprocessor `is_ready` functions
      77             : template <typename Postprocessors>
      78           1 : struct RunEventsAndDenseTriggers {
      79             :  private:
      80             :   static_assert(tt::is_a_v<tmpl::list, Postprocessors>);
      81             : 
      82             :   // RAII object to restore the time and variables changed by dense
      83             :   // output.
      84             :   template <typename DbTags, typename Tags>
      85           0 :   class StateRestorer {
      86             :     template <typename Tag, bool IsVariables>
      87           0 :     struct expand_variables_impl {
      88           0 :       using type = typename Tag::tags_list;
      89             :     };
      90             : 
      91             :     template <typename Tag>
      92           0 :     struct expand_variables_impl<Tag, false> {
      93           0 :       using type = tmpl::list<Tag>;
      94             :     };
      95             : 
      96             :     template <typename Tag>
      97           0 :     struct expand_variables
      98             :         : expand_variables_impl<Tag,
      99             :                                 tt::is_a_v<Variables, typename Tag::type>> {};
     100             : 
     101           0 :     using expanded_tags = tmpl::remove_duplicates<
     102             :         tmpl::join<tmpl::transform<Tags, expand_variables<tmpl::_1>>>>;
     103           0 :     using tensors_and_non_tensors = tmpl::partition<
     104             :         expanded_tags,
     105             :         tmpl::bind<
     106             :             tmpl::apply,
     107             :             tmpl::if_<tt::is_a<Tensor, tmpl::bind<tmpl::type_from, tmpl::_1>>,
     108             :                       tmpl::defer<tmpl::parent<std::is_same<
     109             :                           tmpl::bind<tmpl::type_from,
     110             :                                      tmpl::bind<tmpl::type_from, tmpl::_1>>,
     111             :                           DataVector>>>,
     112             :                       std::false_type>>>;
     113           0 :     using tensor_tags = tmpl::front<tensors_and_non_tensors>;
     114           0 :     using non_tensor_tags = tmpl::back<tensors_and_non_tensors>;
     115             : 
     116             :    public:
     117           0 :     StateRestorer(const gsl::not_null<db::DataBox<DbTags>*> box) : box_(box) {}
     118             : 
     119           0 :     void save() {
     120             :       // Only store the value the first time, because after that we
     121             :       // are seeing the value after the previous change instead of the
     122             :       // original.
     123             :       if (not non_tensors_.has_value()) {
     124             :         if constexpr (not std::is_same_v<tensor_tags, tmpl::list<>>) {
     125             :           tensors_.initialize(
     126             :               db::get<tmpl::front<tensor_tags>>(*box_).begin()->size());
     127             :           tmpl::for_each<tensor_tags>([this](auto tag_v) {
     128             :             using tag = tmpl::type_from<decltype(tag_v)>;
     129             :             get<tag>(tensors_) = db::get<tag>(*box_);
     130             :           });
     131             :         }
     132             :         tmpl::as_pack<non_tensor_tags>([this](auto... tags_v) {
     133             :           non_tensors_.emplace(
     134             :               db::get<tmpl::type_from<decltype(tags_v)>>(*box_)...);
     135             :         });
     136             :       }
     137             :     }
     138             : 
     139             :     // WARNING: Manually calling this if there are non_tensor_tags
     140             :     // will cause use-after-moves.
     141           0 :     void restore() {
     142             :       if (non_tensors_.has_value()) {
     143             :         tmpl::for_each<tensor_tags>([this](auto tag_v) {
     144             :           using tag = tmpl::type_from<decltype(tag_v)>;
     145             :           db::mutate<tag>(
     146             :               [this](const gsl::not_null<typename tag::type*> value) {
     147             :                 *value = get<tag>(tensors_);
     148             :               },
     149             :               box_);
     150             :         });
     151             :         tmpl::for_each<non_tensor_tags>([this](auto tag_v) {
     152             :           using tag = tmpl::type_from<decltype(tag_v)>;
     153             :           db::mutate<tag>(
     154             :               [this](const gsl::not_null<typename tag::type*> value) {
     155             :                 *value = std::move(tuples::get<tag>(*non_tensors_));
     156             :               },
     157             :               box_);
     158             :         });
     159             :       }
     160             :     }
     161             : 
     162           0 :     ~StateRestorer() { restore(); }
     163             : 
     164             :    private:
     165           0 :     gsl::not_null<db::DataBox<DbTags>*> box_ = nullptr;
     166             :     // Store all tensors in a single allocation.
     167           0 :     Variables<tensor_tags> tensors_{};
     168             :     std::optional<tuples::tagged_tuple_from_typelist<non_tensor_tags>>
     169           0 :         non_tensors_;
     170             :   };
     171             : 
     172             :   template <typename T>
     173           0 :   struct get_return_tags {
     174           0 :     using type = typename T::return_tags;
     175             :   };
     176             : 
     177             :  public:
     178             :   template <typename DbTags, typename... InboxTags, typename Metavariables,
     179             :             typename ArrayIndex, typename ActionList,
     180             :             typename ParallelComponent>
     181           0 :   static Parallel::iterable_action_return_t apply(
     182             :       db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     183             :       Parallel::GlobalCache<Metavariables>& cache,
     184             :       const ArrayIndex& array_index, const ActionList /*meta*/,
     185             :       const ParallelComponent* const component) {
     186             :     using system = typename Metavariables::system;
     187             :     using variables_tag = typename system::variables_tag;
     188             : 
     189             :     const auto& time_step_id = db::get<::Tags::TimeStepId>(box);
     190             :     if (time_step_id.slab_number() < 0) {
     191             :       // Skip dense output during self-start
     192             :       return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     193             :     }
     194             : 
     195             :     auto& events_and_dense_triggers =
     196             :         db::get_mutable_reference<::Tags::EventsAndDenseTriggers>(
     197             :             make_not_null(&box));
     198             : 
     199             :     const auto step_end =
     200             :         time_step_id.step_time() + db::get<::Tags::TimeStep>(box);
     201             :     const evolution_less_equal<double> before_equal{
     202             :         time_step_id.time_runs_forward()};
     203             : 
     204             :     using postprocessor_return_tags =
     205             :         tmpl::join<tmpl::transform<Postprocessors, get_return_tags<tmpl::_1>>>;
     206             :     // The evolved variables will be restored anyway, so no reason to
     207             :     // copy them twice.
     208             :     using postprocessor_restore_tags =
     209             :         tmpl::list_difference<postprocessor_return_tags,
     210             :                               typename variables_tag::tags_list>;
     211             : 
     212             :     StateRestorer<DbTags, tmpl::list<::Tags::Time>> time_restorer(
     213             :         make_not_null(&box));
     214             :     StateRestorer<DbTags, tmpl::list<variables_tag>> variables_restorer(
     215             :         make_not_null(&box));
     216             :     StateRestorer<DbTags, postprocessor_restore_tags> postprocessor_restorer(
     217             :         make_not_null(&box));
     218             : 
     219             :     for (;;) {
     220             :       const double next_trigger = events_and_dense_triggers.next_trigger(box);
     221             :       if (before_equal(step_end.value(), next_trigger)) {
     222             :         return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     223             :       }
     224             : 
     225             :       // Avoid invalidating compute items unless necessary.
     226             :       if (db::get<::Tags::Time>(box) != next_trigger) {
     227             :         time_restorer.save();
     228             :         db::mutate<::Tags::Time>(
     229             :             [&next_trigger](const gsl::not_null<double*> time) {
     230             :               *time = next_trigger;
     231             :             },
     232             :             make_not_null(&box));
     233             :       }
     234             : 
     235             :       const auto triggered = events_and_dense_triggers.is_ready(
     236             :           make_not_null(&box), cache, array_index, component);
     237             :       using TriggeringState = std::decay_t<decltype(triggered)>;
     238             :       switch (triggered) {
     239             :         case TriggeringState::NotReady:
     240             :           return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     241             :         case TriggeringState::NeedsEvolvedVariables: {
     242             :           using history_tag = ::Tags::HistoryEvolvedVariables<variables_tag>;
     243             :           bool dense_output_succeeded = false;
     244             :           variables_restorer.save();
     245             :           db::mutate<variables_tag>(
     246             :               [&dense_output_succeeded, &next_trigger](
     247             :                   gsl::not_null<typename variables_tag::type*> vars,
     248             :                   const TimeStepper& stepper,
     249             :                   const typename history_tag::type& history) {
     250             :                 *vars = *history.step_start(next_trigger).value;
     251             :                 dense_output_succeeded =
     252             :                     stepper.dense_update_u(vars, history, next_trigger);
     253             :               },
     254             :               make_not_null(&box),
     255             :               db::get<::Tags::TimeStepper<TimeStepper>>(box),
     256             :               db::get<history_tag>(box));
     257             :           if (not dense_output_succeeded) {
     258             :             // Need to take another time step
     259             :             return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     260             :           }
     261             : 
     262             :           bool ready = true;
     263             :           tmpl::for_each<Postprocessors>([&](auto postprocessor_v) {
     264             :             using postprocessor = tmpl::type_from<decltype(postprocessor_v)>;
     265             :             if (ready) {
     266             :               if (not postprocessor::is_ready(make_not_null(&box),
     267             :                                               make_not_null(&inboxes), cache,
     268             :                                               array_index, component)) {
     269             :                 ready = false;
     270             :               }
     271             :             }
     272             :           });
     273             :           if (not ready) {
     274             :             return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     275             :           }
     276             : 
     277             :           postprocessor_restorer.save();
     278             :           tmpl::for_each<Postprocessors>([&box](auto postprocessor_v) {
     279             :             using postprocessor = tmpl::type_from<decltype(postprocessor_v)>;
     280             :             db::mutate_apply<postprocessor>(make_not_null(&box));
     281             :           });
     282             :         }
     283             :           [[fallthrough]];
     284             :         default:
     285             :           break;
     286             :       }
     287             : 
     288             :       events_and_dense_triggers.run_events(box, cache, array_index, component);
     289             :       if (not events_and_dense_triggers.reschedule(make_not_null(&box), cache,
     290             :                                                    array_index, component)) {
     291             :         return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     292             :       }
     293             :     }
     294             :   }
     295             : };
     296             : 
     297           0 : struct InitializeRunEventsAndDenseTriggers {
     298           0 :   using simple_tags_from_options = tmpl::list<::Tags::EventsAndDenseTriggers>;
     299           0 :   using simple_tags = tmpl::list<::Tags::PreviousTriggerTime>;
     300             : 
     301             :   template <typename DbTags, typename... InboxTags, typename Metavariables,
     302             :             typename ArrayIndex, typename ActionList,
     303             :             typename ParallelComponent>
     304           0 :   static Parallel::iterable_action_return_t apply(
     305             :       db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     306             :       Parallel::GlobalCache<Metavariables>& /*cache*/,
     307             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     308             :       const ParallelComponent* const /*component*/) {
     309             :     ::Initialization::mutate_assign<simple_tags>(make_not_null(&box),
     310             :                                                std::nullopt);
     311             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     312             :   }
     313             : };
     314             : 
     315             : /// \brief Initialize/update items related to events and dense triggers after an
     316             : /// AMR change
     317             : ///
     318             : /// Mutates:
     319             : ///   - ::Tags::EventsAndDenseTriggers
     320             : ///   - Tags::PreviousTriggerTime
     321             : ///
     322             : /// For p-refinement:
     323             : ///   - Leaves both items unchanged
     324           1 : struct ProjectRunEventsAndDenseTriggers
     325             :     : tt::ConformsTo<amr::protocols::Projector> {
     326           0 :   using return_tags =
     327             :       tmpl::list<::Tags::EventsAndDenseTriggers, ::Tags::PreviousTriggerTime>;
     328           0 :   using argument_tags = tmpl::list<>;
     329             : 
     330             :   template <size_t Dim>
     331           0 :   static void apply(
     332             :       const gsl::not_null<
     333             :           EventsAndDenseTriggers*> /*events_and_dense_triggers*/,
     334             :       const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,
     335             :       const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {
     336             :     // do not need to update anything
     337             :   }
     338             : 
     339             :   template <typename... Tags>
     340           0 :   static void apply(
     341             :       const gsl::not_null<EventsAndDenseTriggers*> events_and_dense_triggers,
     342             :       const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,
     343             :       const tuples::TaggedTuple<Tags...>& parent_items) {
     344             :     *events_and_dense_triggers = deserialize<EventsAndDenseTriggers>(
     345             :         serialize(get<::Tags::EventsAndDenseTriggers>(parent_items)).data());
     346             :   }
     347             : 
     348             :   template <size_t Dim, typename... Tags>
     349           0 :   static void apply(
     350             :       const gsl::not_null<EventsAndDenseTriggers*> events_and_dense_triggers,
     351             :       const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,
     352             :       const std::unordered_map<ElementId<Dim>, tuples::TaggedTuple<Tags...>>&
     353             :           children_items) {
     354             :     // Serialization of equivalent Events and DenseTriggers is not
     355             :     // guaranteed to produce the same byte stream when there are
     356             :     // things like unordered containers involved, so we can't compare
     357             :     // with other children.
     358             :     *events_and_dense_triggers = deserialize<EventsAndDenseTriggers>(
     359             :         serialize(
     360             :             get<::Tags::EventsAndDenseTriggers>(children_items.begin()->second))
     361             :             .data());
     362             :   }
     363             : };
     364             : }  // namespace evolution::Actions
     365             : 
     366             : /// A wrapper adding an always-true `is_ready` function for a
     367             : /// `RunEventsAndDenseTriggers` postprocessor.  This allows structs
     368             : /// designed as mutate_apply arguments to be used without
     369             : /// modification.
     370             : template <typename T>
     371           1 : struct AlwaysReadyPostprocessor : T {
     372             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     373             :             typename ArrayIndex, typename ParallelComponent>
     374           0 :   static bool is_ready(
     375             :       const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,
     376             :       const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> /*inboxes*/,
     377             :       Parallel::GlobalCache<Metavariables>& /*cache*/,
     378             :       const ArrayIndex& /*array_index*/,
     379             :       const ParallelComponent* const /*component*/) {
     380             :     return true;
     381             :   }
     382             : };

Generated by: LCOV version 1.14