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

Generated by: LCOV version 2.0-1