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