VerifyTemporalIdsAndSendPoints.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
7 #include "NumericalAlgorithms/Interpolation/InterpolationTargetDetail.hpp"
8 #include "NumericalAlgorithms/Interpolation/Tags.hpp"
10 #include "Parallel/Invoke.hpp"
11 #include "Utilities/Gsl.hpp"
12 
13 namespace intrp::Actions {
14 
15 template <typename InterpolationTargetTag>
17 
18 namespace detail {
19 template <typename InterpolationTargetTag, typename ParallelComponent,
20  typename DbTags, typename Metavariables>
21 void verify_temporal_ids_and_send_points_time_independent(
22  const gsl::not_null<db::DataBox<DbTags>*> box,
24  using TemporalId = typename Metavariables::temporal_id::type;
25 
26  // Move all PendingTemporalIds to TemporalIds, provided
27  // that they are not already there, and fill new_temporal_ids
28  // with the temporal_ids that were so moved.
29  std::vector<TemporalId> new_temporal_ids{};
30  db::mutate_apply<tmpl::list<Tags::TemporalIds<TemporalId>,
32  tmpl::list<Tags::CompletedTemporalIds<TemporalId>>>(
33  [&new_temporal_ids](
35  const gsl::not_null<std::deque<TemporalId>*> pending_ids,
36  const std::deque<TemporalId>& completed_ids) noexcept {
37  for (auto& id : *pending_ids) {
38  if (std::find(completed_ids.begin(), completed_ids.end(), id) ==
39  completed_ids.end() and
40  std::find(ids->begin(), ids->end(), id) == ids->end()) {
41  ids->push_back(id);
42  new_temporal_ids.push_back(id);
43  }
44  }
45  pending_ids->clear();
46  },
47  box);
48  if (InterpolationTargetTag::compute_target_points::is_sequential::value) {
49  // Sequential: start interpolation only for the first new_temporal_id.
50  if (not new_temporal_ids.empty()) {
51  auto& my_proxy =
52  Parallel::get_parallel_component<ParallelComponent>(cache);
54  Actions::SendPointsToInterpolator<InterpolationTargetTag>>(
55  my_proxy, new_temporal_ids.front());
56  }
57  } else {
58  // Non-sequential: start interpolation for all new_temporal_ids.
59  auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);
60  for (const auto& id : new_temporal_ids) {
62  Actions::SendPointsToInterpolator<InterpolationTargetTag>>(my_proxy,
63  id);
64  }
65  }
66 }
67 
68 template <typename InterpolationTargetTag, typename ParallelComponent,
69  typename DbTags, typename Metavariables>
70 void verify_temporal_ids_and_send_points_time_dependent(
71  const gsl::not_null<db::DataBox<DbTags>*> box,
73  using TemporalId = typename Metavariables::temporal_id::type;
74 
75  const auto& pending_temporal_ids =
76  db::get<Tags::PendingTemporalIds<TemporalId>>(*box);
77  if (pending_temporal_ids.empty()) {
78  return; // Nothing to do if there are no pending temporal_ids.
79  }
80 
81  auto& this_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);
82  double min_expiration_time = std::numeric_limits<double>::max();
83  const bool at_least_one_pending_temporal_id_is_ready =
84  ::Parallel::mutable_cache_item_is_ready<domain::Tags::FunctionsOfTime>(
85  cache,
86  [&this_proxy, &pending_temporal_ids, &min_expiration_time](
87  const std::unordered_map<
90  functions_of_time) -> std::unique_ptr<Parallel::Callback> {
91  min_expiration_time =
92  std::min_element(functions_of_time.begin(),
93  functions_of_time.end(),
94  [](const auto& a, const auto& b) {
95  return a.second->time_bounds()[1] <
96  b.second->time_bounds()[1];
97  })
98  ->second->time_bounds()[1];
99  for (const auto& pending_id : pending_temporal_ids) {
100  if (pending_id.step_time().value() <= min_expiration_time) {
101  // Success: at least one pending_temporal_id is ok.
103  }
104  }
105  // Failure: none of the pending_temporal_ids are ok.
108  VerifyTemporalIdsAndSendPoints<InterpolationTargetTag>,
109  decltype(this_proxy)>(this_proxy));
110  });
111 
112  if (not at_least_one_pending_temporal_id_is_ready) {
113  // A callback has been set so that VerifyTemporalIdsAndSendPoints will
114  // be called by MutableGlobalCache when domain::Tags::FunctionsOfTime
115  // is updated. So we can exit now.
116  return;
117  }
118 
119  // Move up-to-date PendingTemporalIds to TemporalIds, provided
120  // that they are not already there, and fill new_temporal_ids
121  // with the temporal_ids that were so moved.
122  std::vector<TemporalId> new_temporal_ids{};
123  db::mutate_apply<tmpl::list<Tags::TemporalIds<TemporalId>,
124  Tags::PendingTemporalIds<TemporalId>>,
125  tmpl::list<Tags::CompletedTemporalIds<TemporalId>>>(
126  [&min_expiration_time, &new_temporal_ids](
128  const gsl::not_null<std::deque<TemporalId>*> pending_ids,
129  const std::deque<TemporalId>& completed_ids) noexcept {
130  for (auto it = pending_ids->begin(); it != pending_ids->end();) {
131  if (it->step_time().value() <= min_expiration_time and
132  std::find(completed_ids.begin(), completed_ids.end(), *it) ==
133  completed_ids.end() and
134  std::find(ids->begin(), ids->end(), *it) == ids->end()) {
135  ids->push_back(*it);
136  new_temporal_ids.push_back(*it);
137  it = pending_ids->erase(it);
138  } else {
139  ++it;
140  }
141  }
142  },
143  box);
144 
145  if (InterpolationTargetTag::compute_target_points::is_sequential::value) {
146  // Sequential: start interpolation only for the first new_temporal_id.
147  if (not new_temporal_ids.empty()) {
148  auto& my_proxy =
149  Parallel::get_parallel_component<ParallelComponent>(cache);
151  Actions::SendPointsToInterpolator<InterpolationTargetTag>>(
152  my_proxy, new_temporal_ids.front());
153  }
154  } else {
155  // Non-sequential: start interpolation for all new_temporal_ids.
156  auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);
157  for (const auto& id : new_temporal_ids) {
159  Actions::SendPointsToInterpolator<InterpolationTargetTag>>(my_proxy,
160  id);
161  }
162  // If there are still pending temporal_ids, call
163  // VerifyTemporalIdsAndSendPoints again, so that those pending
164  // temporal_ids can be waited for.
165  if (not db::get<Tags::PendingTemporalIds<TemporalId>>(*box).empty()) {
167  VerifyTemporalIdsAndSendPoints<InterpolationTargetTag>>(my_proxy);
168  }
169  }
170 }
171 } // namespace detail
172 
173 /// \ingroup ActionsGroup
174 /// \brief Sends points to an Interpolator for verified temporal_ids.
175 ///
176 /// VerifyTemporalIdsAndSendPoints is invoked on an InterpolationTarget.
177 ///
178 /// In more detail, does the following:
179 /// - If any map is time-dependent:
180 /// - Moves verified PendingTemporalIds to TemporalIds, where
181 /// verified means that the FunctionsOfTime in the GlobalCache
182 /// are up-to-date for that TemporalId. If no PendingTemporalIds are
183 /// moved, then VerifyTemporalIdsAndSendPoints sets itself as a
184 /// callback in the GlobalCache so that it is called again when the
185 /// FunctionsOfTime are mutated.
186 /// - If the InterpolationTarget is sequential, invokes
187 /// intrp::Actions::SendPointsToInterpolator for the first TemporalId.
188 /// (when interpolation is complete,
189 /// intrp::Actions::InterpolationTargetReceiveVars will begin interpolation
190 /// on the next TemporalId)
191 /// - If the InterpolationTarget is not sequential, invokes
192 /// intrp::Actions::SendPointsToInterpolator for all valid TemporalIds,
193 /// and then if PendingTemporalIds is non-empty it invokes itself.
194 ///
195 /// - If all maps are time-independent:
196 /// - Moves all PendingTemporalIds to TemporalIds
197 /// - If the InterpolationTarget is sequential, invokes
198 /// intrp::Actions::SendPointsToInterpolator for the first TemporalId.
199 /// (when interpolation is complete,
200 /// intrp::Actions::InterpolationTargetReceiveVars will begin interpolation
201 /// on the next TemporalId)
202 /// - If the InterpolationTarget is not sequential, invokes
203 /// intrp::Actions::SendPointsToInterpolator for all TemporalIds.
204 ///
205 /// Uses:
206 /// - DataBox:
207 /// - `intrp::Tags::PendingTeporalIds`
208 /// - `intrp::Tags::TeporalIds`
209 ///
210 /// DataBox changes:
211 /// - Adds: nothing
212 /// - Removes: nothing
213 /// - Modifies:
214 /// - `intrp::Tags::PendingTeporalIds`
215 /// - `intrp::Tags::TeporalIds`
216 ///
217 /// For requirements on InterpolationTargetTag, see InterpolationTarget
218 template <typename InterpolationTargetTag>
219 struct VerifyTemporalIdsAndSendPoints {
220  template <typename ParallelComponent, typename DbTags, typename Metavariables,
221  typename ArrayIndex,
222  Requires<tmpl::list_contains_v<
223  DbTags,
224  Tags::TemporalIds<typename Metavariables::temporal_id::type>>> =
225  nullptr>
226  static void apply(db::DataBox<DbTags>& box,
228  const ArrayIndex& /*array_index*/) noexcept {
229  if constexpr (std::is_same_v<typename InterpolationTargetTag::
230  compute_target_points::frame,
231  ::Frame::Grid>) {
232  detail::verify_temporal_ids_and_send_points_time_independent<
233  InterpolationTargetTag, ParallelComponent>(make_not_null(&box),
234  cache);
235  } else {
236  if (InterpolationTarget_detail::maps_are_time_dependent<
237  InterpolationTargetTag>(box, tmpl::type_<Metavariables>{})) {
238  if constexpr (InterpolationTarget_detail::
239  cache_contains_functions_of_time<
240  Metavariables>::value) {
241  detail::verify_temporal_ids_and_send_points_time_dependent<
242  InterpolationTargetTag, ParallelComponent>(make_not_null(&box),
243  cache);
244  } else {
245  // We error here because the maps are time-dependent, yet
246  // the cache does not contain FunctionsOfTime. It would be
247  // nice to make this a compile-time error; however, we want
248  // the code to compile for the completely time-independent
249  // case where there are no FunctionsOfTime in the cache at
250  // all. Unfortunately, checking whether the maps are
251  // time-dependent is currently not constexpr.
252  ERROR(
253  "There is a time-dependent CoordinateMap in at least one "
254  "of the Blocks, but FunctionsOfTime are not in the "
255  "GlobalCache. If you intend to use a time-dependent "
256  "CoordinateMap, please add FunctionsOfTime to the GlobalCache.");
257  }
258  } else {
259  detail::verify_temporal_ids_and_send_points_time_independent<
260  InterpolationTargetTag, ParallelComponent>(make_not_null(&box),
261  cache);
262  }
263  }
264  }
265 };
266 } // namespace intrp::Actions
std::string
intrp::Actions::VerifyTemporalIdsAndSendPoints
Sends points to an Interpolator for verified temporal_ids.
Definition: VerifyTemporalIdsAndSendPoints.hpp:16
Parallel::GlobalCache
Definition: ElementReceiveInterpPoints.hpp:15
Frame::Grid
Definition: IndexType.hpp:43
GlobalCache.hpp
std::vector
db::get
const auto & get(const DataBox< TagList > &box) noexcept
Retrieve the item with tag Tag from the DataBox.
Definition: DataBox.hpp:791
intrp::Actions
Holds Actions for Interpolator and InterpolationTarget.
Definition: ElementInitInterpPoints.hpp:24
ERROR
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:37
DataBox.hpp
std::deque
ActionTesting::cache
Parallel::GlobalCache< Metavariables > & cache(MockRuntimeSystem< Metavariables > &runner, const ArrayIndex &array_index) noexcept
Returns the GlobalCache of Component with index array_index.
Definition: MockRuntimeSystemFreeFunctions.hpp:382
Gsl.hpp
intrp::Tags::PendingTemporalIds
temporal_ids that have been flagged to interpolate on, but that have not yet been added to Tags::Temp...
Definition: Tags.hpp:65
Parallel::simple_action
void simple_action(Proxy &&proxy) noexcept
Invoke a simple action on proxy
Definition: Invoke.hpp:62
make_not_null
gsl::not_null< T * > make_not_null(T *ptr) noexcept
Construct a not_null from a pointer. Often this will be done as an implicit conversion,...
Definition: Gsl.hpp:880
Requires
typename Requires_detail::requires_impl< B >::template_error_type_failed_to_meet_requirements_on_template_parameters Requires
Express requirements on the template parameters of a function or class, replaces std::enable_if_t
Definition: Requires.hpp:67
std::numeric_limits::max
T max(T... args)
std::unique_ptr
std::unordered_map
Parallel::SimpleActionCallback
Wraps a call to a simple action and its arguments. Can be invoked only once.
Definition: Callback.hpp:35
cpp20::find
constexpr InputIt find(InputIt first, InputIt last, const T &value)
Definition: Algorithm.hpp:136
gsl::not_null
Require a pointer to not be a nullptr
Definition: ReadSpecPiecewisePolynomial.hpp:13