SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/EventsAndDenseTriggers - EventsAndDenseTriggers.hpp Hit Total Coverage
Commit: 965048f86d23c819715b3af1ca3f880c8145d4bb Lines: 6 47 12.8 %
Date: 2024-05-16 17:00:40
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 :     void pup(PUP::er& p) {
      77             :       p | trigger;
      78             :       p | events;
      79             :     }
      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 :     double next_check;
      89           0 :     std::optional<bool> is_triggered;
      90           0 :     size_t num_events_ready;
      91           0 :     std::unique_ptr<DenseTrigger> trigger;
      92           0 :     std::vector<std::unique_ptr<Event>> events;
      93             : 
      94             :     // NOLINTNEXTLINE(google-runtime-references)
      95           0 :     void pup(PUP::er& p);
      96             :   };
      97           0 :   using Storage = std::vector<TriggerRecord>;
      98             : 
      99             :  public:
     100           0 :   EventsAndDenseTriggers() = default;
     101           0 :   explicit EventsAndDenseTriggers(ConstructionType events_and_triggers);
     102             : 
     103             :   template <typename DbTags>
     104           0 :   double next_trigger(const db::DataBox<DbTags>& box);
     105             : 
     106           0 :   enum class TriggeringState { Ready, NeedsEvolvedVariables, NotReady };
     107             : 
     108             :   /// Check triggers fire and whether all events to run are ready.  If
     109             :   /// this function returns anything other than NotReady, then
     110             :   /// rerunning it will skip checks (except for
     111             :   /// Event::needs_evolved_variables) until a successful call to
     112             :   /// reschedule.
     113             :   template <typename DbTags, typename Metavariables, typename ArrayIndex,
     114             :             typename ComponentPointer>
     115           1 :   TriggeringState is_ready(gsl::not_null<db::DataBox<DbTags>*> box,
     116             :                            Parallel::GlobalCache<Metavariables>& cache,
     117             :                            const ArrayIndex& array_index,
     118             :                            const ComponentPointer component);
     119             : 
     120             :   /// Run events associated with fired triggers.  This must be called
     121             :   /// after is_ready returns something other then NotReady.  Any
     122             :   /// repeated calls will be no-ops until a successful call to
     123             :   /// reschedule.
     124             :   template <typename DbTags, typename Metavariables, typename ArrayIndex,
     125             :             typename ComponentPointer>
     126           1 :   void run_events(db::DataBox<DbTags>& box,
     127             :                   Parallel::GlobalCache<Metavariables>& cache,
     128             :                   const ArrayIndex& array_index,
     129             :                   const ComponentPointer component);
     130             : 
     131             :   /// Schedule the next check.  This must be called after run_events
     132             :   /// for the current check.  Returns `true` on success, `false` if
     133             :   /// insufficient data is available and the call should be retried
     134             :   /// later.
     135             :   template <typename DbTags, typename Metavariables, typename ArrayIndex,
     136             :             typename ComponentPointer>
     137           1 :   bool reschedule(gsl::not_null<db::DataBox<DbTags>*> box,
     138             :                   Parallel::GlobalCache<Metavariables>& cache,
     139             :                   const ArrayIndex& array_index,
     140             :                   const ComponentPointer component);
     141             : 
     142             :   /// Add a new trigger and set of events.  This can only be called
     143             :   /// during initialization.
     144           1 :   void add_trigger_and_events(std::unique_ptr<DenseTrigger> trigger,
     145             :                               std::vector<std::unique_ptr<Event>> events);
     146             : 
     147             :   template <typename F>
     148           0 :   void for_each_event(F&& f) const;
     149             : 
     150             :   // NOLINTNEXTLINE(google-runtime-references)
     151           0 :   void pup(PUP::er& p);
     152             : 
     153             :  private:
     154             :   template <typename DbTags>
     155           0 :   void initialize(const db::DataBox<DbTags>& box);
     156             : 
     157           0 :   bool initialized() const;
     158             : 
     159           0 :   Storage events_and_triggers_;
     160           0 :   double next_check_ = std::numeric_limits<double>::signaling_NaN();
     161           0 :   evolution_less<double> before_{};
     162             : };
     163             : 
     164             : template <typename DbTags>
     165             : double EventsAndDenseTriggers::next_trigger(const db::DataBox<DbTags>& box) {
     166             :   if (UNLIKELY(not initialized())) {
     167             :     initialize(box);
     168             :   }
     169             : 
     170             :   if (events_and_triggers_.empty()) {
     171             :     return before_.infinity();
     172             :   }
     173             : 
     174             :   return next_check_;
     175             : }
     176             : 
     177             : template <typename DbTags, typename Metavariables, typename ArrayIndex,
     178             :           typename ComponentPointer>
     179           0 : EventsAndDenseTriggers::TriggeringState EventsAndDenseTriggers::is_ready(
     180             :     const gsl::not_null<db::DataBox<DbTags>*> box,
     181             :     Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
     182             :     const ComponentPointer component) {
     183             :   ASSERT(initialized(), "Not initialized");
     184             :   ASSERT(not events_and_triggers_.empty(),
     185             :          "Should not be calling is_ready with no triggers");
     186             : 
     187             :   for (auto& trigger_entry : events_and_triggers_) {
     188             :     if (trigger_entry.next_check != next_check_) {
     189             :       continue;
     190             :     }
     191             :     if (not trigger_entry.is_triggered.has_value()) {
     192             :       const auto is_triggered = trigger_entry.trigger->is_triggered(
     193             :           box, cache, array_index, component);
     194             :       if (not is_triggered.has_value()) {
     195             :         return TriggeringState::NotReady;
     196             :       }
     197             : 
     198             :       trigger_entry.is_triggered = *is_triggered;
     199             :     }
     200             : 
     201             :     if (not *trigger_entry.is_triggered) {
     202             :       continue;
     203             :     }
     204             : 
     205             :     for (; trigger_entry.num_events_ready < trigger_entry.events.size();
     206             :          ++trigger_entry.num_events_ready) {
     207             :       if (not trigger_entry.events[trigger_entry.num_events_ready]->is_ready(
     208             :               *box, cache, array_index, component)) {
     209             :         return TriggeringState::NotReady;
     210             :       }
     211             :     }
     212             :   }
     213             : 
     214             :   for (auto& trigger_entry : events_and_triggers_) {
     215             :     if (trigger_entry.is_triggered != std::optional{true}) {
     216             :       continue;
     217             :     }
     218             :     for (const auto& event : trigger_entry.events) {
     219             :       if (event->needs_evolved_variables()) {
     220             :         return TriggeringState::NeedsEvolvedVariables;
     221             :       }
     222             :     }
     223             :   }
     224             : 
     225             :   return TriggeringState::Ready;
     226             : }
     227             : 
     228             : template <typename DbTags, typename Metavariables, typename ArrayIndex,
     229             :           typename ComponentPointer>
     230             : void EventsAndDenseTriggers::run_events(
     231             :     db::DataBox<DbTags>& box, Parallel::GlobalCache<Metavariables>& cache,
     232             :     const ArrayIndex& array_index, const ComponentPointer component) {
     233             :   ASSERT(initialized(), "Not initialized");
     234             :   ASSERT(not events_and_triggers_.empty(),
     235             :          "Should not be calling run_events with no triggers");
     236             :   using compute_tags = tmpl::remove_duplicates<tmpl::filter<
     237             :       tmpl::flatten<tmpl::transform<
     238             :           tmpl::at<typename Metavariables::factory_creation::factory_classes,
     239             :                    Event>,
     240             :           get_tags<tmpl::_1>>>,
     241             :       db::is_compute_tag<tmpl::_1>>>;
     242             :   const Event::ObservationValue observation_value{db::tag_name<::Tags::Time>(),
     243             :                                                   db::get<::Tags::Time>(box)};
     244             :   auto observation_box =
     245             :       make_observation_box<compute_tags>(make_not_null(&box));
     246             : 
     247             :   for (auto& trigger_entry : events_and_triggers_) {
     248             :     if (trigger_entry.is_triggered == std::optional{true}) {
     249             :       db::mutate<::Tags::PreviousTriggerTime>(
     250             :           [&trigger_entry](const gsl::not_null<std::optional<double>*>
     251             :                                previous_trigger_time) {
     252             :             *previous_trigger_time =
     253             :                 trigger_entry.trigger->previous_trigger_time();
     254             :           },
     255             :           make_not_null(&box));
     256             :       for (const auto& event : trigger_entry.events) {
     257             :         event->run(make_not_null(&observation_box), cache, array_index,
     258             :                    component, observation_value);
     259             :       }
     260             :       db::mutate<::Tags::PreviousTriggerTime>(
     261             :           [](const gsl::not_null<std::optional<double>*>
     262             :                  previous_trigger_time) {
     263             :             *previous_trigger_time =
     264             :                 std::numeric_limits<double>::signaling_NaN();
     265             :           },
     266             :           make_not_null(&box));
     267             :     }
     268             :     // Mark this trigger as handled so we will not reprocess it if
     269             :     // this method or is_ready is called again.
     270             :     trigger_entry.is_triggered = false;
     271             :   }
     272             : }
     273             : 
     274             : template <typename DbTags, typename Metavariables, typename ArrayIndex,
     275             :           typename ComponentPointer>
     276           0 : bool EventsAndDenseTriggers::reschedule(
     277             :     const gsl::not_null<db::DataBox<DbTags>*> box,
     278             :     Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
     279             :     const ComponentPointer component) {
     280             :   ASSERT(initialized(), "Not initialized");
     281             :   ASSERT(not events_and_triggers_.empty(),
     282             :          "Should not be calling run_events with no triggers");
     283             : 
     284             :   double new_next_check = before_.infinity();
     285             :   for (auto& trigger_entry : events_and_triggers_) {
     286             :     if (trigger_entry.next_check == next_check_) {
     287             :       const std::optional<double> next_check =
     288             :           trigger_entry.trigger->next_check_time(box, cache, array_index,
     289             :                                                  component);
     290             :       if (not next_check.has_value()) {
     291             :         return false;
     292             :       }
     293             :       trigger_entry.next_check = *next_check;
     294             :       trigger_entry.num_events_ready = 0;
     295             :     }
     296             :     if (before_(trigger_entry.next_check, new_next_check)) {
     297             :       new_next_check = trigger_entry.next_check;
     298             :     }
     299             :     trigger_entry.is_triggered.reset();
     300             :   }
     301             : 
     302             :   next_check_ = new_next_check;
     303             :   return true;
     304             : }
     305             : 
     306             : template <typename F>
     307             : void EventsAndDenseTriggers::for_each_event(F&& f) const {
     308             :   for (const auto& trigger_and_events : events_and_triggers_) {
     309             :     for (const auto& event : trigger_and_events.events) {
     310             :       f(*event);
     311             :     }
     312             :   }
     313             : }
     314             : 
     315             : template <typename DbTags>
     316             : void EventsAndDenseTriggers::initialize(const db::DataBox<DbTags>& box) {
     317             :   before_ = evolution_less<double>{
     318             :       db::get<::Tags::TimeStepId>(box).time_runs_forward()};
     319             :   next_check_ = db::get<::Tags::Time>(box);
     320             :   for (auto& trigger_record : events_and_triggers_) {
     321             :     trigger_record.next_check = next_check_;
     322             :   }
     323             : }
     324             : 
     325             : template <>
     326           0 : struct Options::create_from_yaml<EventsAndDenseTriggers> {
     327           0 :   using type = EventsAndDenseTriggers;
     328             :   template <typename Metavariables>
     329           0 :   static type create(const Options::Option& options) {
     330             :     return type(
     331             :         options.parse_as<typename type::ConstructionType, Metavariables>());
     332             :   }
     333             : };

Generated by: LCOV version 1.14