Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Defines classes and functions used for manipulating DataBox's
6 :
7 : #pragma once
8 :
9 : #include <cstddef>
10 : #include <exception>
11 : #include <functional>
12 : #include <initializer_list>
13 : #include <map>
14 : #include <ostream>
15 : #include <pup.h>
16 : #include <sstream>
17 : #include <string>
18 : #include <tuple>
19 : #include <unordered_map>
20 : #include <utility>
21 :
22 : #include "DataStructures/DataBox/Access.hpp"
23 : #include "DataStructures/DataBox/DataBoxTag.hpp"
24 : #include "DataStructures/DataBox/IsApplyCallable.hpp"
25 : #include "DataStructures/DataBox/Item.hpp"
26 : #include "DataStructures/DataBox/SubitemTag.hpp"
27 : #include "DataStructures/DataBox/Subitems.hpp"
28 : #include "DataStructures/DataBox/TagName.hpp"
29 : #include "DataStructures/DataBox/TagTraits.hpp"
30 : #include "Utilities/CleanupRoutine.hpp"
31 : #include "Utilities/ErrorHandling/Assert.hpp"
32 : #include "Utilities/ErrorHandling/Error.hpp"
33 : #include "Utilities/ErrorHandling/StaticAssert.hpp"
34 : #include "Utilities/ForceInline.hpp"
35 : #include "Utilities/Gsl.hpp"
36 : #include "Utilities/NoSuchType.hpp"
37 : #include "Utilities/Overloader.hpp"
38 : #include "Utilities/PrintHelpers.hpp"
39 : #include "Utilities/Requires.hpp"
40 : #include "Utilities/Serialization/Serialize.hpp"
41 : #include "Utilities/TMPL.hpp"
42 : #include "Utilities/TaggedTuple.hpp"
43 : #include "Utilities/TypeTraits.hpp"
44 : #include "Utilities/TypeTraits/CreateIsCallable.hpp"
45 : #include "Utilities/TypeTraits/IsA.hpp"
46 : #include "Utilities/TypeTraits/IsCallable.hpp"
47 :
48 : // IWYU pragma: no_forward_declare brigand::get_destination
49 : // IWYU pragma: no_forward_declare brigand::get_source
50 :
51 : /*!
52 : * \ingroup DataBoxGroup
53 : * \brief Namespace for DataBox related things
54 : */
55 : namespace db {
56 : // Forward declarations
57 : /// \cond
58 : template <typename TagsList>
59 : class DataBox;
60 : /// \endcond
61 :
62 : /// @{
63 : /// \ingroup DataBoxGroup
64 : /// Equal to `true` if `Tag` can be retrieved from a `DataBox` of type
65 : /// `DataBoxType`.
66 : template <typename Tag, typename DataBoxType>
67 1 : using tag_is_retrievable =
68 : tmpl::or_<std::is_same<Tag, ::Tags::DataBox>,
69 : tmpl::any<typename DataBoxType::tags_list,
70 : std::is_base_of<tmpl::pin<Tag>, tmpl::_1>>>;
71 :
72 : template <typename Tag, typename DataBoxType>
73 1 : constexpr bool tag_is_retrievable_v =
74 : tag_is_retrievable<Tag, DataBoxType>::value;
75 : /// @}
76 :
77 : namespace detail {
78 : template <typename TagsList, typename Tag>
79 : struct get_simple_tag_for_base_tag {
80 : using type =
81 : tmpl::conditional_t<db::is_base_tag_v<Tag>,
82 : db::detail::first_matching_tag<TagsList, Tag>, Tag>;
83 : };
84 : } // namespace detail
85 :
86 : namespace detail {
87 : template <typename TagsList, typename Tag>
88 : struct creation_tag_impl {
89 : DEBUG_STATIC_ASSERT(
90 : not has_no_matching_tag_v<TagsList, Tag>,
91 : "Found no tags in the DataBox that match the tag being retrieved.");
92 : DEBUG_STATIC_ASSERT(
93 : has_unique_matching_tag_v<TagsList, Tag>,
94 : "Found more than one tag in the DataBox that matches the tag "
95 : "being retrieved. This happens because more than one tag with the same "
96 : "base (class) tag was added to the DataBox.");
97 : using normalized_tag = first_matching_tag<TagsList, Tag>;
98 : // A list with 0 or 1 elements. This uses `Tag` rather than
99 : // `normalized_tag` because subitems of compute tags normalize to
100 : // `Tags::Subitem<...>`, which is not what is in the `Subitems`
101 : // list.
102 : using parent_of_subitem = tmpl::filter<
103 : TagsList, tmpl::lazy::list_contains<Subitems<tmpl::_1>, tmpl::pin<Tag>>>;
104 : using type = tmpl::front<tmpl::push_back<parent_of_subitem, normalized_tag>>;
105 : };
106 : } // namespace detail
107 :
108 : /*!
109 : * \ingroup DataBoxGroup
110 : * \brief The tag added to \p Box referred to by \p Tag. This
111 : * resolves base tags and converts subitems to full items.
112 : */
113 : template <typename Tag, typename Box>
114 1 : using creation_tag =
115 : typename detail::creation_tag_impl<typename Box::tags_list, Tag>::type;
116 :
117 : namespace detail {
118 : template <typename Tag>
119 : using has_subitems =
120 : tmpl::not_<std::is_same<typename Subitems<Tag>::type, tmpl::list<>>>;
121 :
122 : template <typename Tag>
123 : constexpr bool has_subitems_v = has_subitems<Tag>::value;
124 :
125 : template <typename Tag, typename ParentTag>
126 : struct make_subitem_tag {
127 : using type = ::Tags::Subitem<Tag, ParentTag>;
128 : };
129 :
130 : template <typename Tag, typename = std::nullptr_t>
131 : struct append_subitem_tags {
132 : using type = tmpl::push_front<typename db::Subitems<Tag>::type, Tag>;
133 : };
134 :
135 : template <typename ParentTag>
136 : struct append_subitem_tags<ParentTag,
137 : Requires<has_subitems_v<ParentTag> and
138 : is_immutable_item_tag_v<ParentTag>>> {
139 : using type = tmpl::push_front<
140 : tmpl::transform<typename Subitems<ParentTag>::type,
141 : make_subitem_tag<tmpl::_1, tmpl::pin<ParentTag>>>,
142 : ParentTag>;
143 : };
144 :
145 : template <typename Tag>
146 : struct append_subitem_tags<Tag, Requires<not has_subitems_v<Tag>>> {
147 : using type = tmpl::list<Tag>;
148 : };
149 :
150 : template <typename TagsList>
151 : using expand_subitems =
152 : tmpl::flatten<tmpl::transform<TagsList, append_subitem_tags<tmpl::_1>>>;
153 :
154 : template <typename ComputeTag, typename ArgumentTag,
155 : typename FoundArgumentTagInBox>
156 : struct report_missing_compute_item_argument {
157 : static_assert(std::is_same_v<ComputeTag, void>,
158 : "A compute item's argument could not be found in the DataBox "
159 : "or was found multiple times. See the first template argument "
160 : "of report_missing_compute_item_argument for the compute item "
161 : "and the second for the missing (or duplicated) argument.");
162 : };
163 :
164 : template <typename ComputeTag, typename ArgumentTag>
165 : struct report_missing_compute_item_argument<ComputeTag, ArgumentTag,
166 : std::true_type> {
167 : using type = void;
168 : };
169 :
170 : template <typename TagsList, typename ComputeTag>
171 : struct create_compute_tag_argument_edges {
172 : #ifdef SPECTRE_DEBUG
173 : using argument_check_assertion = tmpl::transform<
174 : typename ComputeTag::argument_tags,
175 : report_missing_compute_item_argument<
176 : tmpl::pin<ComputeTag>, tmpl::_1,
177 : has_unique_matching_tag<tmpl::pin<TagsList>, tmpl::_1>>>;
178 : #endif // SPECTRE_DEBUG
179 : // These edges record that a compute item's value depends on the
180 : // values of it's arguments.
181 : using type = tmpl::transform<
182 : typename ComputeTag::argument_tags,
183 : tmpl::bind<tmpl::edge,
184 : tmpl::bind<first_matching_tag, tmpl::pin<TagsList>, tmpl::_1>,
185 : tmpl::pin<ComputeTag>>>;
186 : };
187 :
188 : template <typename TagsList, typename ImmutableItemTagsList>
189 : struct create_dependency_graph {
190 : using immutable_item_argument_edges =
191 : tmpl::join<tmpl::transform<ImmutableItemTagsList,
192 : detail::create_compute_tag_argument_edges<
193 : tmpl::pin<TagsList>, tmpl::_1>>>;
194 : using type = immutable_item_argument_edges;
195 : };
196 :
197 : // Get the base if it is present
198 : template <typename Tag, typename = std::void_t<>>
199 : struct get_base_impl {
200 : using type = Tag;
201 : };
202 : template <typename Tag>
203 : struct get_base_impl<Tag, std::void_t<typename Tag::base>> {
204 : using type = typename Tag::base;
205 : };
206 :
207 : template <typename Tag>
208 : using get_base = typename get_base_impl<Tag>::type;
209 : } // namespace detail
210 :
211 : /*!
212 : * \ingroup DataBoxGroup
213 : * \brief A DataBox stores objects that can be retrieved by using Tags
214 : * \warning
215 : * The order of the tags in DataBoxes returned by db::create depends on
216 : * implementation-defined behavior, and therefore should not be specified in
217 : * source files. If explicitly naming a DataBox type is necessary they should be
218 : * generated using db::compute_databox_type.
219 : *
220 : * \see db::create
221 : *
222 : * @tparam Tags list of DataBoxTag's
223 : */
224 : template <typename... Tags>
225 1 : class DataBox<tmpl::list<Tags...>> : public Access,
226 : private detail::Item<Tags>... {
227 : public:
228 : /*!
229 : * \brief A typelist (`tmpl::list`) of Tags that the DataBox holds
230 : */
231 1 : using tags_list = tmpl::list<Tags...>;
232 :
233 : /// A list of all the immutable item tags, including their subitems.
234 : /// Immutable items are compute tags and reference tags.
235 1 : using immutable_item_tags =
236 : tmpl::filter<tags_list, db::is_immutable_item_tag<tmpl::_1>>;
237 :
238 : /// A list of all the immutable item tags used to create the DataBox
239 : ///
240 : /// \note This does not include subitems of immutable items
241 1 : using immutable_item_creation_tags =
242 : tmpl::remove_if<immutable_item_tags, tt::is_a<::Tags::Subitem, tmpl::_1>>;
243 :
244 : /// A list of all the mutable item tags, including their subitems
245 1 : using mutable_item_tags =
246 : tmpl::filter<tags_list, db::is_mutable_item_tag<tmpl::_1>>;
247 :
248 : /// A list of the expanded simple subitems, not including the main Subitem
249 : /// tags themselves.
250 : ///
251 : /// Specifically, if there is a `Variables<Tag0, Tag1>`, then this list would
252 : /// contain `Tag0, Tag1`.
253 1 : using mutable_subitem_tags = tmpl::flatten<
254 : tmpl::transform<mutable_item_tags, db::Subitems<tmpl::_1>>>;
255 :
256 : /// A list of all the mutable item tags used to create the DataBox
257 : ///
258 : /// \note This does not include subitems of mutable items
259 1 : using mutable_item_creation_tags =
260 : tmpl::list_difference<mutable_item_tags, mutable_subitem_tags>;
261 :
262 : /// A list of all the mutable tags that have subitems
263 1 : using mutable_item_parent_tags =
264 : tmpl::filter<mutable_item_creation_tags,
265 : tmpl::bind<detail::has_subitems, tmpl::_1>>;
266 :
267 : /// A list of all the compute item tags
268 1 : using compute_item_tags =
269 : tmpl::filter<immutable_item_tags, db::is_compute_tag<tmpl::_1>>;
270 :
271 : /// A list of all the compute tags that have subitems
272 1 : using compute_item_parent_tags =
273 : tmpl::filter<compute_item_tags,
274 : tmpl::bind<detail::has_subitems, tmpl::_1>>;
275 :
276 : /// A list of all the reference tags
277 1 : using reference_item_tags =
278 : tmpl::filter<immutable_item_tags, db::is_reference_tag<tmpl::_1>>;
279 :
280 : /// A list of all the reference tags that have subitems
281 1 : using reference_item_parent_tags =
282 : tmpl::filter<reference_item_tags,
283 : tmpl::bind<detail::has_subitems, tmpl::_1>>;
284 :
285 : /// \cond HIDDEN_SYMBOLS
286 : /*!
287 : * \note the default constructor is only used for serialization
288 : */
289 : DataBox() = default;
290 :
291 : constexpr DataBox(DataBox&& rhs) : detail::Item<Tags>(std::move(rhs))... {
292 : reset_all_subitems();
293 : }
294 : constexpr DataBox& operator=(DataBox&& rhs) {
295 : if (&rhs != this) {
296 : #if defined(__GNUC__) && !defined(__clang__) && \
297 : (__GNUC__ < 8 || (__GNUC__ > 10 && __GNUC__ < 12))
298 : #pragma GCC diagnostic push
299 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
300 : #endif // defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 8 ||
301 : // (__GNUC__ > 10 && __GNUC__ < 12))
302 : ::expand_pack(
303 : (get_item<Tags>() = std::move(rhs.template get_item<Tags>()))...);
304 : #if defined(__GNUC__) && !defined(__clang__) && \
305 : (__GNUC__ < 8 || (__GNUC__ > 10 && __GNUC__ < 12))
306 : #pragma GCC diagnostic pop
307 : #endif // defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 8 ||
308 : // (__GNUC__ > 10 && __GNUC__ < 12))
309 : reset_all_subitems();
310 : }
311 : return *this;
312 : }
313 : DataBox(const DataBox& rhs) = delete;
314 : DataBox& operator=(const DataBox& rhs) = delete;
315 : ~DataBox() override = default;
316 :
317 : /// \endcond
318 :
319 : /// Print the expanded type aliases
320 1 : std::string print_types() const override;
321 :
322 : /// Print the items
323 : template <bool PrintImmutableItems = true>
324 1 : std::string print_items() const;
325 :
326 : /// The size in bytes of each item (excluding reference items)
327 1 : std::map<std::string, size_t> size_of_items() const;
328 :
329 : /// Retrieve the tag `Tag`, should be called by the free function db::get
330 : template <typename Tag>
331 1 : const auto& get() const;
332 :
333 : /// \brief Copy the items with tags `TagsOfItemsToCopy` from the DataBox
334 : /// into a TaggedTuple, should be called by the free function db::copy_items
335 : template <typename... TagsOfItemsToCopy>
336 1 : tuples::TaggedTuple<TagsOfItemsToCopy...> copy_items() const;
337 :
338 : /// Retrieve a mutable reference to the tag `Tag`, should be called
339 : /// by the free function db::get_mutable_reference
340 : template <typename Tag>
341 1 : auto& get_mutable_reference();
342 :
343 : /// Check whether a tags depends on another tag. Should be called
344 : /// through the metafunction db::tag_depends_on.
345 : template <typename Consumer, typename Provider>
346 1 : constexpr static bool tag_depends_on();
347 :
348 : // NOLINTNEXTLINE(google-runtime-references)
349 0 : void pup(PUP::er& p) {
350 : // Only the mutable creation items are serialized. Compute items are
351 : // initialized to unevaluated, and mutable subitems are reinitialized
352 : // cheaply.
353 : pup_impl(p, mutable_item_creation_tags{});
354 : }
355 :
356 : template <typename... AddMutableItemTags, typename AddImmutableItemTagsList,
357 : typename... Args>
358 0 : constexpr DataBox(tmpl::list<AddMutableItemTags...> /*meta*/,
359 : AddImmutableItemTagsList /*meta*/, Args&&... args);
360 :
361 : private:
362 : template <typename CopiedItemsTagList, typename DbTagList>
363 : // clang-tidy: redundant declaration
364 1 : friend auto copy_items(const DataBox<DbTagList>& box);
365 :
366 : template <typename... MutateTags, typename TagList, typename Invokable,
367 : typename... Args>
368 : // clang-tidy: redundant declaration
369 0 : friend decltype(auto) mutate(Invokable&& invokable,
370 : gsl::not_null<DataBox<TagList>*> box, // NOLINT
371 : Args&&... args); // NOLINT
372 :
373 : // evaluates the compute item corresponding to ComputeTag passing along
374 : // items fetched via ArgumentTags
375 : template <typename ComputeTag, typename... ArgumentTags>
376 0 : void evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const;
377 :
378 : // retrieves the reference of the ReferenceTag passing along items fetched via
379 : // ArgumentTags
380 : template <typename ReferenceTag, typename... ArgumentTags>
381 0 : const auto& get_reference_item(tmpl::list<ArgumentTags...> /*meta*/) const;
382 :
383 : // get a constant reference to the item corresponding to Tag
384 : template <typename Tag>
385 0 : const auto& get_item() const {
386 : return static_cast<const detail::Item<Tag>&>(*this);
387 : }
388 :
389 : // get a mutable reference to the item corresponding to Tag
390 : template <typename Tag>
391 0 : auto& get_item() {
392 : return static_cast<detail::Item<Tag>&>(*this);
393 : }
394 :
395 : ////////////////////////////////////////////////////////////////
396 : // Data structure for runtime retrieval and mutation of tags.
397 0 : struct TagGraphs {
398 : std::unordered_map<std::string, const void* (DataBox::*)() const>
399 0 : tag_retrieval_functions{};
400 : std::unordered_map<std::string, std::vector<std::string>>
401 0 : tags_and_dependents{};
402 : std::unordered_map<std::string, bool (DataBox::*)()>
403 0 : tags_and_reset_functions{};
404 : std::unordered_map<std::string, std::vector<std::string>>
405 0 : parent_to_subitem_tags{};
406 0 : std::unordered_map<std::string, std::string> subitem_to_parent_tag{};
407 : std::unordered_map<std::string, void* (DataBox::*)()>
408 0 : tag_mutate_functions{};
409 : std::unordered_map<std::string, void (DataBox::*)()>
410 0 : mutate_mutable_subitems_functions{};
411 : std::unordered_map<std::string, void (DataBox::*)()>
412 0 : reset_compute_items_after_mutate_functions{};
413 : };
414 :
415 : /// \cond
416 : static const TagGraphs tag_graphs_;
417 : /// \endcond
418 0 : static TagGraphs compute_tag_graphs();
419 0 : void reset_parent(const std::string& item_name,
420 : const std::string& skip_this_subitem);
421 0 : void reset_compute_items(const std::string& item_name);
422 : template <typename MutatedTag>
423 0 : void reset_compute_items_after_mutate();
424 0 : void mutate_mutable_subitems(const std::string& tag_name) override;
425 0 : void reset_compute_items_after_mutate(const std::string& tag_name) override;
426 : template <typename Tag>
427 0 : const void* get_item_as_void_pointer() const;
428 : template <typename Tag>
429 0 : void* get_item_as_void_pointer_for_mutate();
430 0 : const void* get_item_by_name(const std::string& tag_name) const override;
431 0 : void* mutate_item_by_name(const std::string& tag_name) override;
432 0 : bool lock_box_for_mutate() override;
433 0 : void unlock_box_after_mutate() override;
434 : // End of runtime retrieval code
435 : ////////////////////////////////////////////////////////////////
436 :
437 : // copy item correspond to Tag
438 : template <typename Tag>
439 0 : auto copy_item() const;
440 :
441 : // copy items corresponding to CopiedItemsTags
442 : // from the DataBox to a TaggedTuple
443 : template <typename... CopiedItemsTags>
444 0 : tuples::TaggedTuple<CopiedItemsTags...> copy_items(
445 : tmpl::list<CopiedItemsTags...> /*meta*/) const;
446 :
447 : template <typename ParentTag>
448 0 : constexpr void add_mutable_subitems_to_box(tmpl::list<> /*meta*/) {}
449 :
450 : // add mutable items for the subitems of the item corresponding to ParentTag
451 : template <typename ParentTag, typename... SubitemTags>
452 0 : constexpr void add_mutable_subitems_to_box(
453 : tmpl::list<SubitemTags...> /*meta*/);
454 :
455 : // sets the mutable item corresponding to Tag with the ArgsIndex object in
456 : // items
457 : template <size_t ArgsIndex, typename Tag, typename... Ts>
458 0 : constexpr char add_mutable_item_to_box(std::tuple<Ts...>& items);
459 :
460 : // set the mutable items corresponding to AddMutableItemTags to the
461 : // appropriate objects from items, and checks the dependencies of the
462 : // immutable items corresponding to AddImmutableItemTags
463 : template <typename... Ts, typename... AddMutableItemTags,
464 : typename... AddImmutableItemTags, size_t... Is>
465 0 : void add_items_to_box(std::tuple<Ts...>& items,
466 : tmpl::list<AddMutableItemTags...> /*meta*/,
467 : std::index_sequence<Is...> /*meta*/,
468 : tmpl::list<AddImmutableItemTags...> /*meta*/);
469 :
470 : // clang-tidy: no non-const references
471 : template <typename... MutableItemCreationTags>
472 0 : void pup_impl(PUP::er& p, // NOLINT
473 : tmpl::list<MutableItemCreationTags...> /*meta*/);
474 :
475 : // Mutating items in the DataBox
476 : template <typename ParentTag>
477 0 : constexpr void mutate_mutable_subitems(tmpl::list<> /*meta*/) {}
478 :
479 : template <typename ParentTag, typename... Subtags>
480 0 : constexpr void mutate_mutable_subitems(tmpl::list<Subtags...> /*meta*/);
481 :
482 : template <typename ParentTag>
483 0 : void mutate_mutable_subitems() {
484 : return mutate_mutable_subitems<ParentTag>(
485 : typename Subitems<ParentTag>::type{});
486 : }
487 :
488 0 : constexpr void reset_all_subitems();
489 :
490 : /// Returns `true` if the compute tag was evaluated before reset, `false` if
491 : /// it was not.
492 : template <typename ImmutableItemTag>
493 1 : bool reset_compute_item();
494 : // End mutating items in the DataBox
495 :
496 0 : using edge_list =
497 : typename detail::create_dependency_graph<tags_list,
498 : immutable_item_tags>::type;
499 :
500 0 : bool mutate_locked_box_{false};
501 : };
502 :
503 : /// \cond
504 : template <typename... Tags>
505 : const typename DataBox<tmpl::list<Tags...>>::TagGraphs
506 : DataBox<tmpl::list<Tags...>>::tag_graphs_ =
507 : DataBox<tmpl::list<Tags...>>::compute_tag_graphs();
508 : /// \endcond
509 :
510 : template <typename... Tags>
511 : std::string DataBox<tmpl::list<Tags...>>::print_types() const {
512 : std::ostringstream os;
513 : os << "DataBox type aliases:\n";
514 : os << "using tags_list = " << pretty_type::get_name<tags_list>() << ";\n";
515 : os << "using immutable_item_tags = "
516 : << pretty_type::get_name<immutable_item_tags>() << ";\n";
517 : os << "using immutable_item_creation_tags = "
518 : << pretty_type::get_name<immutable_item_creation_tags>() << ";\n";
519 : os << "using mutable_item_tags = "
520 : << pretty_type::get_name<mutable_item_tags>() << ";\n";
521 : os << "using mutable_subitem_tags = "
522 : << pretty_type::get_name<mutable_subitem_tags>() << ";\n";
523 : os << "using compute_item_tags = "
524 : << pretty_type::get_name<compute_item_tags>() << ";\n";
525 : os << "using reference_item_tags = "
526 : << pretty_type::get_name<reference_item_tags>() << ";\n";
527 : os << "using edge_list = " << pretty_type::get_name<edge_list>() << ";\n";
528 : return os.str();
529 : }
530 :
531 : template <typename... Tags>
532 : template <bool PrintImmutableItems>
533 : std::string DataBox<tmpl::list<Tags...>>::print_items() const {
534 : std::ostringstream os;
535 : os << "Items:\n";
536 : const auto print_item = [this, &os](auto tag_v) {
537 : (void)this;
538 : using tag = tmpl::type_from<decltype(tag_v)>;
539 : using type = typename tag::type;
540 : os << "----------\n";
541 : os << "Name: " << pretty_type::get_name<tag>() << "\n";
542 : os << "Type: " << pretty_type::get_name<type>() << "\n";
543 : os << "Value: ";
544 : print_value(os, this->get<tag>());
545 : os << "\n";
546 : };
547 : tmpl::for_each<mutable_item_creation_tags>(print_item);
548 : if constexpr (PrintImmutableItems) {
549 : tmpl::for_each<immutable_item_creation_tags>(print_item);
550 : }
551 : return os.str();
552 : }
553 :
554 : template <typename... Tags>
555 : std::map<std::string, size_t> DataBox<tmpl::list<Tags...>>::size_of_items()
556 : const {
557 : std::map<std::string, size_t> result{};
558 : const auto add_item_size = [this, &result](auto tag_v) {
559 : (void)this;
560 : using tag = tmpl::type_from<decltype(tag_v)>;
561 : if constexpr (not db::is_reference_tag_v<tag>) {
562 : // For item of ItemType::Compute, this will not evaluate its function
563 : // (i.e. if the item has never been evaluated its size will be that of a
564 : // default initialized object)
565 : const auto& item = get_item<tag>().get();
566 : if constexpr (tt::is_a_v<std::unique_ptr, typename tag::type>) {
567 : if (item == nullptr) {
568 : result[pretty_type::get_name<tag>()] = 0;
569 : } else {
570 : result[pretty_type::get_name<tag>()] = size_of_object_in_bytes(*item);
571 : }
572 : } else {
573 : result[pretty_type::get_name<tag>()] = size_of_object_in_bytes(item);
574 : }
575 : }
576 : };
577 : tmpl::for_each<mutable_item_creation_tags>(add_item_size);
578 : tmpl::for_each<immutable_item_creation_tags>(add_item_size);
579 : return result;
580 : }
581 :
582 : namespace detail {
583 : // This function exists so that the user can look at the template
584 : // arguments to find out what triggered the static_assert.
585 : template <typename ImmutableItemTag, typename ArgumentTag, typename TagsList>
586 : constexpr char check_immutable_item_tag_dependency() {
587 : using immutable_item_tag_index = tmpl::index_of<TagsList, ImmutableItemTag>;
588 : static_assert(
589 : tmpl::less<tmpl::index_if<TagsList,
590 : std::is_same<tmpl::pin<ArgumentTag>, tmpl::_1>,
591 : immutable_item_tag_index>,
592 : immutable_item_tag_index>::value,
593 : "The argument_tags of an immutable item tag must be added before itself. "
594 : "This is done to ensure no cyclic dependencies arise. See the first and "
595 : "second template arguments of check_immutable_item_tag_dependency for "
596 : "the immutable item tag and its missing (or incorrectly added) argument "
597 : "tag. The third template argument is the TagsList of the DataBox (in "
598 : "which the argument tag should precede the immutable item tag)");
599 : return '0';
600 : }
601 :
602 : template <typename ImmutableItemTag, typename TagsList,
603 : typename... ArgumentsTags>
604 : SPECTRE_ALWAYS_INLINE constexpr void check_immutable_item_tag_dependencies_impl(
605 : tmpl::list<ArgumentsTags...> /*meta*/) {
606 : DEBUG_STATIC_ASSERT(
607 : tmpl2::flat_all_v<is_tag_v<ArgumentsTags>...>,
608 : "Cannot have non-DataBoxTag arguments to a ComputeItem or ReferenceItem. "
609 : "Please make sure all the specified argument_tags derive from "
610 : "db::SimpleTag or db::BaseTag.");
611 : DEBUG_STATIC_ASSERT(
612 : not tmpl2::flat_any_v<std::is_same_v<ArgumentsTags, ImmutableItemTag>...>,
613 : "A ComputeItem cannot take its own Tag as an argument.");
614 : expand_pack(detail::check_immutable_item_tag_dependency<
615 : ImmutableItemTag, ArgumentsTags, TagsList>()...);
616 : }
617 :
618 : template <typename ImmutableItemTag, typename TagsList>
619 : SPECTRE_ALWAYS_INLINE constexpr void check_immutable_item_tag_dependencies() {
620 : check_immutable_item_tag_dependencies_impl<ImmutableItemTag, TagsList>(
621 : tmpl::transform<typename ImmutableItemTag::argument_tags,
622 : tmpl::bind<detail::first_matching_tag,
623 : tmpl::pin<TagsList>, tmpl::_1>>{});
624 : }
625 : } // namespace detail
626 :
627 : template <typename... Tags>
628 : template <typename ParentTag, typename... SubitemTags>
629 : SPECTRE_ALWAYS_INLINE constexpr void
630 : db::DataBox<tmpl::list<Tags...>>::add_mutable_subitems_to_box(
631 : tmpl::list<SubitemTags...> /*meta*/) {
632 : const auto add_mutable_subitem_to_box = [this](auto tag_v) {
633 : (void)this; // Compiler bug warns this is unused
634 : using subitem_tag = decltype(tag_v);
635 : get_item<subitem_tag>() =
636 : detail::Item<subitem_tag>(typename subitem_tag::type{});
637 : Subitems<ParentTag>::template create_item<subitem_tag>(
638 : make_not_null(&get_item<ParentTag>().mutate()),
639 : make_not_null(&get_item<subitem_tag>().mutate()));
640 : };
641 :
642 : EXPAND_PACK_LEFT_TO_RIGHT(add_mutable_subitem_to_box(SubitemTags{}));
643 : }
644 :
645 : template <typename... Tags>
646 : template <size_t ArgsIndex, typename MutableItemTag, typename... Ts>
647 : SPECTRE_ALWAYS_INLINE constexpr char
648 : db::DataBox<tmpl::list<Tags...>>::add_mutable_item_to_box(
649 : std::tuple<Ts...>& items) {
650 : if constexpr (sizeof...(Ts) > 0) {
651 : using ArgType = std::tuple_element_t<ArgsIndex, std::tuple<Ts...>>;
652 : get_item<MutableItemTag>() = detail::Item<MutableItemTag>(
653 : std::forward<ArgType>(std::get<ArgsIndex>(items)));
654 : }
655 : add_mutable_subitems_to_box<MutableItemTag>(
656 : typename Subitems<MutableItemTag>::type{});
657 : return '0'; // must return in constexpr function
658 : }
659 :
660 : // Add items or compute items to the TaggedDeferredTuple `data`. If
661 : // `AddItemTags...` is an empty pack then only compute items are added, while if
662 : // `AddComputeTags...` is an empty pack only items are added. Items are
663 : // always added before compute items.
664 : template <typename... Tags>
665 : template <typename... Ts, typename... AddMutableItemTags,
666 : typename... AddImmutableItemTags, size_t... Is>
667 : SPECTRE_ALWAYS_INLINE void DataBox<tmpl::list<Tags...>>::add_items_to_box(
668 : std::tuple<Ts...>& items, tmpl::list<AddMutableItemTags...> /*meta*/,
669 : std::index_sequence<Is...> /*meta*/,
670 : tmpl::list<AddImmutableItemTags...> /*meta*/) {
671 : expand_pack(add_mutable_item_to_box<Is, AddMutableItemTags>(items)...);
672 : EXPAND_PACK_LEFT_TO_RIGHT(
673 : detail::check_immutable_item_tag_dependencies<AddImmutableItemTags,
674 : tags_list>());
675 : }
676 :
677 : namespace detail {
678 : // This function (and its unused template argument) exist so that
679 : // users can see what tag has the wrong type when the static_assert
680 : // fails.
681 : template <typename Tag, typename TagType, typename SuppliedType>
682 : constexpr int check_initialization_argument_type() {
683 : static_assert(std::is_same_v<TagType, SuppliedType>,
684 : "The type of each Tag must be the same as the type being "
685 : "passed into the function creating the new DataBox. See the "
686 : "template parameters of check_initialization_argument_type for "
687 : "the tag, expected type, and supplied type.");
688 : return 0;
689 : }
690 : } // namespace detail
691 :
692 : /// \cond
693 : template <typename... Tags>
694 : template <typename... AddMutableItemTags, typename AddImmutableItemTagsList,
695 : typename... Args>
696 : constexpr DataBox<tmpl::list<Tags...>>::DataBox(
697 : tmpl::list<AddMutableItemTags...> /*meta*/,
698 : AddImmutableItemTagsList /*meta*/, Args&&... args) {
699 : DEBUG_STATIC_ASSERT(
700 : sizeof...(Args) == 0 or sizeof...(Args) == sizeof...(AddMutableItemTags),
701 : "Must pass in as many arguments as AddTags, or none to "
702 : "default-construct them.");
703 : #ifdef SPECTRE_DEBUG
704 : if constexpr (sizeof...(Args) > 0) {
705 : // The check_argument_type call is very expensive compared to the majority
706 : // of DataBox
707 : expand_pack(detail::check_initialization_argument_type<
708 : AddMutableItemTags, typename AddMutableItemTags::type,
709 : std::decay_t<Args>>()...);
710 : }
711 : #endif // SPECTRE_DEBUG
712 :
713 : std::tuple<Args&&...> args_tuple(std::forward<Args>(args)...);
714 : add_items_to_box(args_tuple, tmpl::list<AddMutableItemTags...>{},
715 : std::make_index_sequence<sizeof...(AddMutableItemTags)>{},
716 : AddImmutableItemTagsList{});
717 : }
718 : /// \endcond
719 :
720 : ////////////////////////////////////////////////////////////////
721 : // Serialization of DataBox
722 :
723 : template <typename... Tags>
724 : template <typename... MutableItemCreationTags>
725 : void DataBox<tmpl::list<Tags...>>::pup_impl(
726 : PUP::er& p, tmpl::list<MutableItemCreationTags...> /*meta*/) {
727 : const auto pup_simple_item = [&p, this](auto current_tag) {
728 : (void)this; // Compiler bug warning this capture is not used
729 : using tag = decltype(current_tag);
730 : get_item<tag>().pup(p);
731 : if (p.isUnpacking()) {
732 : add_mutable_subitems_to_box<tag>(typename Subitems<tag>::type{});
733 : }
734 : };
735 : (void)pup_simple_item; // Silence GCC warning about unused variable
736 : EXPAND_PACK_LEFT_TO_RIGHT(pup_simple_item(MutableItemCreationTags{}));
737 : }
738 :
739 : ////////////////////////////////////////////////////////////////
740 : // Runtime tag retrieval
741 : template <typename... Tags>
742 : auto DataBox<tmpl::list<Tags...>>::compute_tag_graphs() -> TagGraphs {
743 : TagGraphs result{};
744 : // Compute graphs for retrieving tags
745 : EXPAND_PACK_LEFT_TO_RIGHT([&result]() {
746 : using tag = detail::get_base<Tags>;
747 : const std::string tag_name = pretty_type::get_name<tag>();
748 : if (result.tag_retrieval_functions.find(tag_name) ==
749 : result.tag_retrieval_functions.end()) {
750 : result.tag_retrieval_functions[tag_name] =
751 : &DataBox::template get_item_as_void_pointer<Tags>;
752 : } else if (result.tag_retrieval_functions[tag_name] !=
753 : &DataBox::template get_item_as_void_pointer<Tags>) {
754 : result.tag_retrieval_functions[tag_name] = nullptr;
755 : }
756 : }());
757 :
758 : // Compute graphs for resetting compute tags
759 : //
760 : //
761 : // Subitems are a bit tricky. We need:
762 : // - If a parent tag is mutated, need to track through all subitems
763 : // - If a subitem is mutated, need to track through parent tag and all
764 : // subitems
765 : tmpl::for_each<
766 : tmpl::append<mutable_item_parent_tags, reference_item_parent_tags,
767 : compute_item_parent_tags>>([&result](auto tag_v) {
768 : // We choose to always work with the associated simple tags, so we need
769 : // to get the base tag _if_ we have a compute tag.
770 : using parent_tag_compute = tmpl::type_from<decltype(tag_v)>;
771 : using parent_tag = detail::get_base<tmpl::type_from<decltype(tag_v)>>;
772 : using subitems = typename Subitems<parent_tag_compute>::type;
773 : const std::string parent_tag_name = pretty_type::get_name<parent_tag>();
774 : const std::vector<std::string> subitem_tag_names =
775 : pretty_type::vector_of_get_names(subitems{});
776 : for (const std::string& subitem_name : subitem_tag_names) {
777 : result.parent_to_subitem_tags[parent_tag_name].push_back(subitem_name);
778 : ASSERT(result.subitem_to_parent_tag.find(subitem_name) ==
779 : result.subitem_to_parent_tag.end(),
780 : "Trying to insert subitem tag "
781 : << subitem_name
782 : << " for a second time. This is an "
783 : "internal inconsistency bug.\n");
784 : result.subitem_to_parent_tag[subitem_name] = parent_tag_name;
785 : }
786 : });
787 :
788 : tmpl::for_each<tmpl::append<reference_item_tags, compute_item_tags>>(
789 : [&result](auto tag_v) {
790 : using compute_tag = tmpl::type_from<decltype(tag_v)>;
791 : using associated_simple_tag = typename compute_tag::base;
792 : const std::string simple_tag =
793 : pretty_type::get_name<associated_simple_tag>();
794 : const std::vector<std::string> argument_tags =
795 : pretty_type::vector_of_get_names(
796 : tmpl::transform<
797 : typename compute_tag::argument_tags,
798 : detail::get_simple_tag_for_base_tag<
799 : tmpl::pin<tmpl::list<Tags...>>, tmpl::_1>>{});
800 : for (const std::string& argument_tag : argument_tags) {
801 : result.tags_and_dependents[argument_tag].push_back(simple_tag);
802 : }
803 : // If this compute tag is a subitem of another compute tag, then we
804 : // need to make the parent aware that this tag depends on it.
805 : if (result.subitem_to_parent_tag.find(simple_tag) !=
806 : result.subitem_to_parent_tag.end()) {
807 : result.tags_and_dependents[result.subitem_to_parent_tag[simple_tag]]
808 : .push_back(simple_tag);
809 : }
810 : result.tags_and_reset_functions[simple_tag] =
811 : &DataBox::template reset_compute_item<compute_tag>;
812 : });
813 :
814 : // Set mutation function
815 : tmpl::for_each<mutable_item_tags>([&result](auto tag_v) {
816 : using tag = tmpl::type_from<decltype(tag_v)>;
817 : const std::string tag_name = pretty_type::get_name<tag>();
818 : if (result.tag_mutate_functions.find(tag_name) ==
819 : result.tag_mutate_functions.end()) {
820 : result.tag_mutate_functions[tag_name] =
821 : &DataBox::template get_item_as_void_pointer_for_mutate<tag>;
822 : } else if (result.tag_mutate_functions[tag_name] !=
823 : &DataBox::template get_item_as_void_pointer_for_mutate<tag>) {
824 : result.tag_mutate_functions[tag_name] = nullptr;
825 : }
826 :
827 : if (result.mutate_mutable_subitems_functions.find(tag_name) ==
828 : result.mutate_mutable_subitems_functions.end()) {
829 : result.mutate_mutable_subitems_functions[tag_name] =
830 : static_cast<void (DataBox::*)()>(
831 : &DataBox::template mutate_mutable_subitems<tag>);
832 : } else if (result.mutate_mutable_subitems_functions[tag_name] !=
833 : static_cast<void (DataBox::*)()>(
834 : &DataBox::template mutate_mutable_subitems<tag>)) {
835 : result.mutate_mutable_subitems_functions[tag_name] = nullptr;
836 : }
837 :
838 : if (result.reset_compute_items_after_mutate_functions.find(tag_name) ==
839 : result.reset_compute_items_after_mutate_functions.end()) {
840 : result.reset_compute_items_after_mutate_functions[tag_name] =
841 : &DataBox::template reset_compute_items_after_mutate<tag>;
842 : } else if (result.reset_compute_items_after_mutate_functions[tag_name] !=
843 : &DataBox::template reset_compute_items_after_mutate<tag>) {
844 : result.reset_compute_items_after_mutate_functions[tag_name] = nullptr;
845 : }
846 : });
847 : return result;
848 : }
849 :
850 : template <typename... Tags>
851 : void DataBox<tmpl::list<Tags...>>::reset_parent(
852 : const std::string& item_name, const std::string& skip_this_subitem) {
853 : if (const auto parent_tag_it =
854 : tag_graphs_.parent_to_subitem_tags.find(item_name);
855 : parent_tag_it != tag_graphs_.parent_to_subitem_tags.end()) {
856 : for (const auto& subitem_tag : parent_tag_it->second) {
857 : if (subitem_tag == skip_this_subitem) {
858 : continue;
859 : }
860 : const auto dependent_items_it =
861 : tag_graphs_.tags_and_dependents.find(subitem_tag);
862 : if (dependent_items_it == tag_graphs_.tags_and_dependents.end()) {
863 : continue;
864 : }
865 : for (const std::string& dependent_item_name :
866 : dependent_items_it->second) {
867 : ASSERT(tag_graphs_.tags_and_reset_functions.find(dependent_item_name) !=
868 : tag_graphs_.tags_and_reset_functions.end(),
869 : "Item " << dependent_item_name
870 : << " does not have a reset function.");
871 : if ((this->*(tag_graphs_.tags_and_reset_functions)
872 : .at(dependent_item_name))()) {
873 : if (tag_graphs_.tags_and_dependents.find(dependent_item_name) !=
874 : tag_graphs_.tags_and_dependents.end()) {
875 : // If the compute tag was evaluated, then we need to check the
876 : // items that depend on it.
877 : reset_compute_items(dependent_item_name);
878 : }
879 : reset_parent(dependent_item_name, "");
880 : if (const auto parent_it =
881 : tag_graphs_.subitem_to_parent_tag.find(dependent_item_name);
882 : parent_it != tag_graphs_.subitem_to_parent_tag.end()) {
883 : reset_parent(parent_it->second, dependent_item_name);
884 : }
885 : }
886 : }
887 : }
888 : }
889 : }
890 :
891 : template <typename... Tags>
892 : void DataBox<tmpl::list<Tags...>>::reset_compute_items(
893 : const std::string& item_name) {
894 : // If the compute tag was evaluated before reset, then we reset dependent
895 : // compute tags.
896 : ASSERT(tag_graphs_.tags_and_dependents.find(item_name) !=
897 : tag_graphs_.tags_and_dependents.end(),
898 : "Item " << item_name << " does not have any dependents.");
899 : for (const std::string& dependent_item_name :
900 : tag_graphs_.tags_and_dependents.at(item_name)) {
901 : ASSERT(
902 : tag_graphs_.tags_and_reset_functions.find(dependent_item_name) !=
903 : tag_graphs_.tags_and_reset_functions.end(),
904 : "Item " << dependent_item_name << " does not have a reset function.");
905 : if ((this->*(tag_graphs_.tags_and_reset_functions)
906 : .at(dependent_item_name))()) {
907 : reset_parent(dependent_item_name, "");
908 : if (const auto parent_it =
909 : tag_graphs_.subitem_to_parent_tag.find(dependent_item_name);
910 : parent_it != tag_graphs_.subitem_to_parent_tag.end()) {
911 : reset_parent(parent_it->second, dependent_item_name);
912 : }
913 : if (tag_graphs_.tags_and_dependents.find(dependent_item_name) !=
914 : tag_graphs_.tags_and_dependents.end()) {
915 : // If the compute tag was evaluated, then we need to check the items
916 : // that depend on it.
917 : reset_compute_items(dependent_item_name);
918 : }
919 : }
920 : }
921 : // If this tag is a parent tag, reset subitems and their dependents
922 : reset_parent(item_name, "");
923 : // If this tag is a subitem, reset parent and other subitem dependents
924 : if (const auto parent_it = tag_graphs_.subitem_to_parent_tag.find(item_name);
925 : parent_it != tag_graphs_.subitem_to_parent_tag.end()) {
926 : reset_parent(parent_it->second, item_name);
927 : }
928 : }
929 :
930 : template <typename... Tags>
931 : template <typename MutatedTag>
932 : void DataBox<tmpl::list<Tags...>>::reset_compute_items_after_mutate() {
933 : static const std::string mutated_tag = pretty_type::get_name<MutatedTag>();
934 : if (tag_graphs_.tags_and_dependents.find(mutated_tag) !=
935 : tag_graphs_.tags_and_dependents.end()) {
936 : reset_compute_items(mutated_tag);
937 : }
938 :
939 : // Handled subitems
940 : if constexpr (detail::has_subitems<MutatedTag>::value) {
941 : ASSERT(tag_graphs_.parent_to_subitem_tags.find(mutated_tag) !=
942 : tag_graphs_.parent_to_subitem_tags.end(),
943 : "The parent tag "
944 : << mutated_tag
945 : << " is not in the set of parent_to_subitem_tags_. This is "
946 : "an internal inconsistency bug.\n");
947 : for (const auto& subitem_name :
948 : tag_graphs_.parent_to_subitem_tags.at(mutated_tag)) {
949 : if (tag_graphs_.tags_and_dependents.find(subitem_name) !=
950 : tag_graphs_.tags_and_dependents.end()) {
951 : reset_compute_items(subitem_name);
952 : }
953 : }
954 : }
955 : // Handle parent tags
956 : if constexpr (tmpl::list_contains_v<mutable_subitem_tags, MutatedTag>) {
957 : ASSERT(tag_graphs_.subitem_to_parent_tag.find(mutated_tag) !=
958 : tag_graphs_.subitem_to_parent_tag.end(),
959 : "Expected to find a parent tag of "
960 : << mutated_tag
961 : << " but did not. This is an internal inconsistency bug.");
962 : const auto& parent_tag_name =
963 : tag_graphs_.subitem_to_parent_tag.at(mutated_tag);
964 : if (tag_graphs_.tags_and_dependents.find(parent_tag_name) !=
965 : tag_graphs_.tags_and_dependents.end()) {
966 : reset_compute_items(parent_tag_name);
967 : }
968 : for (const auto& subitem_name :
969 : tag_graphs_.parent_to_subitem_tags.at(parent_tag_name)) {
970 : if (tag_graphs_.tags_and_dependents.find(subitem_name) !=
971 : tag_graphs_.tags_and_dependents.end() and
972 : subitem_name != mutated_tag) {
973 : reset_compute_items(subitem_name);
974 : }
975 : }
976 : }
977 : }
978 :
979 : template <typename... Tags>
980 : void DataBox<tmpl::list<Tags...>>::mutate_mutable_subitems(
981 : const std::string& tag_name) {
982 : ASSERT(tag_graphs_.mutate_mutable_subitems_functions.find(tag_name) !=
983 : tag_graphs_.mutate_mutable_subitems_functions.end(),
984 : "Can't find tag " << tag_name
985 : << " when mutating. Either the tag is not mutable "
986 : "or this is an internal inconsistency bug.\n");
987 : (this->*tag_graphs_.mutate_mutable_subitems_functions.at(tag_name))();
988 : }
989 :
990 : template <typename... Tags>
991 : void DataBox<tmpl::list<Tags...>>::reset_compute_items_after_mutate(
992 : const std::string& tag_name) {
993 : ASSERT(
994 : tag_graphs_.reset_compute_items_after_mutate_functions.find(tag_name) !=
995 : tag_graphs_.reset_compute_items_after_mutate_functions.end(),
996 : "Can't find tag " << tag_name
997 : << " when mutating. Either the tag is not mutable "
998 : "or this is an internal inconsistency bug.\n");
999 : (this->*tag_graphs_.reset_compute_items_after_mutate_functions.at(
1000 : tag_name))();
1001 : }
1002 :
1003 : template <typename... Tags>
1004 : template <typename Tag>
1005 : const void* DataBox<tmpl::list<Tags...>>::get_item_as_void_pointer() const {
1006 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1007 : return reinterpret_cast<const void*>(&this->template get<Tag>());
1008 : }
1009 :
1010 : template <typename... Tags>
1011 : template <typename Tag>
1012 : void* DataBox<tmpl::list<Tags...>>::get_item_as_void_pointer_for_mutate() {
1013 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1014 : return reinterpret_cast<void*>(&this->template get_item<Tag>());
1015 : }
1016 :
1017 : template <typename... Tags>
1018 : const void* DataBox<tmpl::list<Tags...>>::get_item_by_name(
1019 : const std::string& tag_name) const {
1020 : ASSERT(tag_graphs_.tag_retrieval_functions.find(tag_name) !=
1021 : tag_graphs_.tag_retrieval_functions.end(),
1022 : "Tag with name " << tag_name
1023 : << " is not found in the DataBox. Known tags are:\n");
1024 : ASSERT(tag_graphs_.tag_retrieval_functions.at(tag_name) != nullptr,
1025 : "Tag with name " << tag_name
1026 : << " is in the DataBox more than once and so "
1027 : "cannot be retrieved by name.");
1028 :
1029 : return (this->*tag_graphs_.tag_retrieval_functions.at(tag_name))();
1030 : }
1031 :
1032 : template <typename... Tags>
1033 : void* DataBox<tmpl::list<Tags...>>::mutate_item_by_name(
1034 : const std::string& tag_name) {
1035 : ASSERT(tag_graphs_.tag_mutate_functions.find(tag_name) !=
1036 : tag_graphs_.tag_mutate_functions.end(),
1037 : "Cannot mutate tag: " << tag_name);
1038 : return (this->*tag_graphs_.tag_mutate_functions.at(tag_name))();
1039 : }
1040 :
1041 : template <typename... Tags>
1042 : bool DataBox<tmpl::list<Tags...>>::lock_box_for_mutate() {
1043 : const bool temp = mutate_locked_box_;
1044 : mutate_locked_box_ = true;
1045 : return temp;
1046 : }
1047 :
1048 : template <typename... Tags>
1049 : void DataBox<tmpl::list<Tags...>>::unlock_box_after_mutate() {
1050 : mutate_locked_box_ = false;
1051 : }
1052 :
1053 : ////////////////////////////////////////////////////////////////
1054 : // Mutating items in the DataBox
1055 : // Classes and functions necessary for db::mutate to work
1056 :
1057 : template <typename... Tags>
1058 : template <typename ImmutableItemTag>
1059 : SPECTRE_ALWAYS_INLINE bool DataBox<tmpl::list<Tags...>>::reset_compute_item() {
1060 : // reference items do not need to be reset, but it still needs to be handled
1061 : // here. Partly just so we get an error if we are calling this function
1062 : // incorrectly and because we need to call this function during resets
1063 : // to get dependency tracking correct when compute tags depend on reference
1064 : // tags.
1065 : if constexpr (db::is_compute_tag_v<ImmutableItemTag>) {
1066 : const bool was_evaluated = get_item<ImmutableItemTag>().evaluated();
1067 : get_item<ImmutableItemTag>().reset();
1068 : return was_evaluated;
1069 : } else if constexpr (db::is_reference_tag_v<ImmutableItemTag>) {
1070 : return true;
1071 : }
1072 : ERROR(
1073 : "Trying to reset a tag that is neither a compute or reference tag. This "
1074 : "is an implementation bug. A decision needs to be made as to whether the "
1075 : "tag was previously evaluated or not. If it was not, then anything that "
1076 : "depends on this tag will not be reset.");
1077 : return false;
1078 : }
1079 :
1080 : template <typename... Tags>
1081 : template <typename ParentTag, typename... Subtags>
1082 : SPECTRE_ALWAYS_INLINE constexpr void
1083 : db::DataBox<tmpl::list<Tags...>>::mutate_mutable_subitems(
1084 : tmpl::list<Subtags...> /*meta*/) {
1085 : const auto helper = [this](auto tag_v) {
1086 : (void)this; // Compiler bug warns about unused this capture
1087 : using tag = decltype(tag_v);
1088 : Subitems<ParentTag>::template create_item<tag>(
1089 : make_not_null(&get_item<ParentTag>().mutate()),
1090 : make_not_null(&get_item<tag>().mutate()));
1091 : };
1092 :
1093 : EXPAND_PACK_LEFT_TO_RIGHT(helper(Subtags{}));
1094 : }
1095 :
1096 : template <typename... Tags>
1097 : SPECTRE_ALWAYS_INLINE constexpr void
1098 : db::DataBox<tmpl::list<Tags...>>::reset_all_subitems() {
1099 : tmpl::for_each<mutable_item_tags>([this](auto tag) {
1100 : using Tag = tmpl::type_from<decltype(tag)>;
1101 : this->mutate_mutable_subitems<Tag>(typename Subitems<Tag>::type{});
1102 : });
1103 : }
1104 :
1105 : /*!
1106 : * \ingroup DataBoxGroup
1107 : * \brief Allows changing the state of one or more non-computed elements in
1108 : * the DataBox
1109 : *
1110 : * `mutate()`'s second argument is the DataBox from which to retrieve the tags
1111 : * `MutateTags`. The objects corresponding to the `MutateTags` are then passed
1112 : * to `invokable`, which is a lambda or a function object taking as many
1113 : * arguments as there are `MutateTags` and with the arguments being of types
1114 : * `gsl::not_null<db::item_type<MutateTags>*>...`. Inside the `invokable` no
1115 : * items can be retrieved from the DataBox `box`. This is to avoid confusing
1116 : * subtleties with order of evaluation of compute items, as well as dangling
1117 : * references. If an `invokable` needs read access to items in `box` they should
1118 : * be passed as additional arguments to `mutate`. Capturing them by reference in
1119 : * a lambda does not work because of a bug in GCC 6.3 and earlier. For a
1120 : * function object the read-only items can also be stored as const references
1121 : * inside the object by passing `db::get<TAG>(t)` to the constructor.
1122 : *
1123 : * \example
1124 : * \snippet Test_DataBox.cpp databox_mutate_example
1125 : *
1126 : * The `invokable` may have function return values, and any returns are
1127 : * forwarded as returns to the `db::mutate` call.
1128 : *
1129 : * For convenience in generic programming, if `::Tags::DataBox` is
1130 : * passed as the sole `MutateTags` parameter, this function will
1131 : * simply call its first argument with its remaining arguments.
1132 : * Except for this case, `::Tags::DataBox` is not usable with this
1133 : * function.
1134 : *
1135 : * \warning Using `db::mutate` returns to obtain non-const references or
1136 : * pointers to box items is potentially very dangerous. The \ref DataBoxGroup
1137 : * "DataBox" cannot track any subsequent changes to quantities that have been
1138 : * "unsafely" extracted in this manner, so we consider it undefined behavior to
1139 : * return pointers or references to \ref DataBoxGroup "DataBox" contents.
1140 : */
1141 : template <typename... MutateTags, typename TagList, typename Invokable,
1142 : typename... Args>
1143 1 : decltype(auto) mutate(Invokable&& invokable,
1144 : const gsl::not_null<DataBox<TagList>*> box,
1145 : Args&&... args) {
1146 : if constexpr ((... or std::is_same_v<MutateTags, Tags::DataBox>)) {
1147 : // This branch doesn't directly access the box, so no locking or
1148 : // resetting is necessary.
1149 : static_assert(
1150 : std::is_same_v<tmpl::list<MutateTags...>, tmpl::list<Tags::DataBox>>,
1151 : "Cannot mutate other tags when obtaining the mutable DataBox.");
1152 : return invokable(box, std::forward<Args>(args)...);
1153 : } else {
1154 : static_assert(
1155 : tmpl2::flat_all_v<
1156 : detail::has_unique_matching_tag_v<TagList, MutateTags>...>,
1157 : "One of the tags being mutated could not be found in the DataBox or "
1158 : "is a base tag identifying more than one tag.");
1159 : static_assert(tmpl2::flat_all_v<tmpl::list_contains_v<
1160 : typename DataBox<TagList>::mutable_item_tags,
1161 : detail::first_matching_tag<TagList, MutateTags>>...>,
1162 : "Can only mutate mutable items");
1163 : if (UNLIKELY(box->mutate_locked_box_)) {
1164 : ERROR(
1165 : "Unable to mutate a DataBox that is already being mutated. This "
1166 : "error occurs when mutating a DataBox from inside the invokable "
1167 : "passed to the mutate function.");
1168 : }
1169 :
1170 : const CleanupRoutine unlock_box = [&box]() {
1171 : box->mutate_locked_box_ = false;
1172 : EXPAND_PACK_LEFT_TO_RIGHT(
1173 : box->template mutate_mutable_subitems<MutateTags>());
1174 : EXPAND_PACK_LEFT_TO_RIGHT(
1175 : box->template reset_compute_items_after_mutate<
1176 : detail::first_matching_tag<TagList, MutateTags>>());
1177 : };
1178 : box->mutate_locked_box_ = true;
1179 : return invokable(
1180 : make_not_null(&box->template get_item<
1181 : detail::first_matching_tag<TagList, MutateTags>>()
1182 : .mutate())...,
1183 : std::forward<Args>(args)...);
1184 : }
1185 : }
1186 :
1187 : ////////////////////////////////////////////////////////////////
1188 : // Retrieving items from the DataBox
1189 :
1190 : /// \cond
1191 : template <typename... Tags>
1192 : template <typename ComputeTag, typename... ArgumentTags>
1193 : void DataBox<tmpl::list<Tags...>>::evaluate_compute_item(
1194 : tmpl::list<ArgumentTags...> /*meta*/) const {
1195 : get_item<ComputeTag>().evaluate(get<ArgumentTags>()...);
1196 : }
1197 :
1198 : template <typename... Tags>
1199 : template <typename ReferenceTag, typename... ArgumentTags>
1200 : const auto& DataBox<tmpl::list<Tags...>>::get_reference_item(
1201 : tmpl::list<ArgumentTags...> /*meta*/) const {
1202 : return ReferenceTag::get(get<ArgumentTags>()...);
1203 : }
1204 :
1205 : template <typename... Tags>
1206 : template <typename Tag>
1207 : const auto& DataBox<tmpl::list<Tags...>>::get() const {
1208 : if constexpr (std::is_same_v<Tag, ::Tags::DataBox>) {
1209 : if (UNLIKELY(mutate_locked_box_)) {
1210 : ERROR(
1211 : "Unable to retrieve a (compute) item 'DataBox' from the DataBox from "
1212 : "within a call to mutate. You must pass these either through the "
1213 : "capture list of the lambda or the constructor of a class, this "
1214 : "restriction exists to avoid complexity.");
1215 : }
1216 : return *this;
1217 : } else {
1218 : DEBUG_STATIC_ASSERT(
1219 : not detail::has_no_matching_tag_v<tags_list, Tag>,
1220 : "Found no tags in the DataBox that match the tag being retrieved.");
1221 : DEBUG_STATIC_ASSERT(
1222 : detail::has_unique_matching_tag_v<tags_list, Tag>,
1223 : "Found more than one tag in the DataBox that matches the tag "
1224 : "being retrieved. This happens because more than one tag with the same "
1225 : "base (class) tag was added to the DataBox.");
1226 : using item_tag = detail::first_matching_tag<tags_list, Tag>;
1227 : if (UNLIKELY(mutate_locked_box_)) {
1228 : ERROR("Unable to retrieve a (compute) item '"
1229 : << db::tag_name<item_tag>()
1230 : << "' from the DataBox from within a "
1231 : "call to mutate. You must pass these either through the capture "
1232 : "list of the lambda or the constructor of a class, this "
1233 : "restriction exists to avoid complexity.");
1234 : }
1235 : if constexpr (detail::Item<item_tag>::item_type ==
1236 : detail::ItemType::Reference) {
1237 : return get_reference_item<item_tag>(typename item_tag::argument_tags{});
1238 : } else {
1239 : if constexpr (detail::Item<item_tag>::item_type ==
1240 : detail::ItemType::Compute) {
1241 : if (not get_item<item_tag>().evaluated()) {
1242 : evaluate_compute_item<item_tag>(typename item_tag::argument_tags{});
1243 : }
1244 : }
1245 : if constexpr (tt::is_a_v<std::unique_ptr, typename item_tag::type>) {
1246 : return *(get_item<item_tag>().get());
1247 : } else {
1248 : return get_item<item_tag>().get();
1249 : }
1250 : }
1251 : }
1252 : }
1253 : /// \endcond
1254 :
1255 : /*!
1256 : * \ingroup DataBoxGroup
1257 : * \brief Retrieve the item with tag `Tag` from the DataBox
1258 : * \requires Type `Tag` is one of the Tags corresponding to an object stored in
1259 : * the DataBox
1260 : *
1261 : * \return The object corresponding to the tag `Tag`
1262 : */
1263 : template <typename Tag, typename TagList>
1264 1 : SPECTRE_ALWAYS_INLINE const auto& get(const DataBox<TagList>& box) {
1265 : return box.template get<Tag>();
1266 : }
1267 :
1268 : ////////////////////////////////////////////////////////////////
1269 : // Copy mutable creation items from the DataBox
1270 :
1271 : /// \cond
1272 : template <typename... Tags>
1273 : template <typename Tag>
1274 : SPECTRE_ALWAYS_INLINE auto DataBox<tmpl::list<Tags...>>::copy_item() const {
1275 : using item_tag = detail::first_matching_tag<tags_list, Tag>;
1276 : using item_type = typename item_tag::type;
1277 : static_assert(tmpl::list_contains_v<mutable_item_creation_tags, item_tag>,
1278 : "Can only copy mutable creation items");
1279 : return deserialize<item_type>(
1280 : serialize<item_type>(get_item<item_tag>().get()).data());
1281 : }
1282 :
1283 : template <typename... DbTags>
1284 : template <typename... CopiedItemsTags>
1285 : tuples::TaggedTuple<CopiedItemsTags...>
1286 : DataBox<tmpl::list<DbTags...>>::copy_items(
1287 : tmpl::list<CopiedItemsTags...> /*meta*/) const {
1288 : return tuples::TaggedTuple<CopiedItemsTags...>{
1289 : copy_item<CopiedItemsTags>()...};
1290 : }
1291 : /// \endcond
1292 :
1293 : /*!
1294 : * \ingroup DataBoxGroup
1295 : * \brief Copy the items from the DataBox into a TaggedTuple
1296 : *
1297 : * \return The objects corresponding to CopiedItemsTagList
1298 : *
1299 : * \note The tags in CopiedItemsTagList must be a subset of
1300 : * the mutable_item_creation_tags of the DataBox
1301 : */
1302 : template <typename CopiedItemsTagList, typename DbTagList>
1303 1 : SPECTRE_ALWAYS_INLINE auto copy_items(const DataBox<DbTagList>& box) {
1304 : return box.copy_items(CopiedItemsTagList{});
1305 : }
1306 :
1307 : ////////////////////////////////////////////////////////////////
1308 : // Get mutable reference from the DataBox
1309 : template <typename... Tags>
1310 : template <typename Tag>
1311 : auto& DataBox<tmpl::list<Tags...>>::get_mutable_reference() {
1312 : DEBUG_STATIC_ASSERT(
1313 : not detail::has_no_matching_tag_v<tmpl::list<Tags...>, Tag>,
1314 : "Found no tags in the DataBox that match the tag being retrieved.");
1315 : DEBUG_STATIC_ASSERT(
1316 : detail::has_unique_matching_tag_v<tmpl::list<Tags...>, Tag>,
1317 : "Found more than one tag in the DataBox that matches the tag "
1318 : "being retrieved. This happens because more than one tag with the same "
1319 : "base (class) tag was added to the DataBox.");
1320 :
1321 : using item_tag = detail::first_matching_tag<tmpl::list<Tags...>, Tag>;
1322 :
1323 : DEBUG_STATIC_ASSERT(tmpl::list_contains_v<mutable_item_tags, item_tag>,
1324 : "Can only mutate mutable items");
1325 :
1326 : DEBUG_STATIC_ASSERT(
1327 : not (... or
1328 : tmpl::list_contains_v<typename Subitems<Tags>::type, item_tag>),
1329 : "Cannot extract references to subitems");
1330 : DEBUG_STATIC_ASSERT(not detail::has_subitems_v<item_tag>,
1331 : "Cannot extract references to items with subitems.");
1332 :
1333 : DEBUG_STATIC_ASSERT(
1334 : tmpl::none<edge_list, std::is_same<tmpl::pin<item_tag>,
1335 : tmpl::get_source<tmpl::_1>>>::value,
1336 : "Cannot extract references to items used by compute items.");
1337 :
1338 : return get_item<item_tag>().mutate();
1339 : }
1340 :
1341 : template <typename... Tags>
1342 : template <typename Consumer, typename Provider>
1343 : constexpr bool DataBox<tmpl::list<Tags...>>::tag_depends_on() {
1344 : // We need to check for things depending on the passed tag, any
1345 : // subitems, and the parent item if we were passed a subitem. These
1346 : // dependencies are handled internally by the mutation functions and
1347 : // not encoded in the graph.
1348 : using provider_aliases =
1349 : tmpl::push_front<typename Subitems<Provider>::type,
1350 : creation_tag<Provider, DataBox<tmpl::list<Tags...>>>>;
1351 :
1352 : // We have to replace subitems with their parents here because
1353 : // subitems of compute tags sometimes get graph edges from their
1354 : // parents and sometimes do not, depending on if they have
1355 : // dependencies.
1356 : using consumer_tag_to_check =
1357 : creation_tag<Consumer, DataBox<tmpl::list<Tags...>>>;
1358 :
1359 : // We cannot recursively call the function we are in, because we
1360 : // need to avoid the normalization done above. Otherwise we could
1361 : // end up in a loop when destination of an edge normalizes to its
1362 : // source.
1363 :
1364 : // Lambdas cannot capture themselves, but they can take themselves
1365 : // as an argument.
1366 : auto check_dependents = [](auto&& recurse,
1367 : auto node_depending_on_provider_v) {
1368 : using node_depending_on_provider = decltype(node_depending_on_provider_v);
1369 : if (std::is_same_v<node_depending_on_provider, consumer_tag_to_check>) {
1370 : return true;
1371 : }
1372 :
1373 : using next_nodes_to_check = tmpl::transform<
1374 : tmpl::filter<
1375 : edge_list,
1376 : tmpl::has_source<tmpl::_1, tmpl::pin<node_depending_on_provider>>>,
1377 : tmpl::get_destination<tmpl::_1>>;
1378 :
1379 : return tmpl::as_pack<next_nodes_to_check>([&](auto... nodes) {
1380 : return (... or recurse(recurse, tmpl::type_from<decltype(nodes)>{}));
1381 : });
1382 : };
1383 :
1384 : return tmpl::as_pack<provider_aliases>([&](auto... aliases) {
1385 : return (... or check_dependents(check_dependents,
1386 : tmpl::type_from<decltype(aliases)>{}));
1387 : });
1388 : }
1389 :
1390 : /*!
1391 : * \ingroup DataBoxGroup
1392 : * \brief Retrieve a mutable reference to the item with tag `Tag` from the
1393 : * DataBox.
1394 : *
1395 : * The tag retrieved cannot be used by any compute tags, cannot have
1396 : * subitems, and cannot itself be a subitem. These requirements
1397 : * prevent changes to the retrieved item from affecting any other tags
1398 : * in the DataBox, so it can safely be modified without causing
1399 : * internal inconsistencies.
1400 : */
1401 : template <typename Tag, typename TagList>
1402 1 : SPECTRE_ALWAYS_INLINE auto& get_mutable_reference(
1403 : const gsl::not_null<DataBox<TagList>*> box) {
1404 : return box->template get_mutable_reference<Tag>();
1405 : }
1406 :
1407 : /// @{
1408 : /*!
1409 : * \ingroup DataBoxGroup
1410 : * \brief Check whether the tag \p Consumer depends on the tag \p
1411 : * Provider in a given \p Box.
1412 : *
1413 : * This is equivalent to the question of whether changing \p Provider
1414 : * (through `db::mutate`, updating one of its dependencies, etc.)
1415 : * could change the value of `db::get<Consumer>`. Tags depend on
1416 : * themselves, and an item and its subitems all depend on one another.
1417 : */
1418 : template <typename Consumer, typename Provider, typename Box>
1419 1 : using tag_depends_on =
1420 : std::bool_constant<Box::template tag_depends_on<Consumer, Provider>()>;
1421 :
1422 : template <typename Consumer, typename Provider, typename Box>
1423 1 : constexpr bool tag_depends_on_v =
1424 : tag_depends_on<Consumer, Provider, Box>::value;
1425 : /// @}
1426 :
1427 : /*!
1428 : * \ingroup DataBoxGroup
1429 : * \brief List of Tags to add to the DataBox
1430 : */
1431 : template <typename... Tags>
1432 1 : using AddSimpleTags = tmpl::flatten<tmpl::list<Tags...>>;
1433 :
1434 : /*!
1435 : * \ingroup DataBoxGroup
1436 : * \brief List of Compute Item Tags to add to the DataBox
1437 : */
1438 : template <typename... Tags>
1439 1 : using AddComputeTags = tmpl::flatten<tmpl::list<Tags...>>;
1440 :
1441 : namespace detail {
1442 : template <class TagList>
1443 : struct compute_dbox_type {
1444 : using immutable_item_tags = detail::expand_subitems<
1445 : tmpl::filter<TagList, db::is_immutable_item_tag<tmpl::_1>>>;
1446 : using mutable_item_tags = detail::expand_subitems<
1447 : tmpl::filter<TagList, db::is_mutable_item_tag<tmpl::_1>>>;
1448 : using type = DataBox<tmpl::append<mutable_item_tags, immutable_item_tags>>;
1449 : };
1450 : } // namespace detail
1451 :
1452 : /*!
1453 : * \ingroup DataBoxGroup
1454 : * \brief Returns the type of the DataBox that would be constructed from the
1455 : * `TagList` of tags.
1456 : */
1457 : template <typename TagList>
1458 1 : using compute_databox_type = typename detail::compute_dbox_type<TagList>::type;
1459 :
1460 : /*!
1461 : * \ingroup DataBoxGroup
1462 : * \brief Create a new DataBox
1463 : *
1464 : * \details
1465 : * Creates a new DataBox holding types Tags::type filled with the arguments
1466 : * passed to the function. Compute and reference items must be added so that
1467 : * their dependencies are added before themselves. For example, say you have
1468 : * compute items `A` and `B` where `B` depends on `A`, then you must add them
1469 : * using `db::AddImmutableItemTags<A, B>`.
1470 : *
1471 : * \example
1472 : * \snippet Test_DataBox.cpp create_databox
1473 : *
1474 : * \see create_from
1475 : *
1476 : * \tparam AddMutableItemTags the tags of the mutable items that are being added
1477 : * \tparam AddImmutableItemTags list of \ref ComputeTag "compute item tags" and
1478 : * \ref ReferenceTag "refernce item tags" to add to the DataBox
1479 : * \param args the initial values for the mutable items to add to the DataBox
1480 : */
1481 : template <typename AddMutableItemTags,
1482 : typename AddImmutableItemTags = tmpl::list<>, typename... Args>
1483 1 : SPECTRE_ALWAYS_INLINE constexpr auto create(Args&&... args) {
1484 : static_assert(tt::is_a_v<tmpl::list, AddImmutableItemTags>,
1485 : "AddImmutableItemTags must be a tmpl::list");
1486 : static_assert(tt::is_a_v<tmpl::list, AddMutableItemTags>,
1487 : "AddMutableItemTags must be a tmpl::list");
1488 : static_assert(
1489 : tmpl::all<AddMutableItemTags, is_non_base_tag<tmpl::_1>>::value and
1490 : tmpl::all<AddImmutableItemTags, is_non_base_tag<tmpl::_1>>::value,
1491 : "Can only add tags derived from db::SimpleTag.");
1492 : static_assert(
1493 : tmpl::all<AddMutableItemTags, is_mutable_item_tag<tmpl::_1>>::value,
1494 : "Cannot add any ComputeTags or ReferenceTags in the AddMutableTags list, "
1495 : "must use the AddImmutableItemTags list.");
1496 : static_assert(
1497 : tmpl::all<AddImmutableItemTags, is_immutable_item_tag<tmpl::_1>>::value,
1498 : "Cannot add any SimpleTags in the AddImmutableItemTags list, must use "
1499 : "the "
1500 : "AddMutableItemTags list.");
1501 :
1502 : using mutable_item_tags = detail::expand_subitems<AddMutableItemTags>;
1503 : using immutable_item_tags = detail::expand_subitems<AddImmutableItemTags>;
1504 :
1505 : return db::DataBox<tmpl::append<mutable_item_tags, immutable_item_tags>>(
1506 : AddMutableItemTags{}, AddImmutableItemTags{},
1507 : std::forward<Args>(args)...);
1508 : }
1509 :
1510 : namespace detail {
1511 : template <typename DataBoxTags, typename... TagsToRetrieve>
1512 : constexpr bool check_tags_are_in_databox(
1513 : DataBoxTags /*meta*/, tmpl::list<TagsToRetrieve...> /*meta*/) {
1514 : static_assert(
1515 : (tag_is_retrievable_v<TagsToRetrieve, DataBox<DataBoxTags>> and ...),
1516 : "A desired tag is not in the DataBox. See the first template "
1517 : "argument of tag_is_retrievable_v for the missing tag, and the "
1518 : "second for the available tags.");
1519 : return true;
1520 : }
1521 :
1522 : template <typename... ArgumentTags, typename F, typename BoxTags,
1523 : typename... Args>
1524 : static constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
1525 : tmpl::list<ArgumentTags...> /*meta*/,
1526 : Args&&... args) {
1527 : if constexpr (is_apply_callable_v<
1528 : F,
1529 : tmpl::conditional_t<
1530 : std::is_same_v<ArgumentTags, ::Tags::DataBox>,
1531 : const DataBox<BoxTags>&,
1532 : const_item_type<ArgumentTags, BoxTags>>...,
1533 : Args...>) {
1534 : return F::apply(::db::get<ArgumentTags>(box)...,
1535 : std::forward<Args>(args)...);
1536 : } else if constexpr (::tt::is_callable_v<
1537 : std::remove_pointer_t<F>,
1538 : tmpl::conditional_t<
1539 : std::is_same_v<ArgumentTags, ::Tags::DataBox>,
1540 : const DataBox<BoxTags>&,
1541 : const_item_type<ArgumentTags, BoxTags>>...,
1542 : Args...>) {
1543 : return std::forward<F>(f)(::db::get<ArgumentTags>(box)...,
1544 : std::forward<Args>(args)...);
1545 : } else {
1546 : error_function_not_callable<
1547 : std::remove_pointer_t<F>,
1548 : tmpl::conditional_t<std::is_same_v<ArgumentTags, ::Tags::DataBox>,
1549 : const DataBox<BoxTags>&,
1550 : const_item_type<ArgumentTags, BoxTags>>...,
1551 : Args...>();
1552 : }
1553 : }
1554 : } // namespace detail
1555 :
1556 : /// @{
1557 : /*!
1558 : * \ingroup DataBoxGroup
1559 : * \brief Apply the invokable `f` with argument Tags `TagsList` from
1560 : * DataBox `box`
1561 : *
1562 : * \details
1563 : * `f` must either be invokable with the arguments of type
1564 : * `db::const_item_type<TagsList>..., Args...` where the first pack expansion
1565 : * is over the elements in the type list `ArgumentTags`, or have a static
1566 : * `apply` function that is callable with the same types.
1567 : * If the class that implements the static `apply` functions also provides an
1568 : * `argument_tags` typelist, then it is used and no explicit `ArgumentTags`
1569 : * template parameter should be specified.
1570 : *
1571 : * \usage
1572 : * Given a function `func` that takes arguments of types
1573 : * `T1`, `T2`, `A1` and `A2`. Let the Tags for the quantities of types `T1` and
1574 : * `T2` in the DataBox `box` be `Tag1` and `Tag2`, and objects `a1` of type
1575 : * `A1` and `a2` of type `A2`, then
1576 : * \code
1577 : * auto result = db::apply<tmpl::list<Tag1, Tag2>>(func, box, a1, a2);
1578 : * \endcode
1579 : * \return `decltype(func(db::get<Tag1>(box), db::get<Tag2>(box), a1, a2))`
1580 : *
1581 : * \semantics
1582 : * For tags `Tags...` in a DataBox `box`, and a function `func` that takes
1583 : * `sizeof...(Tags)` arguments of types `db::const_item_type<Tags>...`, and
1584 : * `sizeof...(Args)` arguments of types `Args...`,
1585 : * \code
1586 : * result = func(box, db::get<Tags>(box)..., args...);
1587 : * \endcode
1588 : *
1589 : * \example
1590 : * \snippet Test_DataBox.cpp apply_example
1591 : * Using a struct with an `apply` method:
1592 : * \snippet Test_DataBox.cpp apply_struct_example
1593 : * If the class `F` has no state, you can also use the stateless overload of
1594 : * `apply`: \snippet Test_DataBox.cpp apply_stateless_struct_example
1595 : *
1596 : * \see DataBox
1597 : * \tparam ArgumentTags typelist of Tags in the order that they are to be passed
1598 : * to `f`
1599 : * \tparam F The invokable to apply
1600 : */
1601 : template <typename ArgumentTags, typename F, typename BoxTags, typename... Args>
1602 1 : SPECTRE_ALWAYS_INLINE constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
1603 : Args&&... args) {
1604 : detail::check_tags_are_in_databox(
1605 : BoxTags{}, tmpl::remove<ArgumentTags, ::Tags::DataBox>{});
1606 : return detail::apply(std::forward<F>(f), box, ArgumentTags{},
1607 : std::forward<Args>(args)...);
1608 : }
1609 :
1610 : template <typename F, typename BoxTags, typename... Args>
1611 1 : SPECTRE_ALWAYS_INLINE constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
1612 : Args&&... args) {
1613 : return apply<typename std::decay_t<F>::argument_tags>(
1614 : std::forward<F>(f), box, std::forward<Args>(args)...);
1615 : }
1616 :
1617 : template <typename F, typename BoxTags, typename... Args>
1618 1 : SPECTRE_ALWAYS_INLINE constexpr auto apply(const DataBox<BoxTags>& box,
1619 : Args&&... args) {
1620 : return apply(F{}, box, std::forward<Args>(args)...);
1621 : }
1622 : /// @}
1623 :
1624 : namespace detail {
1625 : template <typename Tag, typename BoxTags>
1626 : using tag_return_type =
1627 : tmpl::conditional_t<std::is_same_v<Tag, ::Tags::DataBox>,
1628 : db::DataBox<BoxTags>, typename Tag::type>;
1629 :
1630 : template <typename... ReturnTags, typename... ArgumentTags, typename F,
1631 : typename BoxTags, typename... Args>
1632 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
1633 : F&& f, const gsl::not_null<db::DataBox<BoxTags>*> box,
1634 : tmpl::list<ReturnTags...> /*meta*/, tmpl::list<ArgumentTags...> /*meta*/,
1635 : Args&&... args) {
1636 : if constexpr (sizeof...(ReturnTags) == 0) {
1637 : return apply<tmpl::list<ArgumentTags...>>(std::forward<F>(f), *box,
1638 : std::forward<Args>(args)...);
1639 : } else {
1640 : detail::check_tags_are_in_databox(BoxTags{}, tmpl::list<ReturnTags...>{});
1641 : detail::check_tags_are_in_databox(BoxTags{}, tmpl::list<ArgumentTags...>{});
1642 : static_assert(not(... or std::is_same_v<ArgumentTags, Tags::DataBox>),
1643 : "Cannot pass Tags::DataBox to mutate_apply when mutating "
1644 : "since the db::get won't work inside mutate_apply.");
1645 : if constexpr (is_apply_callable_v<
1646 : F,
1647 : const gsl::not_null<
1648 : tag_return_type<ReturnTags, BoxTags>*>...,
1649 : const_item_type<ArgumentTags, BoxTags>..., Args...>) {
1650 : return ::db::mutate<ReturnTags...>(
1651 : [](const gsl::not_null<
1652 : tag_return_type<ReturnTags, BoxTags>*>... mutated_items,
1653 : const_item_type<ArgumentTags, BoxTags>... args_items,
1654 : decltype(std::forward<Args>(args))... l_args) {
1655 : return std::decay_t<F>::apply(mutated_items..., args_items...,
1656 : std::forward<Args>(l_args)...);
1657 : },
1658 : box, db::get<ArgumentTags>(*box)..., std::forward<Args>(args)...);
1659 : } else if constexpr (
1660 : ::tt::is_callable_v<
1661 : F, const gsl::not_null<tag_return_type<ReturnTags, BoxTags>*>...,
1662 : const_item_type<ArgumentTags, BoxTags>..., Args...>) {
1663 : return ::db::mutate<ReturnTags...>(f, box, db::get<ArgumentTags>(*box)...,
1664 : std::forward<Args>(args)...);
1665 : } else {
1666 : error_function_not_callable<
1667 : F, gsl::not_null<tag_return_type<ReturnTags, BoxTags>*>...,
1668 : const_item_type<ArgumentTags, BoxTags>..., Args...>();
1669 : }
1670 : }
1671 : }
1672 : } // namespace detail
1673 :
1674 : /// @{
1675 : /*!
1676 : * \ingroup DataBoxGroup
1677 : * \brief Apply the invokable `f` mutating items `MutateTags` and taking as
1678 : * additional arguments `ArgumentTags` and `args`.
1679 : *
1680 : * \details
1681 : * `f` must either be invokable with the arguments of type
1682 : * `gsl::not_null<db::item_type<MutateTags>*>...,
1683 : * db::const_item_type<ArgumentTags>..., Args...`
1684 : * where the first two pack expansions are over the elements in the typelists
1685 : * `MutateTags` and `ArgumentTags`, or have a static `apply` function that is
1686 : * callable with the same types. If the type of `f` specifies `return_tags` and
1687 : * `argument_tags` typelists, these are used for the `MutateTags` and
1688 : * `ArgumentTags`, respectively.
1689 : *
1690 : * Any return values of the invokable `f` are forwarded as returns to the
1691 : * `mutate_apply` call.
1692 : *
1693 : * \note If `MutateTags` is empty this will forward to `db::apply`, so
1694 : * `::Tags::DataBox` will be available. Otherwise it will call
1695 : * `db::mutate`. See those functions for more details on retrieving
1696 : * the entire box.
1697 : *
1698 : * \example
1699 : * An example of using `mutate_apply` with a lambda:
1700 : * \snippet Test_DataBox.cpp mutate_apply_lambda_example
1701 : *
1702 : * An example of a class with a static `apply` function
1703 : * \snippet Test_DataBox.cpp mutate_apply_struct_definition_example
1704 : * and how to use `mutate_apply` with the above class
1705 : * \snippet Test_DataBox.cpp mutate_apply_struct_example_stateful
1706 : * Note that the class exposes `return_tags` and `argument_tags` typelists, so
1707 : * we don't specify the template parameters explicitly.
1708 : * If the class `F` has no state, like in this example,
1709 : * \snippet Test_DataBox.cpp mutate_apply_struct_definition_example
1710 : * you can also use the stateless overload of `mutate_apply`:
1711 : * \snippet Test_DataBox.cpp mutate_apply_struct_example_stateless
1712 : *
1713 : * \tparam MutateTags typelist of Tags to mutate
1714 : * \tparam ArgumentTags typelist of additional items to retrieve from the
1715 : * DataBox
1716 : * \tparam F The invokable to apply
1717 : */
1718 : template <typename MutateTags, typename ArgumentTags, typename F,
1719 : typename BoxTags, typename... Args>
1720 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
1721 : F&& f, const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) {
1722 : return detail::mutate_apply(std::forward<F>(f), box, MutateTags{},
1723 : ArgumentTags{}, std::forward<Args>(args)...);
1724 : }
1725 :
1726 : template <typename F, typename BoxTags, typename... Args>
1727 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
1728 : F&& f, const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) {
1729 : return mutate_apply<typename std::decay_t<F>::return_tags,
1730 : typename std::decay_t<F>::argument_tags>(
1731 : std::forward<F>(f), box, std::forward<Args>(args)...);
1732 : }
1733 :
1734 : template <typename F, typename BoxTags, typename... Args>
1735 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
1736 : const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) {
1737 : return mutate_apply(F{}, box, std::forward<Args>(args)...);
1738 : }
1739 : /// @}
1740 : } // namespace db
1741 :
1742 : template <typename TagsList>
1743 0 : std::ostream& operator<<(std::ostream& os, const db::DataBox<TagsList>& box) {
1744 : os << box.print_types() << "\n";
1745 : os << box.print_items() << "\n";
1746 : return os;
1747 : }
|