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 : };
|