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