SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/EventsAndDenseTriggers - EventsAndDenseTriggers.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 6 51 11.8 %
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 <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 1.14