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