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