SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/EventsAndDenseTriggers - EventsAndDenseTriggers.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 11.8 % 51 6
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 <limits>
       8              : #include <memory>
       9              : #include <optional>
      10              : #include <vector>
      11              : 
      12              : #include "DataStructures/DataBox/DataBox.hpp"
      13              : #include "DataStructures/DataBox/TagName.hpp"
      14              : #include "Options/Options.hpp"
      15              : #include "Options/String.hpp"
      16              : #include "ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp"
      17              : #include "ParallelAlgorithms/EventsAndTriggers/Event.hpp"
      18              : #include "Time/EvolutionOrdering.hpp"
      19              : #include "Utilities/ErrorHandling/Assert.hpp"
      20              : #include "Utilities/ErrorHandling/Error.hpp"
      21              : #include "Utilities/Gsl.hpp"
      22              : 
      23              : /// \cond
      24              : namespace PUP {
      25              : class er;
      26              : }  // namespace PUP
      27              : namespace Parallel {
      28              : template <typename Metavariables>
      29              : class GlobalCache;
      30              : }  // namespace Parallel
      31              : namespace Tags {
      32              : struct Time;
      33              : struct TimeStepId;
      34              : }  // namespace Tags
      35              : /// \endcond
      36              : 
      37              : namespace Tags {
      38              : /*!
      39              :  * \ingroup EventsAndTriggersGroup
      40              :  * \brief Previous time at which the trigger activated.
      41              :  *
      42              :  * \details This tag is populated with the most recent time the current trigger
      43              :  * fired prior to the current activation.
      44              :  * \note This tag is only populated with a meaningful value for events invoked
      45              :  * from `EventsAndDenseTriggers`; during other actions, the tag should not be
      46              :  * used.
      47              :  */
      48            1 : struct PreviousTriggerTime : db::SimpleTag {
      49            0 :   using type = std::optional<double>;
      50              : };
      51              : }  // namespace Tags
      52              : 
      53              : /// \ingroup EventsAndTriggersGroup
      54              : /// Class that checks dense triggers and runs events
      55            1 : class EventsAndDenseTriggers {
      56              :  private:
      57              :   template <typename Event>
      58            0 :   struct get_tags {
      59            0 :     using type = typename Event::compute_tags_for_observation_box;
      60              :   };
      61              : 
      62              :  public:
      63            0 :   struct TriggerAndEvents {
      64            0 :     struct Trigger {
      65            0 :       using type = std::unique_ptr<::DenseTrigger>;
      66            0 :       static constexpr Options::String help = "Determines when the Events run.";
      67              :     };
      68            0 :     struct Events {
      69            0 :       using type = std::vector<std::unique_ptr<::Event>>;
      70            0 :       static constexpr Options::String help =
      71              :           "These events run when the Trigger fires.";
      72              :     };
      73            0 :     static constexpr Options::String help =
      74              :         "Events that run when the Trigger fires.";
      75            0 :     using options = tmpl::list<Trigger, Events>;
      76            0 :     TriggerAndEvents();
      77            0 :     TriggerAndEvents(std::unique_ptr<::DenseTrigger> trigger_in,
      78              :                      std::vector<std::unique_ptr<::Event>> events_in);
      79            0 :     void pup(PUP::er& p);
      80            0 :     std::unique_ptr<::DenseTrigger> trigger{};
      81            0 :     std::vector<std::unique_ptr<::Event>> events{};
      82              :   };
      83              : 
      84            0 :   using ConstructionType = std::vector<TriggerAndEvents>;
      85              : 
      86              :  private:
      87            0 :   struct TriggerRecord {
      88            0 :     TriggerRecord();
      89            0 :     TriggerRecord(double next_check_in, std::optional<bool> is_triggered_in,
      90              :                   size_t num_events_ready_in,
      91              :                   std::unique_ptr<DenseTrigger> trigger_in,
      92              :                   std::vector<std::unique_ptr<Event>> events_in);
      93            0 :     double next_check{};
      94            0 :     std::optional<bool> is_triggered{};
      95            0 :     size_t num_events_ready{};
      96            0 :     std::unique_ptr<DenseTrigger> trigger{};
      97            0 :     std::vector<std::unique_ptr<Event>> events{};
      98              : 
      99              :     // NOLINTNEXTLINE(google-runtime-references)
     100            0 :     void pup(PUP::er& p);
     101              :   };
     102            0 :   using Storage = std::vector<TriggerRecord>;
     103              : 
     104              :  public:
     105            0 :   EventsAndDenseTriggers() = default;
     106            0 :   explicit EventsAndDenseTriggers(ConstructionType events_and_triggers);
     107              : 
     108              :   template <typename DbTags>
     109            0 :   double next_trigger(const db::DataBox<DbTags>& box);
     110              : 
     111            0 :   enum class TriggeringState { Ready, NeedsEvolvedVariables, NotReady };
     112              : 
     113              :   /// Check triggers fire and whether all events to run are ready.  If
     114              :   /// this function returns anything other than NotReady, then
     115              :   /// rerunning it will skip checks (except for
     116              :   /// Event::needs_evolved_variables) until a successful call to
     117              :   /// reschedule.
     118              :   template <typename DbTags, typename Metavariables, typename ArrayIndex,
     119              :             typename ComponentPointer>
     120            1 :   TriggeringState is_ready(gsl::not_null<db::DataBox<DbTags>*> box,
     121              :                            Parallel::GlobalCache<Metavariables>& cache,
     122              :                            const ArrayIndex& array_index,
     123              :                            const ComponentPointer component);
     124              : 
     125              :   /// Run events associated with fired triggers.  This must be called
     126              :   /// after is_ready returns something other then NotReady.  Any
     127              :   /// repeated calls will be no-ops until a successful call to
     128              :   /// reschedule.
     129              :   template <typename DbTags, typename Metavariables, typename ArrayIndex,
     130              :             typename ComponentPointer>
     131            1 :   void run_events(db::DataBox<DbTags>& box,
     132              :                   Parallel::GlobalCache<Metavariables>& cache,
     133              :                   const ArrayIndex& array_index,
     134              :                   const ComponentPointer component);
     135              : 
     136              :   /// Schedule the next check.  This must be called after run_events
     137              :   /// for the current check.  Returns `true` on success, `false` if
     138              :   /// insufficient data is available and the call should be retried
     139              :   /// later.
     140              :   template <typename DbTags, typename Metavariables, typename ArrayIndex,
     141              :             typename ComponentPointer>
     142            1 :   bool reschedule(gsl::not_null<db::DataBox<DbTags>*> box,
     143              :                   Parallel::GlobalCache<Metavariables>& cache,
     144              :                   const ArrayIndex& array_index,
     145              :                   const ComponentPointer component);
     146              : 
     147              :   /// Add a new trigger and set of events.  This can only be called
     148              :   /// during initialization.
     149            1 :   void add_trigger_and_events(std::unique_ptr<DenseTrigger> trigger,
     150              :                               std::vector<std::unique_ptr<Event>> events);
     151              : 
     152              :   template <typename F>
     153            0 :   void for_each_event(F&& f) const;
     154              : 
     155              :   // NOLINTNEXTLINE(google-runtime-references)
     156            0 :   void pup(PUP::er& p);
     157              : 
     158              :  private:
     159              :   template <typename DbTags>
     160            0 :   void initialize(const db::DataBox<DbTags>& box);
     161              : 
     162            0 :   bool initialized() const;
     163              : 
     164            0 :   Storage events_and_triggers_;
     165            0 :   double next_check_ = std::numeric_limits<double>::signaling_NaN();
     166            0 :   evolution_less<double> before_{};
     167              : };
     168              : 
     169              : template <typename DbTags>
     170              : double EventsAndDenseTriggers::next_trigger(const db::DataBox<DbTags>& box) {
     171              :   if (UNLIKELY(not initialized())) {
     172              :     initialize(box);
     173              :   }
     174              : 
     175              :   if (events_and_triggers_.empty()) {
     176              :     return before_.infinity();
     177              :   }
     178              : 
     179              :   return next_check_;
     180              : }
     181              : 
     182              : template <typename DbTags, typename Metavariables, typename ArrayIndex,
     183              :           typename ComponentPointer>
     184            0 : EventsAndDenseTriggers::TriggeringState EventsAndDenseTriggers::is_ready(
     185              :     const gsl::not_null<db::DataBox<DbTags>*> box,
     186              :     Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
     187              :     const ComponentPointer component) {
     188              :   ASSERT(initialized(), "Not initialized");
     189              :   ASSERT(not events_and_triggers_.empty(),
     190              :          "Should not be calling is_ready with no triggers");
     191              : 
     192              :   for (auto& trigger_entry : events_and_triggers_) {
     193              :     if (trigger_entry.next_check != next_check_) {
     194              :       continue;
     195              :     }
     196              :     if (not trigger_entry.is_triggered.has_value()) {
     197              :       const auto is_triggered = trigger_entry.trigger->is_triggered(
     198              :           box, cache, array_index, component);
     199              :       if (not is_triggered.has_value()) {
     200              :         return TriggeringState::NotReady;
     201              :       }
     202              : 
     203              :       trigger_entry.is_triggered = *is_triggered;
     204              :     }
     205              : 
     206              :     if (not *trigger_entry.is_triggered) {
     207              :       continue;
     208              :     }
     209              : 
     210              :     for (; trigger_entry.num_events_ready < trigger_entry.events.size();
     211              :          ++trigger_entry.num_events_ready) {
     212              :       if (not trigger_entry.events[trigger_entry.num_events_ready]->is_ready(
     213              :               *box, cache, array_index, component)) {
     214              :         return TriggeringState::NotReady;
     215              :       }
     216              :     }
     217              :   }
     218              : 
     219              :   for (auto& trigger_entry : events_and_triggers_) {
     220              :     if (trigger_entry.is_triggered != std::optional{true}) {
     221              :       continue;
     222              :     }
     223              :     for (const auto& event : trigger_entry.events) {
     224              :       if (event->needs_evolved_variables()) {
     225              :         return TriggeringState::NeedsEvolvedVariables;
     226              :       }
     227              :     }
     228              :   }
     229              : 
     230              :   return TriggeringState::Ready;
     231              : }
     232              : 
     233              : template <typename DbTags, typename Metavariables, typename ArrayIndex,
     234              :           typename ComponentPointer>
     235              : void EventsAndDenseTriggers::run_events(
     236              :     db::DataBox<DbTags>& box, Parallel::GlobalCache<Metavariables>& cache,
     237              :     const ArrayIndex& array_index, const ComponentPointer component) {
     238              :   ASSERT(initialized(), "Not initialized");
     239              :   ASSERT(not events_and_triggers_.empty(),
     240              :          "Should not be calling run_events with no triggers");
     241              :   using compute_tags = tmpl::remove_duplicates<tmpl::filter<
     242              :       tmpl::flatten<tmpl::transform<
     243              :           tmpl::at<typename Metavariables::factory_creation::factory_classes,
     244              :                    Event>,
     245              :           get_tags<tmpl::_1>>>,
     246              :       db::is_compute_tag<tmpl::_1>>>;
     247              :   const Event::ObservationValue observation_value{db::tag_name<::Tags::Time>(),
     248              :                                                   db::get<::Tags::Time>(box)};
     249              :   auto observation_box =
     250              :       make_observation_box<compute_tags>(make_not_null(&box));
     251              : 
     252              :   for (auto& trigger_entry : events_and_triggers_) {
     253              :     if (trigger_entry.is_triggered == std::optional{true}) {
     254              :       db::mutate<::Tags::PreviousTriggerTime>(
     255              :           [&trigger_entry](const gsl::not_null<std::optional<double>*>
     256              :                                previous_trigger_time) {
     257              :             *previous_trigger_time =
     258              :                 trigger_entry.trigger->previous_trigger_time();
     259              :           },
     260              :           make_not_null(&box));
     261              :       for (const auto& event : trigger_entry.events) {
     262              :         event->run(make_not_null(&observation_box), cache, array_index,
     263              :                    component, observation_value);
     264              :       }
     265              :       db::mutate<::Tags::PreviousTriggerTime>(
     266              :           [](const gsl::not_null<std::optional<double>*>
     267              :                  previous_trigger_time) {
     268              :             *previous_trigger_time =
     269              :                 std::numeric_limits<double>::signaling_NaN();
     270              :           },
     271              :           make_not_null(&box));
     272              :     }
     273              :     // Mark this trigger as handled so we will not reprocess it if
     274              :     // this method or is_ready is called again.
     275              :     trigger_entry.is_triggered = false;
     276              :   }
     277              : }
     278              : 
     279              : template <typename DbTags, typename Metavariables, typename ArrayIndex,
     280              :           typename ComponentPointer>
     281            0 : bool EventsAndDenseTriggers::reschedule(
     282              :     const gsl::not_null<db::DataBox<DbTags>*> box,
     283              :     Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
     284              :     const ComponentPointer component) {
     285              :   ASSERT(initialized(), "Not initialized");
     286              :   ASSERT(not events_and_triggers_.empty(),
     287              :          "Should not be calling run_events with no triggers");
     288              : 
     289              :   double new_next_check = before_.infinity();
     290              :   for (auto& trigger_entry : events_and_triggers_) {
     291              :     if (trigger_entry.next_check == next_check_) {
     292              :       const std::optional<double> next_check =
     293              :           trigger_entry.trigger->next_check_time(box, cache, array_index,
     294              :                                                  component);
     295              :       if (not next_check.has_value()) {
     296              :         return false;
     297              :       }
     298              :       trigger_entry.next_check = *next_check;
     299              :       trigger_entry.num_events_ready = 0;
     300              :     }
     301              :     if (before_(trigger_entry.next_check, new_next_check)) {
     302              :       new_next_check = trigger_entry.next_check;
     303              :     }
     304              :     trigger_entry.is_triggered.reset();
     305              :   }
     306              : 
     307              :   next_check_ = new_next_check;
     308              :   return true;
     309              : }
     310              : 
     311              : template <typename F>
     312              : void EventsAndDenseTriggers::for_each_event(F&& f) const {
     313              :   for (const auto& trigger_and_events : events_and_triggers_) {
     314              :     for (const auto& event : trigger_and_events.events) {
     315              :       f(*event);
     316              :     }
     317              :   }
     318              : }
     319              : 
     320              : template <typename DbTags>
     321              : void EventsAndDenseTriggers::initialize(const db::DataBox<DbTags>& box) {
     322              :   before_ = evolution_less<double>{
     323              :       db::get<::Tags::TimeStepId>(box).time_runs_forward()};
     324              :   next_check_ = db::get<::Tags::Time>(box);
     325              :   for (auto& trigger_record : events_and_triggers_) {
     326              :     trigger_record.next_check = next_check_;
     327              :   }
     328              : }
     329              : 
     330              : template <>
     331            0 : struct Options::create_from_yaml<EventsAndDenseTriggers> {
     332            0 :   using type = EventsAndDenseTriggers;
     333              :   template <typename Metavariables>
     334            0 :   static type create(const Options::Option& options) {
     335              :     return type(
     336              :         options.parse_as<typename type::ConstructionType, Metavariables>());
     337              :   }
     338              : };
        

Generated by: LCOV version 2.0-1