EventsAndDenseTriggers.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <limits>
7 #include <memory>
8 #include <unordered_map>
9 #include <vector>
10 
12 #include "Evolution/EventsAndDenseTriggers/DenseTrigger.hpp"
13 #include "Options/Options.hpp"
14 #include "ParallelAlgorithms/EventsAndTriggers/Event.hpp"
15 #include "Time/EvolutionOrdering.hpp"
18 
19 /// \cond
20 namespace PUP {
21 class er;
22 } // namespace PUP
23 namespace Parallel {
24 template <typename Metavariables>
25 class GlobalCache;
26 } // namespace Parallel
27 namespace Tags {
28 struct Time;
29 struct TimeStepId;
30 } // namespace Tags
31 /// \endcond
32 
33 namespace evolution {
34 /// \ingroup EventsAndTriggersGroup
35 /// Class that checks dense triggers and runs events
37  public:
38  using ConstructionType =
41 
42  private:
43  struct TriggerRecord {
44  double next_check;
47 
48  // NOLINTNEXTLINE(google-runtime-references)
49  void pup(PUP::er& p) noexcept;
50  };
52 
53  public:
54  EventsAndDenseTriggers() = default;
55  explicit EventsAndDenseTriggers(
56  ConstructionType events_and_triggers) noexcept;
57 
58  template <typename DbTags>
59  double next_trigger(const db::DataBox<DbTags>& box) noexcept;
60 
61  enum class TriggeringState { Ready, NeedsEvolvedVariables, NotReady };
62 
63  template <typename DbTags, typename Metavariables, typename ArrayIndex,
64  typename ComponentPointer>
65  TriggeringState is_ready(const db::DataBox<DbTags>& box,
67  const ArrayIndex& array_index,
68  const ComponentPointer component) noexcept;
69 
70  template <typename DbTags, typename Metavariables, typename ArrayIndex,
71  typename ComponentPointer>
72  void run_events(const db::DataBox<DbTags>& box,
74  const ArrayIndex& array_index,
75  const ComponentPointer component) noexcept;
76 
77  template <typename F>
78  void for_each_event(F&& f) const noexcept;
79 
80  // NOLINTNEXTLINE(google-runtime-references)
81  void pup(PUP::er& p) noexcept;
82 
83  private:
84  typename Storage::iterator heap_end() noexcept {
85  return events_and_triggers_.begin() + heap_size_;
86  }
87 
88  typename Storage::iterator current_trigger() noexcept {
89  return events_and_triggers_.begin() + processing_position_;
90  }
91 
92  template <typename DbTags>
93  void initialize(const db::DataBox<DbTags>& box) noexcept;
94 
95  void populate_active_triggers() noexcept;
96 
97  void finish_processing_trigger_at_current_time(
98  const typename Storage::iterator& index) noexcept;
99 
100  class TriggerTimeAfter {
101  public:
102  explicit TriggerTimeAfter(const bool time_runs_forward);
103 
104  bool operator()(const TriggerRecord& a,
105  const TriggerRecord& b) const noexcept;
106 
107  double infinite_future() const noexcept;
108 
109  // NOLINTNEXTLINE(google-runtime-references)
110  void pup(PUP::er& p) noexcept;
111 
112  private:
113  evolution_greater<double> time_after_{};
114  };
115 
116  // The data structure used here is a heap containing the triggers
117  // not being tested at the moment, with the earliest time as the
118  // root at index 0, followed by the triggers known to trigger at the
119  // current time, followed by the triggers currently being processed.
120  // When we are done with the current time, we choose the next time
121  // to process by examining the heap root and then pop all triggers
122  // requesting that time into the processing area. Triggers are
123  // moved back to the heap either when we know they don't trigger or
124  // when we have run their events.
125  //
126  // Except at initialization (when everything needs to be processed
127  // at the start time), the trigger processing area is expected to be
128  // small.
129  Storage events_and_triggers_;
130  // The size of the heap, or equivalently the index of the start of
131  // the processing area. The initial -1 is a sentinel for an
132  // uninitialized state.
133  typename Storage::difference_type heap_size_ = -1;
134  // Index of the next trigger to process.
135  typename Storage::difference_type processing_position_{};
136  double next_check_ = std::numeric_limits<double>::signaling_NaN();
137  TriggerTimeAfter next_check_after_{false};
138 };
139 
140 template <typename DbTags>
141 double EventsAndDenseTriggers::next_trigger(
142  const db::DataBox<DbTags>& box) noexcept {
143  if (UNLIKELY(heap_size_ == -1)) {
144  initialize(box);
145  }
146 
147  if (events_and_triggers_.empty()) {
148  return next_check_after_.infinite_future();
149  }
150 
151  return next_check_;
152 }
153 
154 template <typename DbTags, typename Metavariables, typename ArrayIndex,
155  typename ComponentPointer>
156 EventsAndDenseTriggers::TriggeringState EventsAndDenseTriggers::is_ready(
157  const db::DataBox<DbTags>& box, Parallel::GlobalCache<Metavariables>& cache,
158  const ArrayIndex& array_index, const ComponentPointer component) noexcept {
159  ASSERT(heap_size_ != -1, "Not initialized");
160  ASSERT(not events_and_triggers_.empty(),
161  "Should not be calling is_ready with no triggers");
162  ASSERT(heap_end() != events_and_triggers_.end(), "No active triggers");
163 
164  const evolution_greater<double> after{
165  db::get<::Tags::TimeStepId>(box).time_runs_forward()};
166 
167  while (current_trigger() != events_and_triggers_.end()) {
168  if (not current_trigger()->trigger->is_ready(box)) {
169  return TriggeringState::NotReady;
170  }
171 
172  const auto is_triggered = current_trigger()->trigger->is_triggered(box);
173  if (not after(is_triggered.next_check, current_trigger()->next_check)) {
174  ERROR("Trigger at time " << current_trigger()->next_check
175  << " rescheduled itself for earlier time "
176  << is_triggered.next_check);
177  }
178  current_trigger()->next_check = is_triggered.next_check;
179  if (not is_triggered.is_triggered) {
180  finish_processing_trigger_at_current_time(current_trigger());
181  }
182  ++processing_position_;
183  }
184 
185  // The triggers we are processing are not in the heap, but are
186  // stored after it in the data array.
187  for (auto trigger = heap_end();
188  trigger != events_and_triggers_.end();
189  ++trigger) {
190  for (const auto& event : trigger->events) {
191  if (not event->is_ready(box, cache, array_index, component)) {
192  return TriggeringState::NotReady;
193  }
194  }
195  }
196 
197  for (auto trigger = heap_end();
198  trigger != events_and_triggers_.end();
199  ++trigger) {
200  for (const auto& event : trigger->events) {
201  if (event->needs_evolved_variables()) {
202  return TriggeringState::NeedsEvolvedVariables;
203  }
204  }
205  }
206 
207  return TriggeringState::Ready;
208 }
209 
210 template <typename DbTags, typename Metavariables, typename ArrayIndex,
211  typename ComponentPointer>
212 void EventsAndDenseTriggers::run_events(
213  const db::DataBox<DbTags>& box, Parallel::GlobalCache<Metavariables>& cache,
214  const ArrayIndex& array_index, const ComponentPointer component) noexcept {
215  ASSERT(heap_size_ != -1, "Not initialized");
216  ASSERT(not events_and_triggers_.empty(),
217  "Should not be calling run_events with no triggers");
218 
219  for (auto trigger = heap_end();
220  trigger != events_and_triggers_.end();
221  ++trigger) {
222  for (const auto& event : trigger->events) {
223  event->run(box, cache, array_index, component);
224  }
225  finish_processing_trigger_at_current_time(trigger);
226  }
227 
228  populate_active_triggers();
229 }
230 
231 template <typename F>
232 void EventsAndDenseTriggers::for_each_event(F&& f) const noexcept {
233  for (const auto& trigger_and_events : events_and_triggers_) {
234  for (const auto& event : trigger_and_events.events) {
235  f(*event);
236  }
237  }
238 }
239 
240 template <typename DbTags>
241 void EventsAndDenseTriggers::initialize(
242  const db::DataBox<DbTags>& box) noexcept {
243  next_check_after_ =
244  TriggerTimeAfter{db::get<::Tags::TimeStepId>(box).time_runs_forward()};
245  next_check_ = db::get<::Tags::Time>(box);
246  for (auto& trigger_record : events_and_triggers_) {
247  trigger_record.next_check = next_check_;
248  }
249  heap_size_ = 0;
250 }
251 } // namespace evolution
252 
253 template <>
254 struct Options::create_from_yaml<evolution::EventsAndDenseTriggers> {
256  template <typename Metavariables>
257  static type create(const Options::Option& options) {
258  return type(options.parse_as<typename type::ConstructionType,
259  Metavariables>());
260  }
261 };
evolution::EventsAndDenseTriggers
Definition: EventsAndDenseTriggers.hpp:36
UNLIKELY
#define UNLIKELY(x)
Definition: Gsl.hpp:73
Parallel::GlobalCache
Definition: ElementReceiveInterpPoints.hpp:15
evolution
Functionality for evolving hyperbolic partial differential equations.
Definition: RunEventsAndDenseTriggers.hpp:30
Options.hpp
vector
Error.hpp
evolution_comparator
Definition: EvolutionOrdering.hpp:20
Options::Option
Definition: Options.hpp:108
ERROR
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:37
DataBox.hpp
Assert.hpp
Options::create_from_yaml
Definition: MinmodType.hpp:11
std::numeric_limits::signaling_NaN
T signaling_NaN(T... args)
memory
TimeStepId
Definition: TimeStepId.hpp:25
ASSERT
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:49
ActionTesting::cache
Parallel::GlobalCache< Metavariables > & cache(MockRuntimeSystem< Metavariables > &runner, const ArrayIndex &array_index) noexcept
Returns the GlobalCache of Component with index array_index.
Definition: MockRuntimeSystemFreeFunctions.hpp:382
limits
Time
Definition: Time.hpp:29
std::unique_ptr< DenseTrigger >
unordered_map
Parallel
Functionality for parallelization.
Definition: ElementReceiveInterpPoints.hpp:13