Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <array>
7 : #include <cstddef>
8 : #include <memory>
9 : #include <optional>
10 : #include <string>
11 : #include <tuple>
12 : #include <unordered_map>
13 : #include <unordered_set>
14 : #include <utility>
15 :
16 : #include "DataStructures/DataBox/DataBox.hpp"
17 : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
18 : #include "Parallel/AlgorithmExecution.hpp"
19 : #include "Parallel/ArrayCollection/IsDgElementCollection.hpp"
20 : #include "Parallel/ArrayCollection/PerformAlgorithmOnElement.hpp"
21 : #include "Parallel/ArrayCollection/Tags/ElementLocations.hpp"
22 : #include "Parallel/ArrayComponentId.hpp"
23 : #include "Parallel/Callback.hpp"
24 : #include "Parallel/GlobalCache.hpp"
25 : #include "Parallel/Info.hpp"
26 : #include "Parallel/ParallelComponentHelpers.hpp"
27 : #include "ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp"
28 : #include "Utilities/Algorithm.hpp"
29 : #include "Utilities/ErrorHandling/Assert.hpp"
30 : #include "Utilities/StdHelpers.hpp"
31 :
32 : /// \cond
33 : namespace Tags {
34 : struct Time;
35 : } // namespace Tags
36 : namespace domain::Tags {
37 : struct FunctionsOfTime;
38 : } // namespace domain::Tags
39 : namespace tuples {
40 : template <class... Tags>
41 : class TaggedTuple;
42 : } // namespace tuples
43 : /// \endcond
44 :
45 : namespace domain {
46 : namespace detail {
47 : template <typename CacheTag, typename Callback, typename Metavariables,
48 : typename ArrayIndex, typename Component, typename... Args>
49 : bool functions_of_time_are_ready_impl(
50 : Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
51 : const Component* /*meta*/, const double time,
52 : const std::optional<std::unordered_set<std::string>>& functions_to_check,
53 : Args&&... args) {
54 : if constexpr (Parallel::is_in_mutable_global_cache<Metavariables, CacheTag>) {
55 : const auto& proxy =
56 : ::Parallel::get_parallel_component<Component>(cache)[array_index];
57 : const Parallel::ArrayComponentId array_component_id =
58 : [&]() -> Parallel::ArrayComponentId {
59 : if constexpr (Parallel::is_dg_element_collection_v<Component>) {
60 : static_assert(
61 : sizeof...(args) == 1,
62 : "This currently assumes the only argument is the ElementId. If you "
63 : "need extra arguments, this can be generalized. We need the "
64 : "ElementId instead of the array_index (which is the nodegroup ID) "
65 : "since each ArrayComponentId is only allowed to register one "
66 : "callback, additional ones are ignored. This means of a "
67 : "DgElementCollection only 1 callback _per node_ would be "
68 : "registered, while we need 1 callback for each element. An "
69 : "alternative approach would be to have the callback be a broadcast "
70 : "to all elements instead of one specific one.");
71 : static_assert((std::is_same_v<ElementId<Metavariables::volume_dim>,
72 : std::decay_t<Args>> and
73 : ...));
74 : return Parallel::make_array_component_id<Component>(args...);
75 : } else {
76 : return Parallel::make_array_component_id<Component>(array_index);
77 : }
78 : }();
79 :
80 : return Parallel::mutable_cache_item_is_ready<CacheTag>(
81 : cache, array_component_id,
82 : [&functions_to_check, &proxy, &time,
83 : &args...](const std::unordered_map<
84 : std::string,
85 : std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&
86 : functions_of_time) {
87 : using ::operator<<;
88 : ASSERT(
89 : alg::all_of(
90 : functions_to_check.value_or(
91 : std::unordered_set<std::string>{}),
92 : [&functions_of_time](const std::string& function_to_check) {
93 : return functions_of_time.count(function_to_check) == 1;
94 : }),
95 : "Not all functions to check ("
96 : << functions_to_check.value() << ") are in the global cache ("
97 : << keys_of(functions_of_time) << ")");
98 : for (const auto& [name, f_of_t] : functions_of_time) {
99 : if (functions_to_check.has_value() and
100 : functions_to_check->count(name) == 0) {
101 : continue;
102 : }
103 : const double expiration_time = f_of_t->time_bounds()[1];
104 : if (time > expiration_time) {
105 : return std::unique_ptr<Parallel::Callback>(
106 : new Callback(proxy, std::forward<Args>(args)...));
107 : }
108 : }
109 : return std::unique_ptr<Parallel::Callback>{};
110 : });
111 : } else {
112 : (void)cache;
113 : (void)array_index;
114 : (void)time;
115 : (void)functions_to_check;
116 : EXPAND_PACK_LEFT_TO_RIGHT((void)args);
117 : return true;
118 : }
119 : }
120 : } // namespace detail
121 :
122 : /// \ingroup ComputationalDomainGroup
123 : /// Check that functions of time are up-to-date.
124 : ///
125 : /// Check that functions of time in \p CacheTag with names in \p
126 : /// functions_to_check are ready at time \p time. If \p functions_to_check is
127 : /// a `std::nullopt`, checks all functions in \p CacheTag. If any function is
128 : /// not ready, schedules a `Parallel::SimpleActionCallback` with the GlobalCache
129 : /// which calls the simple action passed in as a template parameter. The `Args`
130 : /// are forwareded to the callback.
131 : template <typename CacheTag, typename SimpleAction, typename Metavariables,
132 : typename ArrayIndex, typename Component, typename... Args>
133 1 : bool functions_of_time_are_ready_simple_action_callback(
134 : Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
135 : const Component* component_p, const double time,
136 : const std::optional<std::unordered_set<std::string>>& functions_to_check,
137 : Args&&... args) {
138 : using ProxyType =
139 : std::decay_t<decltype(::Parallel::get_parallel_component<Component>(
140 : cache)[array_index])>;
141 : return detail::functions_of_time_are_ready_impl<
142 : CacheTag,
143 : Parallel::SimpleActionCallback<SimpleAction, ProxyType, Args...>>(
144 : cache, array_index, component_p, time, functions_to_check,
145 : std::forward<Args>(args)...);
146 : }
147 :
148 : /// \ingroup ComputationalDomainGroup
149 : /// Check that functions of time are up-to-date.
150 : ///
151 : /// Check that functions of time in \p CacheTag with names in \p
152 : /// functions_to_check are ready at time \p time. If \p functions_to_check is
153 : /// a `std::nullopt`, checks all functions in \p CacheTag. If any function is
154 : /// not ready, schedules a `Parallel::ThreadedActionCallback` with the
155 : /// GlobalCache which calls the threaded action passed in as a template
156 : /// parameter. The `Args` are forwareded to the callback.
157 : template <typename CacheTag, typename ThreadedAction, typename Metavariables,
158 : typename ArrayIndex, typename Component, typename... Args>
159 1 : bool functions_of_time_are_ready_threaded_action_callback(
160 : Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
161 : const Component* component_p, const double time,
162 : const std::optional<std::unordered_set<std::string>>& functions_to_check,
163 : Args&&... args) {
164 : using ProxyType =
165 : std::decay_t<decltype(::Parallel::get_parallel_component<Component>(
166 : cache)[array_index])>;
167 : return detail::functions_of_time_are_ready_impl<
168 : CacheTag,
169 : Parallel::ThreadedActionCallback<ThreadedAction, ProxyType, Args...>>(
170 : cache, array_index, component_p, time, functions_to_check,
171 : std::forward<Args>(args)...);
172 : }
173 :
174 : /// \ingroup ComputationalDomainGroup
175 : /// Check that functions of time are up-to-date.
176 : ///
177 : /// Check that functions of time in \p CacheTag with names in \p
178 : /// functions_to_check are ready at time \p time. If \p functions_to_check is
179 : /// a `std::nullopt`, checks all functions in \p CacheTag. If any function is
180 : /// not ready, schedules a `Parallel::PerformAlgorithmCallback` or
181 : /// `Parallel::Actions::PerformAlgorithmOnElement<false>` callback with the
182 : /// GlobalCache.
183 : template <typename CacheTag, size_t Dim, typename Metavariables,
184 : typename ArrayIndex, typename Component>
185 1 : bool functions_of_time_are_ready_algorithm_callback(
186 : Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,
187 : const Component* component_p, const double time,
188 : const std::optional<std::unordered_set<std::string>>& functions_to_check =
189 : std::nullopt) {
190 : if constexpr (Parallel::is_dg_element_collection_v<Component>) {
191 : const auto element_location =
192 : static_cast<int>(Parallel::local_synchronous_action<
193 : Parallel::Actions::GetItemFromDistributedOject<
194 : Parallel::Tags::ElementLocations<Dim>>>(
195 : Parallel::get_parallel_component<Component>(cache))
196 : ->at(array_index));
197 : ASSERT(element_location == Parallel::my_node<int>(cache),
198 : "Expected to be running on node "
199 : << Parallel::my_node<int>(cache)
200 : << " but the record says it is on node " << element_location);
201 : return functions_of_time_are_ready_threaded_action_callback<
202 : domain::Tags::FunctionsOfTime,
203 : Parallel::Actions::PerformAlgorithmOnElement<false>>(
204 : cache, element_location, component_p, time, std::nullopt, array_index);
205 : } else {
206 : using ProxyType =
207 : std::decay_t<decltype(::Parallel::get_parallel_component<Component>(
208 : cache)[array_index])>;
209 : return detail::functions_of_time_are_ready_impl<
210 : CacheTag, Parallel::PerformAlgorithmCallback<ProxyType>>(
211 : cache, array_index, component_p, time, functions_to_check);
212 : }
213 : }
214 :
215 0 : namespace Actions {
216 : /// \ingroup ComputationalDomainGroup
217 : /// Check that functions of time are up-to-date.
218 : ///
219 : /// Wait for all functions of time in `domain::Tags::FunctionsOfTime`
220 : /// to be ready at `::Tags::Time`. This ensures that the coordinates
221 : /// can be safely accessed in later actions without first verifying
222 : /// the state of the time-dependent maps.
223 : template <size_t Dim>
224 1 : struct CheckFunctionsOfTimeAreReady {
225 : template <typename DbTags, typename... InboxTags, typename Metavariables,
226 : typename ArrayIndex, typename ActionList,
227 : typename ParallelComponent>
228 0 : static Parallel::iterable_action_return_t apply(
229 : db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
230 : Parallel::GlobalCache<Metavariables>& cache,
231 : const ArrayIndex& array_index, ActionList /*meta*/,
232 : const ParallelComponent* component) {
233 : const bool ready = functions_of_time_are_ready_algorithm_callback<
234 : domain::Tags::FunctionsOfTime, Dim>(cache, array_index, component,
235 : db::get<::Tags::Time>(box));
236 :
237 : return {ready ? Parallel::AlgorithmExecution::Continue
238 : : Parallel::AlgorithmExecution::Retry,
239 : std::nullopt};
240 : }
241 : };
242 : } // namespace Actions
243 :
244 : /// \ingroup ComputationalDomainGroup
245 : /// Dense-output postprocessor to check that functions of time are up-to-date.
246 : ///
247 : /// Check that all functions of time in
248 : /// `domain::Tags::FunctionsOfTime` are ready at `::Tags::Time`. This
249 : /// ensures that the coordinates can be safely accessed in later
250 : /// actions without first verifying the state of the time-dependent
251 : /// maps. This postprocessor does not actually modify anything.
252 : template <size_t Dim>
253 1 : struct CheckFunctionsOfTimeAreReadyPostprocessor {
254 0 : using return_tags = tmpl::list<>;
255 0 : using argument_tags = tmpl::list<>;
256 0 : static void apply() {}
257 :
258 : template <typename DbTagsList, typename... InboxTags, typename Metavariables,
259 : typename ArrayIndex, typename ParallelComponent>
260 0 : static bool is_ready(
261 : const gsl::not_null<db::DataBox<DbTagsList>*> box,
262 : const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> /*inboxes*/,
263 : Parallel::GlobalCache<Metavariables>& cache,
264 : const ArrayIndex& array_index, const ParallelComponent* component) {
265 : return functions_of_time_are_ready_algorithm_callback<
266 : domain::Tags::FunctionsOfTime, Dim>(cache, array_index, component,
267 : db::get<::Tags::Time>(*box));
268 : }
269 : };
270 : } // namespace domain
|