Algorithm.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <boost/variant/variant.hpp>
7 #include <charm++.h>
8 #include <converse.h>
9 #include <cstddef>
10 #include <exception>
11 #include <initializer_list>
12 #include <ostream>
13 #include <pup.h>
14 #include <tuple>
15 #include <unordered_set>
16 #include <utility>
17 
18 #include "DataStructures/DataBox/DataBox.hpp" // IWYU pragma: keep
19 #include "DataStructures/DataBox/PrefixHelpers.hpp"
20 #include "Parallel/AlgorithmMetafunctions.hpp"
21 #include "Parallel/Algorithms/AlgorithmArrayDeclarations.hpp"
22 #include "Parallel/Algorithms/AlgorithmGroupDeclarations.hpp"
23 #include "Parallel/Algorithms/AlgorithmNodegroupDeclarations.hpp"
24 #include "Parallel/Algorithms/AlgorithmSingletonDeclarations.hpp"
25 #include "Parallel/CharmRegistration.hpp"
26 #include "Parallel/GlobalCache.hpp"
27 #include "Parallel/NodeLock.hpp"
28 #include "Parallel/ParallelComponentHelpers.hpp"
29 #include "Parallel/PhaseDependentActionList.hpp"
30 #include "Parallel/PupStlCpp11.hpp"
31 #include "Parallel/SimpleActionVisitation.hpp"
32 #include "Parallel/TypeTraits.hpp"
37 #include "Utilities/Gsl.hpp"
38 #include "Utilities/MakeString.hpp"
39 #include "Utilities/NoSuchType.hpp"
40 #include "Utilities/Overloader.hpp"
41 #include "Utilities/PrettyType.hpp"
42 #include "Utilities/Requires.hpp"
44 #include "Utilities/TMPL.hpp"
45 #include "Utilities/TaggedTuple.hpp"
46 #include "Utilities/TypeTraits.hpp"
47 
48 // IWYU pragma: no_include <array> // for tuple_size
49 
50 // IWYU pragma: no_include "Parallel/Algorithm.hpp" // Include... ourself?
51 
52 namespace Parallel {
53 /// \cond
54 template <typename ParallelComponent, typename PhaseDepActionList>
55 class AlgorithmImpl;
56 /// \endcond
57 
58 /*!
59  * \ingroup ParallelGroup
60  * \brief A distributed object (Charm++ Chare) that executes a series of Actions
61  * and is capable of sending and receiving data. Acts as an interface to
62  * Charm++.
63  *
64  * ### Different Types of Algorithms
65  * Charm++ chares can be one of four types, which is specified by the type alias
66  * `chare_type` inside the `ParallelComponent`. The four available types of
67  * Algorithms are:
68  * 1. A Parallel::Algorithms::Singleton where there is only one
69  * in the entire execution of the program.
70  * 2. A Parallel::Algorithms::Array which holds zero or more
71  * elements each of which is a distributed object on some core. An array can
72  * grow and shrink in size dynamically if need be and can also be bound to
73  * another array. That is, the bound array has the same number of elements as
74  * the array it is bound to, and elements with the same ID are on the same core.
75  * 3. A Parallel::Algorithms::Group, which is an array but there is
76  * one element per core and they are not able to be moved around between cores.
77  * These are typically useful for gathering data from array elements on their
78  * core, and then processing or reducing it.
79  * 4. A Parallel::Algorithms::Nodegroup, which is similar to a
80  * group except that there is one element per node. For Charm++ SMP (shared
81  * memory parallelism) builds a node corresponds to the usual definition of a
82  * node on a supercomputer. However, for non-SMP builds nodes and cores are
83  * equivalent. An important difference between groups and nodegroups is that
84  * entry methods (remote calls to functions) are not threadsafe on nodegroups.
85  * It is up to the person writing the Actions that will be executed on the
86  * Nodegroup Algorithm to ensure they are threadsafe.
87  *
88  * ### What is an Algorithm?
89  * An Algorithm is a distributed object, a Charm++ chare, that repeatedly
90  * executes a series of Actions. An Action is a struct that has a `static` apply
91  * function with signature:
92  *
93  * \code
94  * template <typename... DbTags, typename... InboxTags, typename Metavariables,
95  * typename ArrayIndex, typename ActionList>
96  * static auto apply(db::DataBox<tmpl::list<DbTags...>>& box,
97  * tuples::TaggedTuple<InboxTags...>& inboxes,
98  * const GlobalCache<Metavariables>& cache,
99  * const ArrayIndex& array_index,
100  * const TemporalId& temporal_id, const ActionList meta);
101  * \endcode
102  *
103  * Note that any of the arguments can be const or non-const references except
104  * `array_index`, which must be a `const&`.
105  *
106  * ### Explicit instantiations of entry methods
107  * The code in src/Parallel/CharmMain.tpp registers all entry methods, and if
108  * one is not properly registered then a static_assert explains how to have it
109  * be registered. If there is a bug in the implementation and an entry method
110  * isn't being registered or hitting a static_assert then Charm++ will give an
111  * error of the following form:
112  *
113  * \verbatim
114  * registration happened after init Entry point: simple_action(), addr:
115  * 0x555a3d0e2090
116  * ------------- Processor 0 Exiting: Called CmiAbort ------------
117  * Reason: Did you forget to instantiate a templated entry method in a .ci file?
118  * \endverbatim
119  *
120  * If you encounter this issue please file a bug report supplying everything
121  * necessary to reproduce the issue.
122  */
123 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
124 class AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>
125  : public ParallelComponent::chare_type::template cbase<
126  ParallelComponent,
127  typename get_array_index<typename ParallelComponent::chare_type>::
128  template f<ParallelComponent>> {
129  static_assert(
130  sizeof...(PhaseDepActionListsPack) > 0,
131  "Must have at least one phase dependent action list "
132  "(PhaseActions) in a parallel component. See the first template "
133  "parameter of 'AlgorithmImpl' in the error message to see which "
134  "component doesn't have any phase dependent action lists.");
135 
136  public:
137  /// List of Actions in the order that generates the DataBox types
138  using all_actions_list = tmpl::flatten<
139  tmpl::list<typename PhaseDepActionListsPack::action_list...>>;
140  /// The metavariables class passed to the Algorithm
141  using metavariables = typename ParallelComponent::metavariables;
142  /// List off all the Tags that can be received into the Inbox
144  /// The type of the object used to identify the element of the array, group
145  /// or nodegroup spatially. The default should be an `int`.
146  using array_index = typename get_array_index<
147  typename ParallelComponent::chare_type>::template f<ParallelComponent>;
148 
149  using parallel_component = ParallelComponent;
150  /// The type of the Chare
151  using chare_type = typename parallel_component::chare_type;
152  /// The Charm++ proxy object type
153  using cproxy_type =
154  typename chare_type::template cproxy<parallel_component, array_index>;
155  /// The Charm++ base object type
156  using cbase_type =
157  typename chare_type::template cbase<parallel_component, array_index>;
158  /// The type of the phases
159  using PhaseType =
160  typename tmpl::front<tmpl::list<PhaseDepActionListsPack...>>::phase_type;
161 
162  using phase_dependent_action_lists = tmpl::list<PhaseDepActionListsPack...>;
163 
164  /// \cond
165  // Needed for serialization
166  AlgorithmImpl() noexcept;
167  /// \endcond
168 
169  /// Constructor used by Main to initialize the algorithm
170  template <class... InitializationTags>
171  AlgorithmImpl(
172  const Parallel::CProxy_GlobalCache<metavariables>&
173  global_cache_proxy,
174  tuples::TaggedTuple<InitializationTags...> initialization_items) noexcept;
175 
176  /// Charm++ migration constructor, used after a chare is migrated
177  explicit AlgorithmImpl(CkMigrateMessage* /*msg*/) noexcept;
178 
179  void pup(PUP::er& p) noexcept override { // NOLINT
180 #ifdef SPECTRE_CHARM_PROJECTIONS
181  p | non_action_time_start_;
182 #endif
183  if (performing_action_) {
184  ERROR("cannot serialize while performing action!");
185  }
186  p | performing_action_;
187  p | phase_;
188  p | algorithm_step_;
190  p | node_lock_;
191  }
192  p | terminate_;
193  p | box_;
194  p | inboxes_;
195  p | array_index_;
196  p | global_cache_;
197  }
198  /// \cond
199  ~AlgorithmImpl() override;
200 
201  AlgorithmImpl(const AlgorithmImpl& /*unused*/) = delete;
202  AlgorithmImpl& operator=(const AlgorithmImpl& /*unused*/) = delete;
203  AlgorithmImpl(AlgorithmImpl&& /*unused*/) = delete;
204  AlgorithmImpl& operator=(AlgorithmImpl&& /*unused*/) = delete;
205  /// \endcond
206 
207  /*!
208  * \brief Calls the `apply` function `Action` after a reduction has been
209  * completed.
210  *
211  * The `apply` function must take `arg` as its last argument.
212  */
213  template <typename Action, typename Arg>
214  void reduction_action(Arg arg) noexcept;
215 
216  /// \brief Explicitly call the action `Action`. If the returned DataBox type
217  /// is not one of the types of the algorithm then a compilation error occurs.
218  template <typename Action, typename... Args>
219  void simple_action(std::tuple<Args...> args) noexcept;
220 
221  template <typename Action>
222  void simple_action() noexcept;
223 
224  // @{
225  /// Call an Action on a local nodegroup requiring the Action to handle thread
226  /// safety.
227  ///
228  /// The `Parallel::NodeLock` of the nodegroup is passed to the Action instead
229  /// of the `action_list` as a `const gsl::not_null<Parallel::NodeLock*>&`. The
230  /// node lock can be locked with the `Parallel::NodeLock::lock()` function,
231  /// and unlocked with `Parallel::unlock()`. `Parallel::NodeLock::try_lock()`
232  /// is also provided in case something useful can be done if the lock couldn't
233  /// be acquired.
234  template <
235  typename Action, typename... Args,
236  Requires<(sizeof...(Args), std::is_same_v<Parallel::Algorithms::Nodegroup,
237  chare_type>)> = nullptr>
238  void threaded_action(std::tuple<Args...> args) noexcept {
239  (void)Parallel::charmxx::RegisterThreadedAction<ParallelComponent, Action,
240  Args...>::registrar;
241  forward_tuple_to_threaded_action<Action>(
242  std::move(args), std::make_index_sequence<sizeof...(Args)>{});
243  }
244 
245  template <typename Action>
246  void threaded_action() noexcept {
247  // NOLINTNEXTLINE(modernize-redundant-void-arg)
248  (void)Parallel::charmxx::RegisterThreadedAction<ParallelComponent,
249  Action>::registrar;
250  Algorithm_detail::simple_action_visitor<Action, ParallelComponent>(
251  box_, *global_cache_,
252  static_cast<const array_index&>(array_index_),
253  make_not_null(&node_lock_));
254  }
255  // @}
256 
257  /// \brief Receive data and store it in the Inbox, and try to continue
258  /// executing the algorithm
259  ///
260  /// When an algorithm has terminated it can be restarted by passing
261  /// `enable_if_disabled = true`. This allows long-term disabling and
262  /// re-enabling of algorithms
263  template <typename ReceiveTag, typename ReceiveDataType>
264  void receive_data(typename ReceiveTag::temporal_id instance,
265  ReceiveDataType&& t,
266  bool enable_if_disabled = false) noexcept;
267 
268  // @{
269  /// Start evaluating the algorithm until the is_ready function of an Action
270  /// returns false, or an Action returns with `terminate` set to `true`
271  ///
272  /// In the case where no phase is passed the current phase is assumed.
273  constexpr void perform_algorithm() noexcept;
274 
275  constexpr void perform_algorithm(const bool restart_if_terminated) noexcept {
276  if (restart_if_terminated) {
277  set_terminate(false);
278  }
279  perform_algorithm();
280  }
281  // @}
282 
283  void start_phase(const PhaseType next_phase) noexcept {
284  // terminate should be true since we exited a phase previously.
285  if (not get_terminate()) {
286  ERROR(
287  "An algorithm must always be set to terminate at the beginning of a "
288  "phase. Since this is not the case the previous phase did not end "
289  "correctly. The integer corresponding to the previous phase is: "
290  << static_cast<int>(phase_)
291  << " and the next phase is: " << static_cast<int>(next_phase));
292  }
293  // set terminate to true if there are no actions in this PDAL
294  set_terminate(number_of_actions_in_phase(next_phase) == 0);
295  phase_ = next_phase;
296  algorithm_step_ = 0;
297  perform_algorithm();
298  }
299 
300  /// Tell the Algorithm it should no longer execute the algorithm. This does
301  /// not mean that the execution of the program is terminated, but only that
302  /// the algorithm has terminated. An algorithm can be restarted by pass `true`
303  /// as the second argument to the `receive_data` method or by calling
304  /// perform_algorithm(true).
305  constexpr void set_terminate(const bool t) noexcept { terminate_ = t; }
306 
307  /// Check if an algorithm should continue being evaluated
308  constexpr bool get_terminate() const noexcept { return terminate_; }
309 
310  private:
311  static constexpr bool is_singleton =
312  std::is_same_v<chare_type, Parallel::Algorithms::Singleton>;
313 
314  template <class Dummy = int,
315  Requires<(sizeof(Dummy), is_singleton)> = nullptr>
316  constexpr void set_array_index() noexcept {}
317  template <class Dummy = int,
318  Requires<(sizeof(Dummy), not is_singleton)> = nullptr>
319  void set_array_index() noexcept {
320  // down cast to the algorithm_type, so that the `thisIndex` method can be
321  // called, which is defined in the CBase class
322  array_index_ = static_cast<typename chare_type::template algorithm_type<
323  ParallelComponent, array_index>&>(*this)
324  .thisIndex;
325  }
326 
327  template <typename PhaseDepActions, size_t... Is>
328  constexpr bool iterate_over_actions(
329  std::index_sequence<Is...> /*meta*/) noexcept;
330 
331  template <typename Action, typename... Args, size_t... Is>
332  void forward_tuple_to_action(std::tuple<Args...>&& args,
333  std::index_sequence<Is...> /*meta*/) noexcept {
334  Algorithm_detail::simple_action_visitor<Action, ParallelComponent>(
335  box_, *global_cache_,
336  static_cast<const array_index&>(array_index_),
337  std::forward<Args>(std::get<Is>(args))...);
338  }
339 
340  template <typename Action, typename... Args, size_t... Is>
341  void forward_tuple_to_threaded_action(
342  std::tuple<Args...>&& args,
343  std::index_sequence<Is...> /*meta*/) noexcept {
344  const gsl::not_null<Parallel::NodeLock*> node_lock{&node_lock_};
345  Algorithm_detail::simple_action_visitor<Action, ParallelComponent>(
346  box_, *global_cache_,
347  static_cast<const array_index&>(array_index_), node_lock,
348  std::forward<Args>(std::get<Is>(args))...);
349  }
350 
351  size_t number_of_actions_in_phase(const PhaseType phase) const noexcept {
352  size_t number_of_actions = 0;
353  const auto helper = [&number_of_actions, phase](auto pdal_v) {
354  if (pdal_v.phase == phase) {
355  number_of_actions = pdal_v.number_of_actions;
356  }
357  };
358  EXPAND_PACK_LEFT_TO_RIGHT(helper(PhaseDepActionListsPack{}));
359  return number_of_actions;
360  }
361 
362  // Member variables
363 
364 #ifdef SPECTRE_CHARM_PROJECTIONS
365  double non_action_time_start_;
366 #endif
367 
368  Parallel::GlobalCache<metavariables>* global_cache_{nullptr};
369  bool performing_action_ = false;
370  PhaseType phase_{};
371  std::size_t algorithm_step_ = 0;
372  tmpl::conditional_t<Parallel::is_node_group_proxy<cproxy_type>::value,
374  node_lock_;
375 
376  bool terminate_{true};
377 
378  using all_cache_tags = get_const_global_cache_tags<metavariables>;
379  using initial_databox = db::compute_databox_type<tmpl::flatten<tmpl::list<
380  Tags::GlobalCacheImpl<metavariables>,
381  typename ParallelComponent::initialization_tags,
383  // The types held by the boost::variant, box_
384  using databox_phase_types = typename Algorithm_detail::build_databox_types<
385  tmpl::list<>, phase_dependent_action_lists, initial_databox,
386  inbox_tags_list, metavariables, array_index, ParallelComponent>::type;
387 
388  template <typename T>
389  struct get_databox_types {
390  using type = typename T::databox_types;
391  };
392 
393  using databox_types = tmpl::flatten<
394  tmpl::transform<databox_phase_types, get_databox_types<tmpl::_1>>>;
395  // Create a boost::variant that can hold any of the DataBox's
396  using variant_boxes = tmpl::remove_duplicates<
397  tmpl::push_front<databox_types, db::DataBox<tmpl::list<>>>>;
399  tuples::tagged_tuple_from_typelist<inbox_tags_list> inboxes_{};
400  array_index array_index_;
401 };
402 
403 ////////////////////////////////////////////////////////////////
404 // Definitions
405 ////////////////////////////////////////////////////////////////
406 
407 /// \cond
408 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
409 AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::
410  AlgorithmImpl() noexcept {
411  set_array_index();
412 }
413 
414 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
415 template <class... InitializationTags>
416 AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::
417  AlgorithmImpl(const Parallel::CProxy_GlobalCache<metavariables>&
418  global_cache_proxy,
420  initialization_items) noexcept
421  : AlgorithmImpl() {
422  (void)initialization_items; // avoid potential compiler warnings if unused
423  global_cache_ = global_cache_proxy.ckLocalBranch();
424  box_ = db::create<
425  db::AddSimpleTags<tmpl::flatten<
426  tmpl::list<Tags::GlobalCacheImpl<metavariables>,
427  typename ParallelComponent::initialization_tags>>>,
430  global_cache_,
431  std::move(get<InitializationTags>(initialization_items))...);
432 }
433 
434 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
435 AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::
436  AlgorithmImpl(CkMigrateMessage* msg) noexcept
437  : cbase_type(msg) {
438  if (UNLIKELY(msg == nullptr)) {
439  ERROR(
440  "The AlgorithmImpl has been constructed with a nullptr as a "
441  "CkMigrateMessage* -- most likely this indicates that a constructor "
442  "is being used incorrectly, as the CkMigrateMessage* constructor "
443  "should only be used by the charm framework when migrating. "
444  "Constructing with a nullptr CkMigrateMessage* is dangerous and can "
445  "cause segfaults.");
446  }
447 }
448 
449 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
450 AlgorithmImpl<ParallelComponent,
451  tmpl::list<PhaseDepActionListsPack...>>::~AlgorithmImpl() {
452  // We place the registrar in the destructor since every AlgorithmImpl will
453  // have a destructor, but we have different constructors so it's not clear
454  // which will be instantiated.
456  ParallelComponent>::registrar;
457 }
458 
459 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
460 template <typename Action, typename Arg>
461 void AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::
462  reduction_action(Arg arg) noexcept {
464  ParallelComponent, Action, std::decay_t<Arg>>::registrar;
465  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
466  node_lock_.lock();
467  }
468  if (performing_action_) {
469  ERROR(
470  "Already performing an Action and cannot execute additional Actions "
471  "from inside of an Action. This is only possible if the "
472  "reduction_action function is not invoked via a proxy, which makes "
473  "no sense for a reduction.");
474  }
475  performing_action_ = true;
476  arg.finalize();
477  forward_tuple_to_action<Action>(std::move(arg.data()),
478  std::make_index_sequence<Arg::pack_size()>{});
479  performing_action_ = false;
480  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
481  node_lock_.unlock();
482  }
483  perform_algorithm();
484 }
485 
486 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
487 template <typename Action, typename... Args>
488 void AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::
489  simple_action(std::tuple<Args...> args) noexcept {
490  (void)Parallel::charmxx::RegisterSimpleAction<ParallelComponent, Action,
491  Args...>::registrar;
492  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
493  node_lock_.lock();
494  }
495  if (performing_action_) {
496  ERROR(
497  "Already performing an Action and cannot execute additional Actions "
498  "from inside of an Action. This is only possible if the "
499  "simple_action function is not invoked via a proxy, which "
500  "we do not allow.");
501  }
502  performing_action_ = true;
503  forward_tuple_to_action<Action>(std::move(args),
504  std::make_index_sequence<sizeof...(Args)>{});
505  performing_action_ = false;
506  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
507  node_lock_.unlock();
508  }
509  perform_algorithm();
510 }
511 
512 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
513 template <typename Action>
514 void AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::
515  simple_action() noexcept {
516  (void)Parallel::charmxx::RegisterSimpleAction<ParallelComponent,
517  Action>::registrar;
518  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
519  node_lock_.lock();
520  }
521  if (performing_action_) {
522  ERROR(
523  "Already performing an Action and cannot execute additional Actions "
524  "from inside of an Action. This is only possible if the "
525  "simple_action function is not invoked via a proxy, which "
526  "we do not allow.");
527  }
528  performing_action_ = true;
529  Algorithm_detail::simple_action_visitor<Action, ParallelComponent>(
530  box_, *global_cache_,
531  static_cast<const array_index&>(array_index_));
532  performing_action_ = false;
533  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
534  node_lock_.unlock();
535  }
536  perform_algorithm();
537 }
538 
539 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
540 template <typename ReceiveTag, typename ReceiveDataType>
541 void AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::
542  receive_data(typename ReceiveTag::temporal_id instance, ReceiveDataType&& t,
543  const bool enable_if_disabled) noexcept {
544  (void)Parallel::charmxx::RegisterReceiveData<ParallelComponent,
545  ReceiveTag>::registrar;
546  try {
547  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
548  node_lock_.lock();
549  }
550  if (enable_if_disabled) {
551  set_terminate(false);
552  }
553  ReceiveTag::insert_into_inbox(
554  make_not_null(&tuples::get<ReceiveTag>(inboxes_)), instance,
555  std::forward<ReceiveDataType>(t));
556  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
557  node_lock_.unlock();
558  }
559  } catch (std::exception& e) {
560  ERROR("Fatal error: Unexpected exception caught in receive_data: "
561  << e.what());
562  }
563  perform_algorithm();
564 }
565 
566 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
567 constexpr void AlgorithmImpl<
568  ParallelComponent,
569  tmpl::list<PhaseDepActionListsPack...>>::perform_algorithm() noexcept {
570  if (performing_action_ or get_terminate()) {
571  return;
572  }
573 #ifdef SPECTRE_CHARM_PROJECTIONS
574  non_action_time_start_ = sys::wall_time();
575 #endif
576  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
577  node_lock_.lock();
578  }
579  const auto invoke_for_phase = [this](auto phase_dep_v) noexcept {
580  using PhaseDep = decltype(phase_dep_v);
581  constexpr PhaseType phase = PhaseDep::phase;
582  using actions_list = typename PhaseDep::action_list;
583  if (phase_ == phase) {
584  while (tmpl::size<actions_list>::value > 0 and not get_terminate() and
585  iterate_over_actions<PhaseDep>(
586  std::make_index_sequence<tmpl::size<actions_list>::value>{})) {
587  }
588  }
589  };
590  // Loop over all phases, once the current phase is found we perform the
591  // algorithm in that phase until we are no longer able to because we are
592  // waiting on data to be sent or because the algorithm has been marked as
593  // terminated.
594  EXPAND_PACK_LEFT_TO_RIGHT(invoke_for_phase(PhaseDepActionListsPack{}));
595  if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {
596  node_lock_.unlock();
597  }
598 #ifdef SPECTRE_CHARM_PROJECTIONS
599  traceUserBracketEvent(SPECTRE_CHARM_NON_ACTION_WALLTIME_EVENT_ID,
600  non_action_time_start_, sys::wall_time());
601 #endif
602 }
603 /// \endcond
604 
605 template <typename ParallelComponent, typename... PhaseDepActionListsPack>
606 template <typename PhaseDepActions, size_t... Is>
607 constexpr bool
608 AlgorithmImpl<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::
609  iterate_over_actions(const std::index_sequence<Is...> /*meta*/) noexcept {
610  bool take_next_action = true;
611  const auto helper = [ this, &take_next_action ](auto iteration) noexcept {
612  constexpr size_t iter = decltype(iteration)::value;
613  if (not(take_next_action and not terminate_ and algorithm_step_ == iter)) {
614  return;
615  }
616  using actions_list = typename PhaseDepActions::action_list;
617  using this_action = tmpl::at_c<actions_list, iter>;
618  // Invoke the action's static `apply` method. The overloads are for handling
619  // the cases where the `apply` method returns:
620  // 1. only a DataBox
621  // 2. a DataBox and a bool determining whether or not to terminate
622  // 3. a DataBox, a bool, and an integer corresponding to which action in the
623  // current phase's algorithm to execute next.
624  //
625  // The first argument to the invokable is the DataBox to be passed into the
626  // action's `apply` method, while the second is:
627  // ```
628  // typename std::tuple_size<decltype(this_action::apply(
629  // box, inboxes_, *global_cache_,
630  // std::as_const(array_index_), actions_list{},
631  // std::add_pointer_t<ParallelComponent>{}))>::type{}
632  // ```
633  const auto invoke_this_action = make_overloader(
634  [this](auto& my_box,
635  std::integral_constant<size_t, 1> /*meta*/) noexcept {
636  std::tie(box_) =
637  this_action::apply(my_box, inboxes_, *global_cache_,
638  std::as_const(array_index_), actions_list{},
640  },
641  [this](auto& my_box,
642  std::integral_constant<size_t, 2> /*meta*/) noexcept {
643  std::tie(box_, terminate_) =
644  this_action::apply(my_box, inboxes_, *global_cache_,
645  std::as_const(array_index_), actions_list{},
647  },
648  [this](auto& my_box,
649  std::integral_constant<size_t, 3> /*meta*/) noexcept {
650  std::tie(box_, terminate_, algorithm_step_) =
651  this_action::apply(my_box, inboxes_, *global_cache_,
652  std::as_const(array_index_), actions_list{},
654  });
655 
656  // `check_if_ready` calls the `is_ready` static method on the action
657  // `action` if it has one, otherwise returns true. The first argument is the
658  // ```
659  // Algorithm_detail::is_is_ready_callable_t<action, databox,
660  // tuples::tagged_tuple_from_typelist<inbox_tags_list>,
661  // Parallel::GlobalCache<metavariables>, array_index>{}
662  // ```
663  const auto check_if_ready = make_overloader(
664  [this](std::true_type /*has_is_ready*/, auto action,
665  const auto& check_local_box) noexcept {
666  return decltype(action)::is_ready(
667  check_local_box, std::as_const(inboxes_), *global_cache_,
668  std::as_const(array_index_));
669  },
670  [](std::false_type /*has_is_ready*/, auto /*action*/,
671  const auto& /*box*/) noexcept { return true; });
672 
673  constexpr size_t phase_index =
674  tmpl::index_of<phase_dependent_action_lists, PhaseDepActions>::value;
675  using databox_phase_type = tmpl::at_c<databox_phase_types, phase_index>;
676  using databox_types_this_phase = typename databox_phase_type::databox_types;
677 
678  const auto display_databox_error = [this](const size_t line_number,
679  auto first_type,
680  auto... types) noexcept {
681  ERROR("The DataBox type being retrieved at algorithm step: "
682  << algorithm_step_ << " in phase " << phase_index
683  << " corresponding to action "
684  << pretty_type::get_name<this_action>() << " on line "
685  << line_number
686  << " is not the correct type but is of variant index "
687  << box_.which()
688  << ". If you are using Goto and Label actions then you are using "
689  "them incorrectly. \nValid DataBox Types: \n "
690  << pretty_type::get_name<typename decltype(first_type)::type>()
691  << (MakeString{} << ",\n "
692  << ...
693  << pretty_type::get_name<typename decltype(types)::type>())
694  << "\nVariant type:\n " << pretty_type::get_name<variant_boxes>());
695  };
696 
697  // The overload separately handles the first action in the phase from the
698  // remaining actions. The reason for this is that the first action can have
699  // as its input DataBox either the output of the last action in the phase or
700  // the output of the last action in the *previous* phase. This is handled by
701  // checking which DataBox is currently in the `boost::variant` (using the
702  // call `box_.which()`).
704  // clang-format off
705  [this, &take_next_action, &check_if_ready, &invoke_this_action,
706  &display_databox_error](auto current_iter) noexcept
708  decltype(current_iter)>::value> {
709  // clang-format on
710  // When `algorithm_step_ == 0` we could be the first DataBox or
711  // the last Databox.
712  using first_databox = tmpl::at_c<databox_types_this_phase, 0>;
713  using last_databox =
714  tmpl::at_c<databox_types_this_phase,
715  tmpl::size<databox_types_this_phase>::value - 1>;
716  using local_this_action =
717  tmpl::at_c<actions_list, decltype(current_iter)::value>;
718  if (box_.which() ==
719  static_cast<int>(
720  tmpl::index_of<variant_boxes, first_databox>::value)) {
721  using this_databox = first_databox;
722  auto& box = boost::get<this_databox>(box_);
723  if (not check_if_ready(
724  Algorithm_detail::is_is_ready_callable_t<
725  local_this_action, this_databox,
726  tuples::tagged_tuple_from_typelist<inbox_tags_list>,
728  array_index>{},
729  local_this_action{}, box)) {
730  take_next_action = false;
731  return nullptr;
732  }
733  performing_action_ = true;
734  algorithm_step_++;
735  invoke_this_action(
736  box,
737  typename std::tuple_size<decltype(local_this_action::apply(
738  box, inboxes_, *global_cache_,
739  std::as_const(array_index_), actions_list{},
741  } else if (box_.which() ==
742  static_cast<int>(
743  tmpl::index_of<variant_boxes,
744  last_databox>::value)) {
745  using this_databox = last_databox;
746  auto& box = boost::get<this_databox>(box_);
747  if (not check_if_ready(
748  Algorithm_detail::is_is_ready_callable_t<
749  local_this_action, this_databox,
750  tuples::tagged_tuple_from_typelist<inbox_tags_list>,
752  array_index>{},
753  local_this_action{}, box)) {
754  take_next_action = false;
755  return nullptr;
756  }
757  performing_action_ = true;
758  algorithm_step_++;
759  invoke_this_action(
760  box,
761  typename std::tuple_size<decltype(local_this_action::apply(
762  box, inboxes_, *global_cache_,
763  std::as_const(array_index_), actions_list{},
765  } else {
766  display_databox_error(__LINE__, tmpl::type_<first_databox>{},
767  tmpl::type_<last_databox>{});
768  }
769  return nullptr;
770  },
771  // clang-format off
772  [
773  this, &take_next_action, &check_if_ready, &invoke_this_action, &
774  display_databox_error
775  ](auto current_iter) noexcept
777  decltype(current_iter)>::value> {
778  // clang-format on
779  // When `algorithm_step_ != 0` we must be the DataBox of the
780  // action before this action.
781  using this_databox = tmpl::at_c<databox_types_this_phase,
782  decltype(current_iter)::value>;
783  using local_this_action =
784  tmpl::at_c<actions_list, decltype(current_iter)::value>;
785  if (box_.which() ==
786  static_cast<int>(
787  tmpl::index_of<variant_boxes, this_databox>::value)) {
788  auto& box = boost::get<this_databox>(box_);
789  if (not check_if_ready(
790  Algorithm_detail::is_is_ready_callable_t<
791  local_this_action, this_databox,
792  tuples::tagged_tuple_from_typelist<inbox_tags_list>,
794  array_index>{},
795  local_this_action{}, box)) {
796  take_next_action = false;
797  return nullptr;
798  }
799  performing_action_ = true;
800  algorithm_step_++;
801  invoke_this_action(
802  box,
803  typename std::tuple_size<decltype(local_this_action::apply(
804  box, inboxes_, *global_cache_,
805  std::as_const(array_index_), actions_list{},
807  } else {
808  display_databox_error(__LINE__, tmpl::type_<this_databox>{});
809  }
810  return nullptr;
812  performing_action_ = false;
813  // Wrap counter if necessary
814  if (algorithm_step_ >= tmpl::size<actions_list>::value) {
815  algorithm_step_ = 0;
816  }
817  };
818  // In case of no Actions avoid compiler warning.
819  (void)helper;
820  // This is a template for loop for Is
822  return take_next_action;
823 }
824 } // namespace Parallel
NoSuchType
Used to mark "no type" or "bad state" for metaprogramming.
Definition: NoSuchType.hpp:10
std::is_same
std::integral_constant
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::inbox_tags_list
Parallel::get_inbox_tags< all_actions_list > inbox_tags_list
List off all the Tags that can be received into the Inbox.
Definition: Algorithm.hpp:143
EXPAND_PACK_LEFT_TO_RIGHT
#define EXPAND_PACK_LEFT_TO_RIGHT(...)
Expand a parameter pack evaluating the terms from left to right.
Definition: TMPL.hpp:563
utility
exception
make_overloader
Overloader< Fs... > make_overloader(Fs... fs)
Create Overloader<Fs...>, see Overloader for details.
Definition: Overloader.hpp:109
UNLIKELY
#define UNLIKELY(x)
Definition: Gsl.hpp:73
Parallel::GlobalCache< metavariables >
db::compute_databox_type
typename detail::compute_dbox_type< get_items< TagList >, get_compute_items< TagList > >::type compute_databox_type
Returns the type of the DataBox that would be constructed from the TagList of tags.
Definition: DataBox.hpp:1044
Parallel::is_node_group_proxy
Definition: TypeTraits.hpp:34
unordered_set
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::cbase_type
typename chare_type::template cbase< parallel_component, array_index > cbase_type
The Charm++ base object type.
Definition: Algorithm.hpp:157
GlobalCache.hpp
std::make_index_sequence
std::set_terminate
T set_terminate(T... args)
Error.hpp
PrettyType.hpp
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::cproxy_type
typename chare_type::template cproxy< parallel_component, array_index > cproxy_type
The Charm++ proxy object type.
Definition: Algorithm.hpp:154
db::AddComputeTags
tmpl::flatten< tmpl::list< Tags... > > AddComputeTags
List of Compute Item Tags to add to the DataBox.
Definition: DataBox.hpp:1006
ParallelInfo.hpp
BoostHelpers.hpp
tuple
std::as_const
T as_const(T... args)
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::chare_type
typename parallel_component::chare_type chare_type
The type of the Chare.
Definition: Algorithm.hpp:151
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::all_actions_list
tmpl::flatten< tmpl::list< typename PhaseDepActionListsPack::action_list... > > all_actions_list
List of Actions in the order that generates the DataBox types.
Definition: Algorithm.hpp:139
Parallel::get_inbox_tags
tmpl::remove_duplicates< tmpl::join< tmpl::transform< ActionsList, 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:31
Parallel::NodeLock
A typesafe wrapper for a lock for synchronization of shared resources on a given node,...
Definition: NodeLock.hpp:25
ERROR
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:36
db::create
constexpr auto create(Args &&... args)
Create a new DataBox.
Definition: DataBox.hpp:1068
std::add_pointer_t
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::array_index
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:147
ActionTesting::is_ready
bool is_ready(MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index) noexcept
Runs the is_ready function and returns the result for the next action in the current phase on the arr...
Definition: MockRuntimeSystemFreeFunctions.hpp:155
ActionTesting::get_terminate
bool get_terminate(const MockRuntimeSystem< Metavariables > &runner, const typename Component::array_index &array_index) noexcept
Returns whether or not the Component with index array_index has been terminated.
Definition: MockRuntimeSystemFreeFunctions.hpp:221
DataBox.hpp
Parallel::charmxx::RegisterReceiveData
Derived class for registering receive_data functions.
Definition: CharmRegistration.hpp:420
cstddef
pretty_type::get_name
std::string get_name()
Returns a string with the prettiest typename known for the type T.
Definition: PrettyType.hpp:677
Assert.hpp
make_boost_variant_over
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:43
tuples::TaggedTuple
An associative container that is indexed by structs.
Definition: TaggedTuple.hpp:271
Parallel::charmxx::RegisterThreadedAction
Derived class for registering threaded actions.
Definition: CharmRegistration.hpp:296
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::PhaseType
typename tmpl::front< tmpl::list< PhaseDepActionListsPack... > >::phase_type PhaseType
The type of the phases.
Definition: Algorithm.hpp:160
std::decay_t
PupStlCpp11.hpp
sys::wall_time
double wall_time()
The current wall time in seconds.
Definition: ParallelInfo.hpp:92
db::AddSimpleTags
tmpl::flatten< tmpl::list< Tags... > > AddSimpleTags
List of Tags to add to the DataBox.
Definition: DataBox.hpp:985
Parallel::receive_data
void receive_data(Proxy &&proxy, typename ReceiveTag::temporal_id temporal_id, ReceiveDataType &&receive_data, const bool enable_if_disabled=false) noexcept
Send the data args... to the algorithm running on proxy, and tag the message with the identifier temp...
Definition: Invoke.hpp:49
Gsl.hpp
TypeTraits.hpp
Parallel::charmxx::RegisterParallelComponent
Derived class for registering parallel components.
Definition: CharmRegistration.hpp:130
ForceInline.hpp
Requires.hpp
db::wrap_tags_in
tmpl::transform< TagList, tmpl::bind< Wrapper, tmpl::_1, tmpl::pin< Args >... > > wrap_tags_in
Create a new tmpl::list of tags by wrapping each tag in TagList in Wrapper<_, Args....
Definition: PrefixHelpers.hpp:30
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::get_terminate
constexpr bool get_terminate() const noexcept
Check if an algorithm should continue being evaluated.
Definition: Algorithm.hpp:308
std::size_t
Parallel::simple_action
void simple_action(Proxy &&proxy) noexcept
Invoke a simple action on proxy
Definition: Invoke.hpp:84
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::metavariables
typename ParallelComponent::metavariables metavariables
The metavariables class passed to the Algorithm.
Definition: Algorithm.hpp:141
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
Parallel::charmxx::RegisterSimpleAction
Derived class for registering simple actions.
Definition: CharmRegistration.hpp:220
ostream
Parallel::charmxx::RegisterReductionAction
Derived class for registering reduction actions.
Definition: CharmRegistration.hpp:464
TMPL.hpp
Parallel::AlgorithmImpl< ParallelComponent, tmpl::list< PhaseDepActionListsPack... > >::set_terminate
constexpr void set_terminate(const bool t) noexcept
Tell the Algorithm it should no longer execute the algorithm. This does not mean that the execution o...
Definition: Algorithm.hpp:305
Parallel::threaded_action
void threaded_action(Proxy &&proxy) noexcept
Invoke a threaded action on proxy, where the proxy must be a nodegroup.
Definition: Invoke.hpp:103
MakeString
Make a string by streaming into object.
Definition: MakeString.hpp:18
gsl::not_null
Require a pointer to not be a nullptr
Definition: ReadSpecThirdOrderPiecewisePolynomial.hpp:13
initializer_list