MockRuntimeSystem.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <algorithm>
7 #include <cstddef>
8 #include <tuple>
9 #include <unordered_map>
10 #include <unordered_set>
11 #include <utility>
12 
14 #include "Parallel/GlobalCache.hpp"
15 #include "Parallel/ParallelComponentHelpers.hpp"
17 #include "Utilities/TMPL.hpp"
18 #include "Utilities/TaggedTuple.hpp"
19 #include "Utilities/TypeTraits.hpp"
20 #include "Utilities/TypeTraits/IsA.hpp"
21 
22 /// \cond
23 namespace ActionTesting {
24 template <typename Component>
25 class MockDistributedObject;
26 template <typename SimpleTagsList, typename ComputeTagsList = tmpl::list<>>
27 struct InitializeDataBox;
28 } // namespace ActionTesting
29 /// \endcond
30 
31 namespace ActionTesting {
32 
33 namespace detail {
34 // `get_initialization` computes the type of the TaggedTuple that holds the
35 // initial simple tags for the DataBox, as well as the
36 // `initialize_databox_action` so that the user interface to the Action Testing
37 // does not require the user to redundantly specify this information.
38 // `get_initialization` also performs various sanity checks for the user.
39 template <typename Component>
40 struct get_initialization {
41  using phase = typename Component::metavariables::Phase;
42 
43  template <typename T>
44  struct is_initialization_phase {
45  using type =
47  };
48 
49  using initialization_pdal_list =
50  tmpl::filter<typename Component::phase_dependent_action_list,
51  is_initialization_phase<tmpl::_1>>;
52  static_assert(
53  tmpl::size<initialization_pdal_list>::value == 1,
54  "Must have exactly one Initialization PhaseDependentActionList");
55 
56  using initialization_pdal = typename tmpl::front<initialization_pdal_list>;
57 
58  static_assert(
59  tt::is_a_v<ActionTesting::InitializeDataBox,
60  tmpl::front<typename initialization_pdal::action_list>>,
61  "The first action in the initialization phase must be "
62  "ActionTesting::InitializeDataBox, even if the simple and "
63  "compute tags are empty.");
64 
65  using initialize_databox_action =
66  tmpl::front<typename initialization_pdal::action_list>;
67 
68  using InitialValues = typename initialize_databox_action::InitialValues;
69 };
70 
71 // Checks whether or not the `Metavariables` has a `Phase::Initialization`.
72 template <typename Metavariables, typename = std::void_t<>>
73 struct has_initialization_phase : std::false_type {};
74 
75 template <typename Metavariables>
76 struct has_initialization_phase<
77  Metavariables, std::void_t<decltype(Metavariables::Phase::Initialization)>>
78  : std::true_type {};
79 
80 template <typename Metavariables>
81 constexpr bool has_initialization_phase_v =
82  has_initialization_phase<Metavariables>::value;
83 } // namespace detail
84 
85 /// \ingroup TestingFrameworkGroup
86 /// A class that mocks the infrastructure needed to run actions. It simulates
87 /// message passing using the inbox infrastructure and handles most of the
88 /// arguments to the apply and is_ready action methods. This mocks the Charm++
89 /// runtime system as well as the layer built on top of it as part of SpECTRE.
90 template <typename Metavariables>
92  public:
93  // No moving, since MockCollectionOfDistributedObjectsProxy holds a pointer to
94  // us.
95  MockRuntimeSystem(const MockRuntimeSystem&) = delete;
97  MockRuntimeSystem& operator=(const MockRuntimeSystem&) = delete;
98  MockRuntimeSystem& operator=(MockRuntimeSystem&&) = delete;
99  ~MockRuntimeSystem() = default;
100 
101  template <typename Component>
102  struct InboxesTag {
103  using type = std::unordered_map<
104  typename Component::array_index,
105  tuples::tagged_tuple_from_typelist<
106  typename MockDistributedObject<Component>::inbox_tags_list>>;
107  };
108 
109  template <typename Component>
111  using type = std::unordered_map<typename Component::array_index,
113  };
114 
116  using CacheTuple = tuples::tagged_tuple_from_typelist<
118  using MutableCacheTuple = tuples::tagged_tuple_from_typelist<
120 
121  using mock_objects_tags =
122  tmpl::transform<typename Metavariables::component_list,
123  tmpl::bind<MockDistributedObjectsTag, tmpl::_1>>;
124  using CollectionOfMockDistributedObjects =
125  tuples::tagged_tuple_from_typelist<mock_objects_tags>;
126  using Inboxes = tuples::tagged_tuple_from_typelist<
127  tmpl::transform<typename Metavariables::component_list,
128  tmpl::bind<InboxesTag, tmpl::_1>>>;
129 
130 
131  /// Construct from the tuple of GlobalCache objects.
132  explicit MockRuntimeSystem(CacheTuple cache_contents,
133  MutableCacheTuple mutable_cache_contents = {})
134  : mutable_cache_(serialize_and_deserialize(mutable_cache_contents)),
135  // serialize_and_deserialize is not necessary here, but we
136  // serialize_and_deserialize to reveal bugs in ActionTesting
137  // tests where cache contents are not serializable.
138  cache_(serialize_and_deserialize(cache_contents), &mutable_cache_)
139  {
140  tmpl::for_each<typename Metavariables::component_list>(
141  [this](auto component) {
142  using Component = tmpl::type_from<decltype(component)>;
143  Parallel::get_parallel_component<Component>(cache_).set_data(
144  &tuples::get<MockDistributedObjectsTag<Component>>(
145  mock_distributed_objects_),
146  &tuples::get<InboxesTag<Component>>(inboxes_));
147  });
148  }
149 
150  /// Construct from the tuple of GlobalCache objects that might
151  /// be in a different order.
152  template <typename... Tags>
155  tuples::reorder<CacheTuple>(std::move(cache_contents))) {}
156 
157  /// Construct from the tuple of GlobalCache and MutableGlobalCache
158  /// objects that might be in a different order.
159  template <typename... CacheTags, typename... MutableCacheTags>
161  tuples::TaggedTuple<CacheTags...> cache_contents,
162  tuples::TaggedTuple<MutableCacheTags...> mutable_cache_contents)
164  tuples::reorder<CacheTuple>(std::move(cache_contents)),
165  tuples::reorder<MutableCacheTuple>(
166  std::move(mutable_cache_contents))) {}
167 
168  /// Emplace a component that does not need to be initialized.
169  template <typename Component, typename... Options>
170  void emplace_component(const typename Component::array_index& array_index,
171  Options&&... opts) noexcept {
172  mock_distributed_objects<Component>().emplace(
173  array_index,
175  array_index, &cache_,
176  &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),
177  std::forward<Options>(opts)...));
178  }
179 
180  /// Emplace a component that needs to be initialized.
181  template <typename Component, typename... Options,
182  typename Metavars = Metavariables,
185  const typename Component::array_index& array_index,
186  const typename detail::get_initialization<Component>::InitialValues&
187  initial_values,
188  Options&&... opts) noexcept {
189  detail::get_initialization<Component>::initialize_databox_action::
190  set_initial_values(initial_values);
191  auto iterator_bool = mock_distributed_objects<Component>().emplace(
192  array_index,
194  array_index, &cache_,
195  &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),
196  std::forward<Options>(opts)...));
197  if (not iterator_bool.second) {
198  ERROR("Failed to insert parallel component '"
199  << pretty_type::get_name<Component>() << "' with index "
200  << array_index);
201  }
202  iterator_bool.first->second.set_phase(Metavariables::Phase::Initialization);
203  iterator_bool.first->second.next_action();
204  }
205 
206  // @{
207  /// Invoke the simple action `Action` on the `Component` labeled by
208  /// `array_index` immediately.
209  template <typename Component, typename Action, typename Arg0,
210  typename... Args>
211  void simple_action(const typename Component::array_index& array_index,
212  Arg0&& arg0, Args&&... args) noexcept {
213  mock_distributed_objects<Component>()
214  .at(array_index)
215  .template simple_action<Action>(
216  std::make_tuple(std::forward<Arg0>(arg0),
217  std::forward<Args>(args)...),
218  true);
219  }
220 
221  template <typename Component, typename Action>
222  void simple_action(
223  const typename Component::array_index& array_index) noexcept {
224  mock_distributed_objects<Component>()
225  .at(array_index)
226  .template simple_action<Action>(true);
227  }
228  // @}
229 
230  // @{
231  /// Invoke the threaded action `Action` on the `Component` labeled by
232  /// `array_index` immediately.
233  template <typename Component, typename Action, typename Arg0,
234  typename... Args>
235  void threaded_action(const typename Component::array_index& array_index,
236  Arg0&& arg0, Args&&... args) noexcept {
237  mock_distributed_objects<Component>()
238  .at(array_index)
239  .template threaded_action<Action>(
240  std::make_tuple(std::forward<Arg0>(arg0),
241  std::forward<Args>(args)...),
242  true);
243  }
244 
245  template <typename Component, typename Action>
246  void threaded_action(
247  const typename Component::array_index& array_index) noexcept {
248  mock_distributed_objects<Component>()
249  .at(array_index)
250  .template threaded_action<Action>(true);
251  }
252  // @}
253 
254  /// Return true if there are no queued simple actions on the
255  /// `Component` labeled by `array_index`.
256  template <typename Component>
258  const typename Component::array_index& array_index) const noexcept {
259  return mock_distributed_objects<Component>()
260  .at(array_index)
261  .is_simple_action_queue_empty();
262  }
263 
264  /// Invoke the next queued simple action on the `Component` labeled by
265  /// `array_index`.
266  template <typename Component>
268  const typename Component::array_index& array_index) noexcept {
269  mock_distributed_objects<Component>()
270  .at(array_index)
271  .invoke_queued_simple_action();
272  }
273 
274  /// Return true if there are no queued threaded actions on the
275  /// `Component` labeled by `array_index`.
276  template <typename Component>
278  const typename Component::array_index& array_index) const noexcept {
279  return mock_distributed_objects<Component>()
280  .at(array_index)
281  .is_threaded_action_queue_empty();
282  }
283 
284  /// Invoke the next queued threaded action on the `Component` labeled by
285  /// `array_index`.
286  template <typename Component>
288  const typename Component::array_index& array_index) noexcept {
289  mock_distributed_objects<Component>()
290  .at(array_index)
291  .invoke_queued_threaded_action();
292  }
293 
294  /// Instead of the next call to `next_action` applying the next action in
295  /// the action list, force the next action to be `Action`
296  template <typename Component, typename Action>
298  const typename Component::array_index& array_index) noexcept {
299  static_assert(
300  tmpl::list_contains_v<
301  typename MockDistributedObject<Component>::all_actions_list,
302  Action>,
303  "Cannot force a next action that is not in the action list of the "
304  "parallel component. See the first template parameter of "
305  "'force_next_action_to_be' for the component and the second template "
306  "parameter for the action.");
307  bool found_matching_phase = false;
308  const auto invoke_for_phase =
309  [this, &array_index, &found_matching_phase](auto phase_dep_v) noexcept {
310  using PhaseDep = decltype(phase_dep_v);
311  constexpr typename Metavariables::Phase phase = PhaseDep::type::phase;
312  using actions_list = typename PhaseDep::type::action_list;
313  auto& distributed_object =
314  this->mock_distributed_objects<Component>().at(array_index);
315  if (distributed_object.get_phase() == phase) {
316  found_matching_phase = true;
317  distributed_object.force_next_action_to_be(
318  tmpl::conditional_t<
319  std::is_same<tmpl::no_such_type_,
320  tmpl::index_of<actions_list, Action>>::value,
322  tmpl::index_of<actions_list, Action>>::value);
323  }
324  };
325  tmpl::for_each<typename MockDistributedObject<
326  Component>::phase_dependent_action_lists>(invoke_for_phase);
327  if (not found_matching_phase) {
328  ERROR(
329  "Could not find any actions in the current phase for the component '"
330  << pretty_type::short_name<Component>()
331  << "'. Maybe you are not in the phase you expected to be in? The "
332  "integer value corresponding to the current phase is "
333  << static_cast<int>(this->mock_distributed_objects<Component>()
334  .at(array_index)
335  .get_phase()));
336  }
337  }
338 
339  /// Obtain the index into the action list of the next action.
340  template <typename Component>
342  const typename Component::array_index& array_index) const noexcept {
343  return mock_distributed_objects<Component>()
344  .at(array_index)
345  .get_next_action_index();
346  }
347 
348  /// Invoke the next action in the ActionList on the parallel component
349  /// `Component` on the component labeled by `array_index`.
350  template <typename Component>
352  const typename Component::array_index& array_index) noexcept {
353  mock_distributed_objects<Component>().at(array_index).next_action();
354  }
355 
356  /// Call is_ready on the next action in the action list as if on the portion
357  /// of Component labeled by array_index.
358  template <typename Component>
359  bool is_ready(const typename Component::array_index& array_index) noexcept {
360  return mock_distributed_objects<Component>().at(array_index).is_ready();
361  }
362 
363  // @{
364  /// Access the inboxes for a given component.
365  template <typename Component>
366  auto inboxes() noexcept -> std::unordered_map<
367  typename Component::array_index,
368  tuples::tagged_tuple_from_typelist<
369  typename MockDistributedObject<Component>::inbox_tags_list>>& {
370  return tuples::get<InboxesTag<Component>>(inboxes_);
371  }
372 
373  template <typename Component>
374  auto inboxes() const noexcept -> const std::unordered_map<
375  typename Component::array_index,
376  tuples::tagged_tuple_from_typelist<
377  typename MockDistributedObject<Component>::inbox_tags_list>>& {
378  return tuples::get<InboxesTag<Component>>(inboxes_);
379  }
380  // @}
381 
382  /// Find the set of array indices on Component where the specified
383  /// inbox is not empty.
384  template <typename Component, typename InboxTag>
385  auto nonempty_inboxes() noexcept
386  -> std::unordered_set<typename Component::array_index> {
388  for (const auto& element_box : inboxes<Component>()) {
389  if (not tuples::get<InboxTag>(element_box.second).empty()) {
390  result.insert(element_box.first);
391  }
392  }
393  return result;
394  }
395 
396  /// Access the mocked distributed objects for a component, indexed by array
397  /// index.
398  template <typename Component>
399  auto& mock_distributed_objects() noexcept {
400  return tuples::get<MockDistributedObjectsTag<Component>>(
401  mock_distributed_objects_);
402  }
403 
404  template <typename Component>
405  const auto& mock_distributed_objects() const noexcept {
406  return tuples::get<MockDistributedObjectsTag<Component>>(
407  mock_distributed_objects_);
408  }
409 
410  GlobalCache& cache() noexcept { return cache_; }
411 
412  /// Set the phase of all parallel components to `next_phase`
413  void set_phase(const typename Metavariables::Phase next_phase) noexcept {
414  tmpl::for_each<mock_objects_tags>(
415  [this, &next_phase](auto component_v) noexcept {
416  for (auto& object : tuples::get<typename decltype(component_v)::type>(
417  mock_distributed_objects_)) {
418  object.second.set_phase(next_phase);
419  }
420  });
421  }
422 
423  private:
425  GlobalCache cache_{};
426  Inboxes inboxes_{};
427  CollectionOfMockDistributedObjects mock_distributed_objects_{};
428 };
429 } // namespace ActionTesting
ActionTesting::MockRuntimeSystem::emplace_component
void emplace_component(const typename Component::array_index &array_index, Options &&... opts) noexcept
Emplace a component that does not need to be initialized.
Definition: MockRuntimeSystem.hpp:170
ActionTesting::MockRuntimeSystem::is_simple_action_queue_empty
bool is_simple_action_queue_empty(const typename Component::array_index &array_index) const noexcept
Return true if there are no queued simple actions on the Component labeled by array_index.
Definition: MockRuntimeSystem.hpp:257
ActionTesting::MockRuntimeSystem::invoke_queued_simple_action
void invoke_queued_simple_action(const typename Component::array_index &array_index) noexcept
Invoke the next queued simple action on the Component labeled by array_index.
Definition: MockRuntimeSystem.hpp:267
ActionTesting::MockRuntimeSystem::MockDistributedObjectsTag
Definition: MockRuntimeSystem.hpp:110
ActionTesting::MockRuntimeSystem::invoke_queued_threaded_action
void invoke_queued_threaded_action(const typename Component::array_index &array_index) noexcept
Invoke the next queued threaded action on the Component labeled by array_index.
Definition: MockRuntimeSystem.hpp:287
gsl::at
constexpr T & at(std::array< T, N > &arr, Size index)
Retrieve a entry from a container, with checks in Debug mode that the index being retrieved is valid.
Definition: Gsl.hpp:125
std::is_same
ActionTesting::MockRuntimeSystem::set_phase
void set_phase(const typename Metavariables::Phase next_phase) noexcept
Set the phase of all parallel components to next_phase
Definition: MockRuntimeSystem.hpp:413
std::integral_constant
utility
ActionTesting::MockRuntimeSystem::nonempty_inboxes
auto nonempty_inboxes() noexcept -> std::unordered_set< typename Component::array_index >
Find the set of array indices on Component where the specified inbox is not empty.
Definition: MockRuntimeSystem.hpp:385
ActionTesting::MockRuntimeSystem
Definition: MockRuntimeSystem.hpp:91
get
constexpr Tag::type & get(Variables< TagList > &v) noexcept
Return Tag::type pointing into the contiguous array.
Definition: Variables.hpp:638
Parallel::GlobalCache
Definition: ElementReceiveInterpPoints.hpp:15
ActionTesting::MockRuntimeSystem::is_threaded_action_queue_empty
bool is_threaded_action_queue_empty(const typename Component::array_index &array_index) const noexcept
Return true if there are no queued threaded actions on the Component labeled by array_index.
Definition: MockRuntimeSystem.hpp:277
ActionTesting
Structures used for mocking the parallel components framework in order to test actions.
Definition: ActionTesting.hpp:308
unordered_set
ActionTesting::MockRuntimeSystem::next_action
void next_action(const typename Component::array_index &array_index) noexcept
Invoke the next action in the ActionList on the parallel component Component on the component labeled...
Definition: MockRuntimeSystem.hpp:351
GlobalCache.hpp
Error.hpp
ActionTesting::MockRuntimeSystem::force_next_action_to_be
void force_next_action_to_be(const typename Component::array_index &array_index) noexcept
Instead of the next call to next_action applying the next action in the action list,...
Definition: MockRuntimeSystem.hpp:297
tt::is_a_v
constexpr bool is_a_v
Definition: IsA.hpp:50
ActionTesting::MockRuntimeSystem::inboxes
auto inboxes() noexcept -> std::unordered_map< typename Component::array_index, tuples::tagged_tuple_from_typelist< typename MockDistributedObject< Component >::inbox_tags_list >> &
Access the inboxes for a given component.
Definition: MockRuntimeSystem.hpp:366
tuple
algorithm
Parallel::MutableGlobalCache
Definition: GlobalCache.hpp:101
TestHelpers.hpp
ActionTesting::MockRuntimeSystem::MockRuntimeSystem
MockRuntimeSystem(tuples::TaggedTuple< Tags... > cache_contents)
Construct from the tuple of GlobalCache objects that might be in a different order.
Definition: MockRuntimeSystem.hpp:153
ERROR
#define ERROR(m)
prints an error message to the standard error stream and aborts the program.
Definition: Error.hpp:36
Options
Utilities for parsing input files.
Definition: MinmodType.hpp:8
ActionTesting::MockRuntimeSystem::simple_action
void simple_action(const typename Component::array_index &array_index, Arg0 &&arg0, Args &&... args) noexcept
Invoke the simple action Action on the Component labeled by array_index immediately.
Definition: MockRuntimeSystem.hpp:211
cstddef
ActionTesting::MockRuntimeSystem::InboxesTag
Definition: MockRuntimeSystem.hpp:102
ActionTesting::MockRuntimeSystem::MockRuntimeSystem
MockRuntimeSystem(tuples::TaggedTuple< CacheTags... > cache_contents, tuples::TaggedTuple< MutableCacheTags... > mutable_cache_contents)
Construct from the tuple of GlobalCache and MutableGlobalCache objects that might be in a different o...
Definition: MockRuntimeSystem.hpp:160
ActionTesting::MockDistributedObject
MockDistributedObject mocks the AlgorithmImpl class. It should not be considered as part of the user ...
Definition: MockDistributedObject.hpp:139
tuples::TaggedTuple< Tags... >
serialize_and_deserialize
T serialize_and_deserialize(const T &t)
Serializes and deserializes an object t of type T
Definition: TestHelpers.hpp:45
Parallel::get_const_global_cache_tags
tmpl::remove_duplicates< tmpl::flatten< tmpl::list< typename detail::get_const_global_cache_tags_from_parallel_struct< Metavariables >::type, tmpl::transform< typename Metavariables::component_list, detail::get_const_global_cache_tags_from_parallel_struct< tmpl::_1 > >, tmpl::transform< typename Metavariables::component_list, detail::get_const_global_cache_tags_from_pdal< tmpl::_1 > >> >> get_const_global_cache_tags
Given the metavariables, get a list of the unique tags that will specify the items in the GlobalCache...
Definition: ParallelComponentHelpers.hpp:116
ActionTesting::MockRuntimeSystem::threaded_action
void threaded_action(const typename Component::array_index &array_index, Arg0 &&arg0, Args &&... args) noexcept
Invoke the threaded action Action on the Component labeled by array_index immediately.
Definition: MockRuntimeSystem.hpp:235
ActionTesting::MockRuntimeSystem::emplace_component_and_initialize
void emplace_component_and_initialize(const typename Component::array_index &array_index, const typename detail::get_initialization< Component >::InitialValues &initial_values, Options &&... opts) noexcept
Emplace a component that needs to be initialized.
Definition: MockRuntimeSystem.hpp:184
ActionTesting::MockRuntimeSystem::mock_distributed_objects
auto & mock_distributed_objects() noexcept
Access the mocked distributed objects for a component, indexed by array index.
Definition: MockRuntimeSystem.hpp:399
Parallel::get_mutable_global_cache_tags
tmpl::remove_duplicates< tmpl::flatten< tmpl::list< typename detail::get_mutable_global_cache_tags_from_parallel_struct< Metavariables >::type, tmpl::transform< typename Metavariables::component_list, detail::get_mutable_global_cache_tags_from_parallel_struct< tmpl::_1 > >, tmpl::transform< typename Metavariables::component_list, detail::get_mutable_global_cache_tags_from_pdal< tmpl::_1 > >> >> get_mutable_global_cache_tags
Given the metavariables, get a list of the unique tags that will specify the mutable items in the Glo...
Definition: ParallelComponentHelpers.hpp:134
ActionTesting::MockRuntimeSystem::is_ready
bool is_ready(const typename Component::array_index &array_index) noexcept
Call is_ready on the next action in the action list as if on the portion of Component labeled by arra...
Definition: MockRuntimeSystem.hpp:359
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
unordered_map
ActionTesting::MockRuntimeSystem::get_next_action_index
size_t get_next_action_index(const typename Component::array_index &array_index) const noexcept
Obtain the index into the action list of the next action.
Definition: MockRuntimeSystem.hpp:341
TMPL.hpp
ActionTesting::MockRuntimeSystem::MockRuntimeSystem
MockRuntimeSystem(CacheTuple cache_contents, MutableCacheTuple mutable_cache_contents={})
Construct from the tuple of GlobalCache objects.
Definition: MockRuntimeSystem.hpp:132