Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <optional>
7 :
8 : #include "DataStructures/DataBox/DataBox.hpp"
9 : #include "DataStructures/DataBox/MetavariablesTag.hpp"
10 : #include "IO/Observer/Actions/ObserverRegistration.hpp"
11 : #include "IO/Observer/ObserverComponent.hpp"
12 : #include "IO/Observer/Tags.hpp"
13 : #include "IO/Observer/TypeOfObservation.hpp"
14 : #include "Parallel/AlgorithmExecution.hpp"
15 : #include "Parallel/ArrayComponentId.hpp"
16 : #include "Parallel/GlobalCache.hpp"
17 : #include "Parallel/Invoke.hpp"
18 : #include "Parallel/Local.hpp"
19 : #include "Parallel/Protocols/ElementRegistrar.hpp"
20 : #include "Parallel/TypeTraits.hpp"
21 : #include "ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp"
22 : #include "ParallelAlgorithms/EventsAndTriggers/Tags.hpp"
23 : #include "ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp"
24 : #include "Utilities/ProtocolHelpers.hpp"
25 : #include "Utilities/TMPL.hpp"
26 : #include "Utilities/TaggedTuple.hpp"
27 : #include "Utilities/TypeTraits/CreateHasTypeAlias.hpp"
28 :
29 : namespace observers {
30 : namespace detail {
31 : CREATE_HAS_TYPE_ALIAS(observation_registration_tags)
32 : CREATE_HAS_TYPE_ALIAS_V(observation_registration_tags)
33 : } // namespace detail
34 :
35 : /*!
36 : * \brief Retrieves the observation type and key from an event, if it is an
37 : * event that needs to register with the observers.
38 : *
39 : * The event must define an `observation_registration_tags` type alias that is a
40 : * `tmpl::list<>` of all the tags from the DataBox needed to construct the
41 : * `ObservationKey`, and a `get_observation_type_and_key_for_registration`
42 : * member function if it is to be registered automatically.
43 : */
44 : template <typename DbTagsList>
45 : std::optional<
46 : std::pair<::observers::TypeOfObservation, ::observers::ObservationKey>>
47 1 : get_registration_observation_type_and_key(const Event& event,
48 : const db::DataBox<DbTagsList>& box) {
49 : std::optional<
50 : std::pair<::observers::TypeOfObservation, ::observers::ObservationKey>>
51 : result{};
52 : bool already_registered = false;
53 : using factory_classes =
54 : typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(
55 : box))>::factory_creation::factory_classes;
56 : tmpl::for_each<tmpl::at<factory_classes, Event>>(
57 : [&already_registered, &box, &event, &result](auto event_type_v) {
58 : using EventType = typename decltype(event_type_v)::type;
59 : if constexpr (detail::has_observation_registration_tags_v<EventType>) {
60 : // We require that each event for which
61 : // `has_observation_registration_tags_v` is true be downcastable to
62 : // only *one* event type. This is checked by the `already_registered`
63 : // bool and the error message below. We need to downcast because we do
64 : // not want to impose that every event be registerable with the
65 : // IO/observer components. `dynamic_cast` returns a `nullptr` in the
66 : // case that the downcast failed, which we check for to see if we can
67 : // call the `get_observation_type_and_key_for_registration` function.
68 : const auto* const derived_class_ptr =
69 : dynamic_cast<const EventType*>(&event);
70 : if (derived_class_ptr != nullptr) {
71 : if (already_registered) {
72 : ERROR(
73 : "Already registered the event by casting down to a "
74 : "different Event derived class. This means you have an "
75 : "Event where A inherits from B and both A and B define "
76 : "get_observation_type_and_id_for_registration. This "
77 : "behavior is not supported. Please make a separate Event.");
78 : }
79 : already_registered = true;
80 : result =
81 : db::apply<typename EventType::observation_registration_tags>(
82 : [&derived_class_ptr](const auto&... args) {
83 : return derived_class_ptr
84 : ->get_observation_type_and_key_for_registration(
85 : args...);
86 : },
87 : box);
88 : }
89 : }
90 : });
91 : return result;
92 : }
93 :
94 : namespace Actions {
95 : /*!
96 : * \brief Registers this element of a parallel component with the local
97 : * `Observer` parallel component for each triggered observation.
98 : *
99 : * \details This tells the `Observer` to expect data from this component, as
100 : * well as whether each observation is a Reduction or Volume observation.
101 : * Should be added to the phase dependent action list of the components that
102 : * contribute data for volume and reduction observations.
103 : *
104 : * When this struct is used as an action, the `apply` function will perform the
105 : * registration with observers. However, this struct also offers the static
106 : * member functions `perform_registration` and `perform_deregistration` that
107 : * are needed for either registering when an element is added to a core outside
108 : * of initialization or deregistering when an element is being eliminated from a
109 : * core. The use of separate functions is necessary to provide an interface
110 : * usable outside of iterable actions, e.g. in specialized `pup` functions.
111 : */
112 1 : struct RegisterEventsWithObservers
113 : : tt::ConformsTo<Parallel::protocols::ElementRegistrar> {
114 : private:
115 : template <typename ParallelComponent, typename RegisterOrDeregisterAction,
116 : typename DbTagList, typename Metavariables, typename ArrayIndex>
117 0 : static void register_or_deregister_impl(
118 : const db::DataBox<DbTagList>& box,
119 : Parallel::GlobalCache<Metavariables>& cache,
120 : const ArrayIndex& array_index) {
121 : std::vector<
122 : std::pair<observers::TypeOfObservation, observers::ObservationKey>>
123 : type_of_observation_and_observation_key_pairs;
124 : const auto collect_observations =
125 : [&box,
126 : &type_of_observation_and_observation_key_pairs](const auto& event) {
127 : if (auto obs_type_and_obs_key =
128 : get_registration_observation_type_and_key(event, box);
129 : obs_type_and_obs_key.has_value()) {
130 : type_of_observation_and_observation_key_pairs.push_back(
131 : *obs_type_and_obs_key);
132 : }
133 : };
134 :
135 : if constexpr (db::tag_is_retrievable_v<
136 : ::Tags::EventsAndTriggers<
137 : Triggers::WhenToCheck::AtIterations>,
138 : db::DataBox<DbTagList>>) {
139 : const auto& triggers_and_events = db::get<
140 : ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtIterations>>(box);
141 : triggers_and_events.for_each_event(collect_observations);
142 : }
143 :
144 : if constexpr (db::tag_is_retrievable_v<
145 : ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,
146 : db::DataBox<DbTagList>>) {
147 : const auto& triggers_and_events =
148 : db::get<::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(
149 : box);
150 : triggers_and_events.for_each_event(collect_observations);
151 : }
152 :
153 : if constexpr (db::tag_is_retrievable_v<
154 : ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>,
155 : db::DataBox<DbTagList>>) {
156 : const auto& triggers_and_events =
157 : db::get<::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>>(
158 : box);
159 : triggers_and_events.for_each_event(collect_observations);
160 : }
161 :
162 : if constexpr (db::tag_is_retrievable_v<
163 : ::Tags::EventsAndTriggers<
164 : Triggers::WhenToCheck::AtCheckpoints>,
165 : db::DataBox<DbTagList>>) {
166 : const auto& triggers_and_events = db::get<
167 : ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtCheckpoints>>(box);
168 : triggers_and_events.for_each_event(collect_observations);
169 : }
170 :
171 : if constexpr (db::tag_is_retrievable_v<::Tags::EventsAndDenseTriggers,
172 : db::DataBox<DbTagList>>) {
173 : const auto& triggers_and_events =
174 : db::get<::Tags::EventsAndDenseTriggers>(box);
175 : triggers_and_events.for_each_event(collect_observations);
176 : }
177 :
178 : if constexpr (db::tag_is_retrievable_v<::Tags::EventsRunAtCleanup,
179 : db::DataBox<DbTagList>>) {
180 : for (const auto& event : db::get<::Tags::EventsRunAtCleanup>(box)) {
181 : collect_observations(*event);
182 : }
183 : }
184 :
185 : if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {
186 : using volume_register_action =
187 : tmpl::conditional_t<std::is_same_v<RegisterOrDeregisterAction,
188 : RegisterContributorWithObserver>,
189 : RegisterVolumeContributorWithObserverWriter,
190 : DeregisterVolumeContributorWithObserverWriter>;
191 : using reduction_register_action =
192 : tmpl::conditional_t<std::is_same_v<RegisterOrDeregisterAction,
193 : RegisterContributorWithObserver>,
194 : RegisterReductionContributorWithObserverWriter,
195 : DeregisterReductionContributorWithObserverWriter>;
196 : auto* observer_writer = Parallel::local_branch(
197 : Parallel::get_parallel_component<
198 : observers::ObserverWriter<Metavariables>>(cache));
199 : ASSERT(observer_writer != nullptr,
200 : "The observer writer component should be valid. This is an "
201 : "internal bug.");
202 : for (const auto& [type_of_observation, observation_key] :
203 : type_of_observation_and_observation_key_pairs) {
204 : switch (type_of_observation) {
205 : case TypeOfObservation::Reduction:
206 : Parallel::simple_action<reduction_register_action>(
207 : *observer_writer, observation_key,
208 : Parallel::ArrayComponentId{
209 : std::add_pointer_t<ParallelComponent>{nullptr},
210 : Parallel::ArrayIndex<ArrayIndex>(array_index)});
211 : break;
212 : case TypeOfObservation::Volume:
213 : Parallel::simple_action<volume_register_action>(
214 : *observer_writer, observation_key,
215 : Parallel::ArrayComponentId{
216 : std::add_pointer_t<ParallelComponent>{nullptr},
217 : Parallel::ArrayIndex<ArrayIndex>(array_index)});
218 : break;
219 : default:
220 : ERROR(
221 : "Attempting to deregister an unknown TypeOfObservation. "
222 : "Should be one of 'Reduction' or 'Volume'");
223 : };
224 : }
225 : } else {
226 : static_assert(Parallel::is_array_v<ParallelComponent>);
227 :
228 : for (const auto& [type_of_observation, observation_key] :
229 : type_of_observation_and_observation_key_pairs) {
230 : auto& observer =
231 : *Parallel::local_branch(Parallel::get_parallel_component<
232 : observers::Observer<Metavariables>>(cache));
233 : Parallel::simple_action<RegisterOrDeregisterAction>(
234 : observer, observation_key,
235 : Parallel::ArrayComponentId(
236 : std::add_pointer_t<ParallelComponent>{nullptr},
237 : Parallel::ArrayIndex<std::decay_t<ArrayIndex>>{array_index}),
238 : type_of_observation);
239 : }
240 : }
241 : }
242 :
243 : public: // ElementRegistrar protocol
244 : template <typename ParallelComponent, typename DbTagList,
245 : typename Metavariables, typename ArrayIndex>
246 0 : static void perform_registration(const db::DataBox<DbTagList>& box,
247 : Parallel::GlobalCache<Metavariables>& cache,
248 : const ArrayIndex& array_index) {
249 : register_or_deregister_impl<ParallelComponent,
250 : RegisterContributorWithObserver>(box, cache,
251 : array_index);
252 : }
253 :
254 : template <typename ParallelComponent, typename DbTagList,
255 : typename Metavariables, typename ArrayIndex>
256 0 : static void perform_deregistration(
257 : const db::DataBox<DbTagList>& box,
258 : Parallel::GlobalCache<Metavariables>& cache,
259 : const ArrayIndex& array_index) {
260 : register_or_deregister_impl<ParallelComponent,
261 : DeregisterContributorWithObserver>(box, cache,
262 : array_index);
263 : }
264 :
265 : public: // Iterable action
266 : template <typename DbTagList, typename... InboxTags, typename Metavariables,
267 : typename ArrayIndex, typename ActionList,
268 : typename ParallelComponent>
269 0 : static Parallel::iterable_action_return_t apply(
270 : db::DataBox<DbTagList>& box,
271 : const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
272 : Parallel::GlobalCache<Metavariables>& cache,
273 : const ArrayIndex& array_index, const ActionList /*meta*/,
274 : const ParallelComponent* const /*meta*/) {
275 : perform_registration<ParallelComponent>(box, cache, array_index);
276 : return {Parallel::AlgorithmExecution::Continue, std::nullopt};
277 : }
278 : };
279 : } // namespace Actions
280 : } // namespace observers
|