Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <limits>
7 : #include <optional>
8 : #include <pup.h>
9 : #include <type_traits>
10 :
11 : #include "DataStructures/DataBox/DataBox.hpp"
12 : #include "DataStructures/DataBox/MetavariablesTag.hpp"
13 : #include "Utilities/CallWithDynamicType.hpp"
14 : #include "Utilities/Gsl.hpp"
15 : #include "Utilities/Serialization/CharmPupable.hpp"
16 : #include "Utilities/Serialization/PupStlCpp17.hpp"
17 :
18 : /// \cond
19 : namespace Parallel {
20 : template <typename Metavariables>
21 : class GlobalCache;
22 : } // namespace Parallel
23 : namespace Tags {
24 : struct Time;
25 : } // namespace Tags
26 : /// \endcond
27 :
28 : /*!
29 : * \ingroup EventsAndTriggersGroup
30 : * \brief Contains denser triggers.
31 : */
32 1 : namespace DenseTriggers {}
33 :
34 : /// \ingroup EventsAndTriggersGroup
35 : /// Base class for checking whether to run an Event at arbitrary times.
36 : ///
37 : /// The DataBox passed to the member functions will have
38 : /// `::Tags::Time`, and therefore any compute tags depending on that
39 : /// value, set to the time to be tested. Any discrete properties of
40 : /// steps or slabs, such as the step size, may have the values from
41 : /// times off by one step. The evolved variables will be in an
42 : /// unspecified state.
43 1 : class DenseTrigger : public PUP::able {
44 : protected:
45 : /// \cond
46 : DenseTrigger() = default;
47 : DenseTrigger(const DenseTrigger&) = default;
48 : DenseTrigger(DenseTrigger&&) = default;
49 : DenseTrigger& operator=(const DenseTrigger&) = default;
50 : DenseTrigger& operator=(DenseTrigger&&) = default;
51 : /// \endcond
52 :
53 : public:
54 0 : ~DenseTrigger() override = default;
55 :
56 : /// \cond
57 : explicit DenseTrigger(CkMigrateMessage* const msg) : PUP::able(msg) {}
58 : WRAPPED_PUPable_abstract(DenseTrigger); // NOLINT
59 : /// \endcond
60 :
61 : /// Check whether the trigger fires. Returns std::nullopt if
62 : /// insufficient data is available to make the decision. The
63 : /// trigger is not responsible for checking whether dense output of
64 : /// the evolved variables is possible, but may need to check things
65 : /// such as the availability of FunctionOfTime data.
66 : template <typename DbTags, typename Metavariables, typename ArrayIndex,
67 : typename Component>
68 1 : std::optional<bool> is_triggered(
69 : const gsl::not_null<db::DataBox<DbTags>*> box,
70 : Parallel::GlobalCache<Metavariables>& cache,
71 : const ArrayIndex& array_index, const Component* const component) {
72 : using factory_classes =
73 : typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(
74 : *box))>::factory_creation::factory_classes;
75 : previous_trigger_time_ = next_previous_trigger_time_;
76 : return call_with_dynamic_type<std::optional<bool>,
77 : tmpl::at<factory_classes, DenseTrigger>>(
78 : this,
79 : [this, &array_index, &box, &cache, &component](auto* const trigger) {
80 : using TriggerType = std::decay_t<decltype(*trigger)>;
81 : const auto result = db::mutate_apply<
82 : typename TriggerType::is_triggered_return_tags,
83 : typename TriggerType::is_triggered_argument_tags>(
84 : [&array_index, &cache, &component,
85 : &trigger](const auto&... args) {
86 : return trigger->is_triggered(cache, array_index, component,
87 : args...);
88 : },
89 : box);
90 : if (result == std::optional{true}) {
91 : next_previous_trigger_time_ = db::get<::Tags::Time>(*box);
92 : }
93 : return result;
94 : });
95 : }
96 :
97 : /// Obtain the next time to check the trigger, or std::nullopt if the
98 : /// trigger is not ready to report yet.
99 : template <typename DbTags, typename Metavariables, typename ArrayIndex,
100 : typename Component>
101 1 : std::optional<double> next_check_time(
102 : const gsl::not_null<db::DataBox<DbTags>*> box,
103 : Parallel::GlobalCache<Metavariables>& cache,
104 : const ArrayIndex& array_index, const Component* component) {
105 : using factory_classes =
106 : typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(
107 : *box))>::factory_creation::factory_classes;
108 : return call_with_dynamic_type<std::optional<double>,
109 : tmpl::at<factory_classes, DenseTrigger>>(
110 : this, [&array_index, &box, &cache, &component](auto* const trigger) {
111 : using TriggerType = std::decay_t<decltype(*trigger)>;
112 : return db::mutate_apply<
113 : typename TriggerType::next_check_time_return_tags,
114 : typename TriggerType::next_check_time_argument_tags>(
115 : [&array_index, &cache, &component,
116 : &trigger](const auto&... args) {
117 : return trigger->next_check_time(cache, array_index, component,
118 : args...);
119 : },
120 : box);
121 : });
122 : }
123 :
124 : /// \brief Reports the value of `::Tags::Time` when the trigger most recently
125 : /// fired, except for the most recent call of `is_triggered`.
126 : ///
127 : /// \details The most recent call of `is_triggered` is not used for reporting
128 : /// the previous trigger so that the time reported to the event is actually
129 : /// the previous time value on which the trigger fired and activated the
130 : /// event. Without ignoring the most recent call of `is_triggered`, we'd just
131 : /// always be reporting the current time to the event, because events always
132 : /// run after their associated triggers fire via a call to `is_triggered`.
133 1 : std::optional<double> previous_trigger_time() const {
134 : return previous_trigger_time_;
135 : }
136 :
137 0 : void pup(PUP::er& p) override {
138 : p | next_previous_trigger_time_;
139 : p | previous_trigger_time_;
140 : }
141 :
142 : private:
143 0 : std::optional<double> next_previous_trigger_time_ = std::nullopt;
144 0 : std::optional<double> previous_trigger_time_ = std::nullopt;
145 : };
|