Algorithm.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 // <array> wanted for tuple_size
7 // IWYU pragma: no_include <array>
8 #include <boost/variant/variant.hpp>
9 #include <cstddef>
10 #include <exception>
11 #include <initializer_list>
12 #include <lrtslock.h>
13 #include <ostream>
14 #include <pup.h>
15 #include <tuple>
16 #include <unordered_set>
17 #include <utility>
18 
19 #include "DataStructures/DataBox/DataBox.hpp" // IWYU pragma: keep
20 #include "ErrorHandling/Assert.hpp"
21 #include "ErrorHandling/Error.hpp"
22 // Include... ourself?
23 // IWYU pragma: no_include "Parallel/Algorithm.hpp"
24 #include "Parallel/AlgorithmMetafunctions.hpp"
25 #include "Parallel/CharmRegistration.hpp"
26 #include "Parallel/NodeLock.hpp"
27 #include "Parallel/ParallelComponentHelpers.hpp"
28 #include "Parallel/SimpleActionVisitation.hpp"
29 #include "Parallel/TypeTraits.hpp"
32 #include "Utilities/Gsl.hpp"
33 #include "Utilities/NoSuchType.hpp"
34 #include "Utilities/Overloader.hpp"
35 #include "Utilities/PrettyType.hpp"
36 #include "Utilities/Requires.hpp"
37 #include "Utilities/TMPL.hpp"
39 #include "Utilities/TypeTraits.hpp"
40 
41 /// \cond
42 namespace Parallel {
43 template <class Metavariables>
44 class CProxy_ConstGlobalCache;
45 template <class Metavariables>
46 class ConstGlobalCache;
47 namespace Algorithms {
48 struct Nodegroup;
49 struct Singleton;
50 } // namespace Algorithms
51 } // namespace Parallel
52 // IWYU pragma: no_forward_declare db::DataBox
53 /// \endcond
54 
55 namespace Parallel {
56 /// \cond
57 template <typename ParallelComponent, typename ActionList>
58 class AlgorithmImpl;
59 /// \endcond
60 
61 /*!
62  * \ingroup ParallelGroup
63  * \brief A distributed object (Charm++ Chare) that executes a series of Actions
64  * and is capable of sending and receiving data. Acts as an interface to
65  * Charm++.
66  *
67  * ### Different Types of Algorithms
68  * Charm++ chares can be one of four types, which is specified by the type alias
69  * `chare_type` inside the `ParallelComponent`. The four available types of
70  * Algorithms are:
71  * 1. A Parallel::Algorithms::Singleton where there is only one
72  * in the entire execution of the program.
73  * 2. A Parallel::Algorithms::Array which holds zero or more
74  * elements each of which is a distributed object on some core. An array can
75  * grow and shrink in size dynamically if need be and can also be bound to
76  * another array. That is, the bound array has the same number of elements as
77  * the array it is bound to, and elements with the same ID are on the same core.
78  * 3. A Parallel::Algorithms::Group, which is an array but there is
79  * one element per core and they are not able to be moved around between cores.
80  * These are typically useful for gathering data from array elements on their
81  * core, and then processing or reducing it.
82  * 4. A Parallel::Algorithms::Nodegroup, which is similar to a
83  * group except that there is one element per node. For Charm++ SMP (shared
84  * memory parallelism) builds a node corresponds to the usual definition of a
85  * node on a supercomputer. However, for non-SMP builds nodes and cores are
86  * equivalent. An important difference between groups and nodegroups is that
87  * entry methods (remote calls to functions) are not threadsafe on nodegroups.
88  * It is up to the person writing the Actions that will be executed on the
89  * Nodegroup Algorithm to ensure they are threadsafe.
90  *
91  * ### What is an Algorithm?
92  * An Algorithm is a distributed object, a Charm++ chare, that repeatedly
93  * executes a series of Actions. An Action is a struct that has a `static` apply
94  * function with signature:
95  *
96  * \code
97  * template <typename... DbTags, typename... InboxTags, typename Metavariables,
98  * typename ArrayIndex, typename ActionList>
99  * static auto apply(db::DataBox<tmpl::list<DbTags...>>& box,
100  * tuples::TaggedTuple<InboxTags...>& inboxes,
101  * const ConstGlobalCache<Metavariables>& cache,
102  * const ArrayIndex& array_index,
103  * const TemporalId& temporal_id, const ActionList meta);
104  * \endcode
105  *
106  * Note that any of the arguments can be const or non-const references except
107  * `array_index`, which must be a `const&`.
108  *
109  * ### Explicit instantiations of entry methods
110  * The code in src/Parallel/CharmMain.tpp registers all entry methods, and if
111  * one is not properly registered then a static_assert explains how to have it
112  * be registered. If there is a bug in the implementation and an entry method
113  * isn't being registered or hitting a static_assert then Charm++ will give an
114  * error of the following form:
115  *
116  * \verbatim
117  * registration happened after init Entry point: simple_action(), addr:
118  * 0x555a3d0e2090
119  * ------------- Processor 0 Exiting: Called CmiAbort ------------
120  * Reason: Did you forget to instantiate a templated entry method in a .ci file?
121  * \endverbatim
122  *
123  * If you encounter this issue please file a bug report supplying everything
124  * necessary to reproduce the issue.
125  */
126 template <typename ParallelComponent, typename... ActionsPack>
127 class AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>> {
128  public:
129  using initial_databox = typename ParallelComponent::initial_databox;
130  /// The metavariables class passed to the Algorithm
131  using metavariables = typename ParallelComponent::metavariables;
132  /// List of Actions in the order they will be executed
133  using actions_list = tmpl::list<ActionsPack...>;
134  /// List off all the Tags that can be received into the Inbox
136  /// The type of the object used to identify the element of the array, group
137  /// or nodegroup spatially. The default should be an `int`.
138  using array_index = typename get_array_index<
139  typename ParallelComponent::chare_type>::template f<ParallelComponent>;
140 
141  using parallel_component = ParallelComponent;
142  /// The type of the Chare
143  using chare_type = typename ParallelComponent::chare_type;
144  /// The Charm++ proxy object type
145  using cproxy_type =
146  typename chare_type::template cproxy<ParallelComponent, array_index>;
147  /// The Charm++ base object type
148  using cbase_type =
149  typename chare_type::template cbase<ParallelComponent, array_index>;
150  /// \cond
151  // The types held by the boost::variant, box_
152  using databox_types = Algorithm_detail::build_action_return_typelist<
153  initial_databox,
154  tmpl::list<tuples::tagged_tuple_from_typelist<inbox_tags_list>,
157  ActionsPack...>;
158  /// \endcond
159 
160  /// \cond
161  // Needed for serialization
162  AlgorithmImpl() noexcept;
163  /// \endcond
164 
165  /// Constructor used by Main to initialize the algorithm
166  explicit AlgorithmImpl(const Parallel::CProxy_ConstGlobalCache<metavariables>&
167  global_cache_proxy) noexcept;
168 
169  /// Charm++ migration constructor, used after a chare is migrated
170  constexpr explicit AlgorithmImpl(CkMigrateMessage* /*msg*/) noexcept;
171 
172  /// \cond
173  ~AlgorithmImpl();
174 
175  AlgorithmImpl(const AlgorithmImpl& /*unused*/) = delete;
176  AlgorithmImpl& operator=(const AlgorithmImpl& /*unused*/) = delete;
177  AlgorithmImpl(AlgorithmImpl&& /*unused*/) = delete;
178  AlgorithmImpl& operator=(AlgorithmImpl&& /*unused*/) = delete;
179  /// \endcond
180 
181  /*!
182  * \brief Calls the `apply` function `Action` after a reduction has been
183  * completed.
184  *
185  * The `apply` function must take `arg` as its last argument.
186  */
187  template <typename Action, typename Arg>
188  void reduction_action(Arg arg) noexcept;
189 
190  /// \brief Explicitly call the action `Action`. If the returned DataBox type
191  /// is not one of the types of the algorithm then a compilation error occurs.
192  template <typename Action, typename... Args>
193  void simple_action(std::tuple<Args...> args) noexcept;
194 
195  template <typename Action>
196  void simple_action() noexcept;
197 
198  // @{
199  /// Call an Action on a local nodegroup requiring the Action to handle thread
200  /// safety.
201  ///
202  /// The `CmiNodelock` of the nodegroup is passed to the Action instead of the
203  /// `action_list` as a `const gsl::not_null<CmiNodelock*>&`. The node lock can
204  /// be locked with the `Parallel::lock()` function, and unlocked with
205  /// `Parallel::unlock()`. `Parallel::try_lock()` is also provided in case
206  /// something useful can be done if the lock couldn't be acquired.
207  template <typename Action, typename... Args,
208  Requires<(sizeof...(Args),
209  cpp17::is_same_v<Parallel::Algorithms::Nodegroup,
210  chare_type>)> = nullptr>
211  void threaded_action(std::tuple<Args...> args) noexcept {
212  (void)Parallel::charmxx::RegisterThreadedAction<ParallelComponent, Action,
213  Args...>::registrar;
214  forward_tuple_to_threaded_action<Action>(
215  std::move(args), std::make_index_sequence<sizeof...(Args)>{});
216  }
217 
218  template <typename Action>
219  void threaded_action() noexcept {
220  // NOLINTNEXTLINE(modernize-redundant-void-arg)
221  (void)Parallel::charmxx::RegisterThreadedAction<ParallelComponent,
222  Action>::registrar;
223  Algorithm_detail::simple_action_visitor<Action, initial_databox>(
224  box_, inboxes_, *const_global_cache_,
225  static_cast<const array_index&>(array_index_), actions_list{},
226  std::add_pointer_t<ParallelComponent>{}, make_not_null(&node_lock_));
227  }
228  // @}
229 
230  /// \brief Receive data and store it in the Inbox, and try to continue
231  /// executing the algorithm
232  ///
233  /// When an algorithm has terminated it can be restarted by passing
234  /// `enable_if_disabled = true`. This allows long-term disabling and
235  /// re-enabling of algorithms
236  template <typename ReceiveTag, typename ReceiveDataType>
237  void receive_data(typename ReceiveTag::temporal_id instance,
238  ReceiveDataType&& t,
239  bool enable_if_disabled = false) noexcept;
240 
241  /// Start evaluating the algorithm until the is_ready function of an Action
242  /// returns false, or an Action returns with `terminate` set to `true`
243  constexpr void perform_algorithm() noexcept;
244 
245  /// Tell the Algorithm it should no longer execute the algorithm. This does
246  /// not mean that the execution of the program is terminated, but only that
247  /// the algorithm has terminated. An algorithm can be restarted by pass `true`
248  /// as the second argument to the `receive_data` method.
249  constexpr void set_terminate(bool t) noexcept { terminate_ = t; }
250 
251  /// Check if an algorithm should continue being evaluated
252  constexpr bool get_terminate() const noexcept { return terminate_; }
253 
254  private:
255  static constexpr bool is_singleton =
256  cpp17::is_same_v<chare_type, Parallel::Algorithms::Singleton>;
257 
258  template <class Dummy = int,
260  constexpr void set_array_index() noexcept {}
261  template <class Dummy = int,
263  void set_array_index() noexcept {
264  // down cast to the algorithm_type, so that the `thisIndex` method can be
265  // called, which is defined in the CBase class
266  array_index_ = static_cast<typename chare_type::template algorithm_type<
267  ParallelComponent, array_index>&>(*this)
268  .thisIndex;
269  }
270 
271  template <size_t... Is>
272  constexpr bool iterate_over_actions(
273  std::index_sequence<Is...> /*meta*/) noexcept;
274 
275  template <typename Action, typename... Args, size_t... Is>
276  void forward_tuple_to_action(std::tuple<Args...>&& args,
277  std::index_sequence<Is...> /*meta*/) noexcept {
278  Algorithm_detail::simple_action_visitor<Action, initial_databox>(
279  box_, inboxes_, *const_global_cache_,
280  static_cast<const array_index&>(array_index_), actions_list{},
281  std::add_pointer_t<ParallelComponent>{},
282  std::forward<Args>(std::get<Is>(args))...);
283  }
284 
285  template <typename Action, typename... Args, size_t... Is>
286  void forward_tuple_to_threaded_action(
287  std::tuple<Args...>&& args,
288  std::index_sequence<Is...> /*meta*/) noexcept {
289  const gsl::not_null<CmiNodeLock*> node_lock{&node_lock_};
290  Algorithm_detail::simple_action_visitor<Action, initial_databox>(
291  box_, inboxes_, *const_global_cache_,
292  static_cast<const array_index&>(array_index_), actions_list{},
293  std::add_pointer_t<ParallelComponent>{}, node_lock,
294  std::forward<Args>(std::get<Is>(args))...);
295  }
296 
297  // @{
298  /// Since it's not clear how or if it's possible at all to do SFINAE with
299  /// Charm's ci files, we use a forward to implementation, where the
300  /// implementation is a simple function call that we can use SFINAE with
301  template <typename ReceiveTag, typename ReceiveDataType,
303  nullptr>
304  void receive_data_impl(typename ReceiveTag::temporal_id& instance,
305  ReceiveDataType&& t);
306 
307  template <
308  typename ReceiveTag, typename ReceiveDataType,
310  typename ReceiveTag::type::mapped_type>> = nullptr>
311  constexpr void receive_data_impl(typename ReceiveTag::temporal_id& instance,
312  ReceiveDataType&& t);
313  // @}
314 
315  // Member variables
316 
317 #ifdef SPECTRE_CHARM_PROJECTIONS
318  double non_action_time_start_;
319 #endif
320 
321  Parallel::ConstGlobalCache<metavariables>* const_global_cache_{nullptr};
322  bool performing_action_ = false;
323  std::size_t algorithm_step_ = 0;
324  tmpl::conditional_t<Parallel::is_node_group_proxy<cproxy_type>::value,
325  CmiNodeLock, NoSuchType>
326  node_lock_;
327 
328  bool terminate_{false};
329  // Create a boost::variant that can hold any of the DataBox's
330  make_boost_variant_over<tmpl::remove_duplicates<
331  tmpl::append<tmpl::list<db::DataBox<tmpl::list<>>>, databox_types>>>
332  box_;
333  tuples::tagged_tuple_from_typelist<inbox_tags_list> inboxes_{};
334  array_index array_index_;
335 };
336 
337 ////////////////////////////////////////////////////////////////
338 // Definitions
339 ////////////////////////////////////////////////////////////////
340 
341 /// \cond
342 template <typename ParallelComponent, typename... ActionsPack>
343 AlgorithmImpl<ParallelComponent,
344  tmpl::list<ActionsPack...>>::AlgorithmImpl() noexcept {
345  make_overloader([](CmiNodeLock& node_lock) { node_lock = create_lock(); },
346  [](NoSuchType /*unused*/) {})(node_lock_);
347  set_array_index();
348 }
349 
350 template <typename ParallelComponent, typename... ActionsPack>
351 AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::AlgorithmImpl(
352  const Parallel::CProxy_ConstGlobalCache<metavariables>&
353  global_cache_proxy) noexcept
354  : AlgorithmImpl() {
355  const_global_cache_ = global_cache_proxy.ckLocalBranch();
356 }
357 
358 template <typename ParallelComponent, typename... ActionsPack>
359 constexpr AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::
360  AlgorithmImpl(CkMigrateMessage* /*msg*/) noexcept
361  : AlgorithmImpl() {}
362 
363 template <typename ParallelComponent, typename... ActionsPack>
364 AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::~AlgorithmImpl() {
365  // We place the registrar in the destructor since every AlgorithmImpl will
366  // have a destructor, but we have different constructors so it's not clear
367  // which will be instantiated.
369  ParallelComponent>::registrar;
370  make_overloader([](CmiNodeLock& node_lock) { free_lock(&node_lock); },
371  [](NoSuchType /*unused*/) {})(node_lock_);
372 }
373 
374 template <typename ParallelComponent, typename... ActionsPack>
375 template <typename Action, typename Arg>
376 void AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::
377  reduction_action(Arg arg) noexcept {
379  ParallelComponent, Action, std::decay_t<Arg>>::registrar;
380  lock(&node_lock_);
381  if (performing_action_) {
382  ERROR(
383  "Already performing an Action and cannot execute additional Actions "
384  "from inside of an Action. This is only possible if the "
385  "reduction_action function is not invoked via a proxy, which makes "
386  "no sense for a reduction.");
387  }
388  performing_action_ = true;
389  arg.finalize();
390  forward_tuple_to_action<Action>(std::move(arg.data()),
391  std::make_index_sequence<Arg::pack_size()>{});
392  performing_action_ = false;
393  unlock(&node_lock_);
394 }
395 
396 template <typename ParallelComponent, typename... ActionsPack>
397 template <typename Action, typename... Args>
398 void AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::
399  simple_action(std::tuple<Args...> args) noexcept {
400  (void)Parallel::charmxx::RegisterSimpleAction<ParallelComponent, Action,
401  Args...>::registrar;
402  lock(&node_lock_);
403  if (performing_action_) {
404  ERROR(
405  "Already performing an Action and cannot execute additional Actions "
406  "from inside of an Action. This is only possible if the "
407  "simple_action function is not invoked via a proxy, which "
408  "we do not allow.");
409  }
410  performing_action_ = true;
411  forward_tuple_to_action<Action>(std::move(args),
412  std::make_index_sequence<sizeof...(Args)>{});
413  performing_action_ = false;
414  unlock(&node_lock_);
415 }
416 
417 template <typename ParallelComponent, typename... ActionsPack>
418 template <typename Action>
419 void AlgorithmImpl<ParallelComponent,
420  tmpl::list<ActionsPack...>>::simple_action() noexcept {
421  (void)Parallel::charmxx::RegisterSimpleAction<ParallelComponent,
422  Action>::registrar;
423  lock(&node_lock_);
424  if (performing_action_) {
425  ERROR(
426  "Already performing an Action and cannot execute additional Actions "
427  "from inside of an Action. This is only possible if the "
428  "simple_action function is not invoked via a proxy, which "
429  "we do not allow.");
430  }
431  performing_action_ = true;
432  Algorithm_detail::simple_action_visitor<Action, initial_databox>(
433  box_, inboxes_, *const_global_cache_,
434  static_cast<const array_index&>(array_index_), actions_list{},
436  performing_action_ = false;
437  unlock(&node_lock_);
438 }
439 
440 template <typename ParallelComponent, typename... ActionsPack>
441 template <typename ReceiveTag, typename ReceiveDataType>
442 void AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::receive_data(
443  typename ReceiveTag::temporal_id instance, ReceiveDataType&& t,
444  const bool enable_if_disabled) noexcept {
445  (void)Parallel::charmxx::RegisterReceiveData<ParallelComponent,
446  ReceiveTag>::registrar;
447  try {
448  lock(&node_lock_);
449  if (enable_if_disabled) {
450  set_terminate(false);
451  }
452  receive_data_impl<ReceiveTag>(instance, std::forward<ReceiveDataType>(t));
453  unlock(&node_lock_);
454  } catch (std::exception& e) {
455  ERROR("Fatal error: Unexpected exception caught in receive_data: "
456  << e.what());
457  }
458  perform_algorithm();
459 }
460 
461 template <typename ParallelComponent, typename... ActionsPack>
462 constexpr void
463 AlgorithmImpl<ParallelComponent,
464  tmpl::list<ActionsPack...>>::perform_algorithm() noexcept {
465  if (performing_action_ or get_terminate()) {
466  return;
467  }
468 #ifdef SPECTRE_CHARM_PROJECTIONS
469  non_action_time_start_ = Parallel::wall_time();
470 #endif
471  lock(&node_lock_);
472  while (tmpl::size<actions_list>::value > 0 and not get_terminate() and
473  iterate_over_actions(
474  std::make_index_sequence<tmpl::size<actions_list>::value>{})) {
475  }
476  unlock(&node_lock_);
477 #ifdef SPECTRE_CHARM_PROJECTIONS
478  traceUserBracketEvent(SPECTRE_CHARM_NON_ACTION_WALLTIME_EVENT_ID,
479  non_action_time_start_, Parallel::wall_time());
480 #endif
481 }
482 /// \endcond
483 
484 template <typename ParallelComponent, typename... ActionsPack>
485 template <size_t... Is>
486 constexpr bool AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::
487  iterate_over_actions(const std::index_sequence<Is...> /*meta*/) noexcept {
488  bool take_next_action = true;
489  const auto helper = [ this, &take_next_action ](auto iteration) noexcept {
490  constexpr size_t iter = decltype(iteration)::value;
491  if (not(take_next_action and not terminate_ and algorithm_step_ == iter)) {
492  return;
493  }
494  using this_action = tmpl::at_c<actions_list, iter>;
495  using this_databox =
496  tmpl::at_c<databox_types,
497  iter == 0 ? tmpl::size<databox_types>::value - 1 : iter>;
498  this_databox* box{};
499 
500  try {
501  box = &boost::get<this_databox>(box_);
502  } catch (std::exception& e) {
503  ERROR(
504  "\nFailed to retrieve Databox in take_next_action:\nCaught "
505  "exception: '"
506  << e.what() << "'\nDataBox type: '"
507  << pretty_type::get_name<this_databox>() << "'\nIteration: " << iter
508  << "\nAction: '" << pretty_type::get_name<this_action>()
509  << "'\nBoost::Variant id: " << box_.which()
510  << "\nBoost::Variant type is: '" << type_of_current_state(box_)
511  << "'\n\n");
512  }
513 
514  const auto check_if_ready = make_overloader(
515  [this, &box](std::true_type /*has_is_ready*/, auto t) {
516  return decltype(t)::is_ready(
517  static_cast<const this_databox&>(*box),
518  static_cast<
519  const tuples::tagged_tuple_from_typelist<inbox_tags_list>&>(
520  inboxes_),
521  *const_global_cache_,
522  static_cast<const array_index&>(array_index_));
523  },
524  [](std::false_type /*has_is_ready*/, auto) { return true; });
525 
526  if (not check_if_ready(
527  Algorithm_detail::is_is_ready_callable_t<
528  this_action, this_databox,
529  tuples::tagged_tuple_from_typelist<inbox_tags_list>,
531  this_action{})) {
532  take_next_action = false;
533  return;
534  }
535 
536 #ifdef SPECTRE_CHARM_PROJECTIONS
537  traceUserBracketEvent(SPECTRE_CHARM_NON_ACTION_WALLTIME_EVENT_ID,
538  non_action_time_start_, Parallel::wall_time());
539  double start_time = detail::start_trace_action<this_action>();
540 #endif
541  performing_action_ = true;
542  algorithm_step_++;
544  [this](auto& my_box, std::integral_constant<size_t, 1> /*meta*/)
545  SPECTRE_JUST_ALWAYS_INLINE noexcept {
546  std::tie(box_) = this_action::apply(
547  my_box, inboxes_, *const_global_cache_,
548  static_cast<const array_index&>(array_index_), actions_list{},
550  },
551  [this](auto& my_box, std::integral_constant<size_t, 2> /*meta*/)
552  SPECTRE_JUST_ALWAYS_INLINE noexcept {
553  std::tie(box_, terminate_) = this_action::apply(
554  my_box, inboxes_, *const_global_cache_,
555  static_cast<const array_index&>(array_index_), actions_list{},
557  },
558  [this](auto& my_box, std::integral_constant<size_t, 3> /*meta*/)
559  SPECTRE_JUST_ALWAYS_INLINE noexcept {
560  std::tie(box_, terminate_, algorithm_step_) = this_action::apply(
561  my_box, inboxes_, *const_global_cache_,
562  static_cast<const array_index&>(array_index_), actions_list{},
564  })(
565  *box, typename std::tuple_size<decltype(this_action::apply(
566  *box, inboxes_, *const_global_cache_,
567  static_cast<const array_index&>(array_index_), actions_list{},
569 
570  performing_action_ = false;
571 #ifdef SPECTRE_CHARM_PROJECTIONS
572  detail::stop_trace_action<this_action>(start_time);
573  non_action_time_start_ = Parallel::wall_time();
574 #endif
575  // Wrap counter if necessary
576  if (algorithm_step_ >= tmpl::size<actions_list>::value) {
577  algorithm_step_ = 0;
578  }
579  };
580  // In case of no Actions
581  static_cast<void>(helper);
582  // This is a template for loop for Is
583  static_cast<void>(std::initializer_list<char>{
584  (static_cast<void>(helper(std::integral_constant<size_t, Is>{})),
585  '0')...});
586  return take_next_action;
587 }
588 
589 template <typename ParallelComponent, typename... ActionsPack>
590 template <typename ReceiveTag, typename ReceiveDataType,
592 void AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::
593  receive_data_impl(typename ReceiveTag::temporal_id& instance,
594  ReceiveDataType&& t) {
595  static_assert(
597  cpp20::remove_cvref_t<typename ReceiveDataType::first_type>,
598  typename ReceiveTag::type::mapped_type::key_type> and
600  cpp20::remove_cvref_t<typename ReceiveDataType::second_type>,
601  typename ReceiveTag::type::mapped_type::mapped_type>,
602  "The type of the data passed to receive_data for a tag that holds a map "
603  "must be a std::pair.");
604 #ifdef SPECTRE_CHARM_RECEIVE_MAP_DATA_EVENT_ID
605  double start_time = Parallel::wall_time();
606 #endif
607  auto& inbox = tuples::get<ReceiveTag>(inboxes_)[instance];
608  ASSERT(0 == inbox.count(t.first),
609  "Receiving data from the 'same' source twice. The message id is: "
610  << t.first);
611  if (not inbox.insert(std::forward<ReceiveDataType>(t)).second) {
612  ERROR("Failed to insert data to receive at instance '"
613  << instance << "' with tag '" << pretty_type::get_name<ReceiveTag>()
614  << "'.\n");
615  }
616 #ifdef SPECTRE_CHARM_RECEIVE_MAP_DATA_EVENT_ID
617  traceUserBracketEvent(SPECTRE_CHARM_RECEIVE_MAP_DATA_EVENT_ID, start_time,
619 #endif
620 }
621 
622 template <typename ParallelComponent, typename... ActionsPack>
623 template <typename ReceiveTag, typename ReceiveDataType,
625  typename ReceiveTag::type::mapped_type>>>
626 constexpr void
627 AlgorithmImpl<ParallelComponent, tmpl::list<ActionsPack...>>::receive_data_impl(
628  typename ReceiveTag::temporal_id& instance, ReceiveDataType&& t) {
629  tuples::get<ReceiveTag>(inboxes_)[instance].insert(
630  std::forward<ReceiveDataType>(t));
631 }
632 } // namespace Parallel
Derived class for registering receive_data functions.
Definition: CharmRegistration.hpp:399
void threaded_action(Proxy &&proxy) noexcept
Invoke a threaded action on proxy, where the proxy must be a nodegroup.
Definition: Invoke.hpp:131
tmpl::remove_duplicates< tmpl::join< tmpl::transform< ActionsList, Parallel_detail::get_inbox_tags_from_action< tmpl::_1 > >> > get_inbox_tags
Given a list of Actions, get a list of the unique inbox tags.
Definition: ParallelComponentHelpers.hpp:37
CmiNodeLock create_lock() noexcept
Create a converse CmiNodeLock.
Definition: NodeLock.hpp:16
Defines class tuples::TaggedTuple.
#define SPECTRE_JUST_ALWAYS_INLINE
Always inline a function, but do not mark it inline
Definition: ForceInline.hpp:24
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:35
void receive_data(Proxy &&proxy, typename ReceiveTag::temporal_id temporal_id, ReceiveDataType &&receive_data, const bool enable_if_disabled) noexcept
Send the data args... to the algorithm running on proxy, and tag the message with the identifier temp...
Definition: Invoke.hpp:51
Defines type traits related to Charm++ types.
Overloader< Fs... > make_overloader(Fs... fs)
Create Overloader<Fs...>, see Overloader for details.
Definition: Overloader.hpp:109
tmpl::list< ActionsPack... > actions_list
List of Actions in the order they will be executed.
Definition: Algorithm.hpp:133
Definition: Digraph.hpp:11
typename chare_type::template cproxy< ParallelComponent, array_index > cproxy_type
The Charm++ proxy object type.
Definition: Algorithm.hpp:146
#define ASSERT(a, m)
Assert that an expression should be true.
Definition: Assert.hpp:49
Contains functions that forward to Charm++ parallel functions.
Definition: Abort.hpp:13
Derived class for registering simple actions.
Definition: CharmRegistration.hpp:206
Defines the type alias Requires.
constexpr auto apply(F &&f, const DataBox< BoxTags > &box, Args &&... args)
Apply the function f with argument Tags TagsList from DataBox box
Definition: DataBox.hpp:1595
void threaded_action() noexcept
Call an Action on a local nodegroup requiring the Action to handle thread safety. ...
Definition: Algorithm.hpp:219
void free_lock(const gsl::not_null< CmiNodeLock *> node_lock) noexcept
Free a converse CmiNodeLock. Using the lock after free is undefined behavior.
Definition: NodeLock.hpp:23
Derived class for registering threaded actions.
Definition: CharmRegistration.hpp:282
void unlock(const gsl::not_null< CmiNodeLock *> node_lock) noexcept
Unlock a converse CmiNodeLock.
Definition: NodeLock.hpp:62
Used to mark "no type" or "bad state" for metaprogramming.
Definition: NoSuchType.hpp:10
typename ParallelComponent::metavariables metavariables
The metavariables class passed to the Algorithm.
Definition: Algorithm.hpp:131
Derived class for registering parallel components.
Definition: CharmRegistration.hpp:116
Defines classes and functions used for manipulating DataBox&#39;s.
constexpr bool is_same_v
Variable template for is_same.
Definition: TypeTraits.hpp:221
typename detail::make_boost_variant_over_impl< tmpl::remove_duplicates< Sequence > >::type make_boost_variant_over
Create a boost::variant with all all the types inside the typelist Sequence.
Definition: BoostHelpers.hpp:51
Defines helper functions for working with boost.
constexpr bool is_a_v
Definition: TypeTraits.hpp:543
typename get_array_index< typename ParallelComponent::chare_type >::template f< ParallelComponent > array_index
The type of the object used to identify the element of the array, group or nodegroup spatially...
Definition: Algorithm.hpp:139
Defines macro to always inline a function.
Defines macro ASSERT.
void lock(const gsl::not_null< CmiNodeLock *> node_lock) noexcept
Lock a converse CmiNodeLock.
Definition: NodeLock.hpp:34
Contains a pretty_type library to write types in a "pretty" format.
constexpr bool get_terminate() const noexcept
Check if an algorithm should continue being evaluated.
Definition: Algorithm.hpp:252
typename chare_type::template cbase< ParallelComponent, array_index > cbase_type
The Charm++ base object type.
Definition: Algorithm.hpp:149
Wraps the template metaprogramming library used (brigand)
Defines functions and classes from the GSL.
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, but it may be necessary to perform the conversion explicitly when type deduction is desired.
Definition: Gsl.hpp:863
void simple_action(Proxy &&proxy) noexcept
Invoke a simple action on proxy
Definition: Invoke.hpp:112
typename ParallelComponent::chare_type chare_type
The type of the Chare.
Definition: Algorithm.hpp:143
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
C++ STL code present in C++17.
Definition: Array.hpp:16
std::string type_of_current_state(const boost::variant< Ts... > &variant) noexcept
Get the type name of the current state of the boost::variant.
Definition: BoostHelpers.hpp:93
Defines macro ERROR.
Defines type traits, some of which are future STL type_traits header.
double wall_time()
The current wall time in seconds.
Definition: Info.hpp:91
constexpr void set_terminate(bool t) noexcept
Tell the Algorithm it should no longer execute the algorithm. This does not mean that the execution o...
Definition: Algorithm.hpp:249
Require a pointer to not be a nullptr
Definition: ConservativeFromPrimitive.hpp:12
Derived class for registering reduction actions.
Definition: CharmRegistration.hpp:460
Parallel::get_inbox_tags< actions_list > inbox_tags_list
List off all the Tags that can be received into the Inbox.
Definition: Algorithm.hpp:135