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 : TagGraphs result{};
801 : // Compute graphs for retrieving tags
802 : const auto process_tag = [&result]<typename ConcreteTag>(
803 : tmpl::type_<ConcreteTag> /*meta*/) {
804 : const std::string concrete_tag_name = pretty_type::get_name<ConcreteTag>();
805 : tmpl::for_each<typename detail::tag_and_bases<ConcreteTag>::type>(
806 : [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {
807 : const std::string tag_name = pretty_type::get_name<Tag>();
808 : detail::set_or_null_if_ambiguous(
809 : make_not_null(&result.tag_retrieval_functions), tag_name,
810 : &DataBox::template get_item_as_void_pointer<ConcreteTag>);
811 : result.tag_aliases[concrete_tag_name].push_back(tag_name);
812 : });
813 : };
814 : (void)process_tag;
815 : EXPAND_PACK_LEFT_TO_RIGHT(process_tag(tmpl::type_<Tags>{}));
816 :
817 : // Compute graphs for resetting compute tags
818 : //
819 : //
820 : // Subitems are a bit tricky. We need:
821 : // - If a parent tag is mutated, need to track through all subitems
822 : // - If a subitem is mutated, need to track through parent tag and all
823 : // subitems
824 : tmpl::for_each<
825 : tmpl::append<mutable_item_parent_tags, reference_item_parent_tags,
826 : compute_item_parent_tags>>([&result](auto tag_v) {
827 : using parent_tag = tmpl::type_from<decltype(tag_v)>;
828 : using subitems = typename Subitems<parent_tag>::type;
829 : const std::string parent_tag_name = pretty_type::get_name<parent_tag>();
830 : const std::vector<std::string> subitem_tag_names =
831 : pretty_type::vector_of_get_names(subitems{});
832 : for (const std::string& subitem_name : subitem_tag_names) {
833 : result.parent_to_subitem_tags[parent_tag_name].push_back(subitem_name);
834 : ASSERT(result.subitem_to_parent_tag.find(subitem_name) ==
835 : result.subitem_to_parent_tag.end(),
836 : "Trying to insert subitem tag "
837 : << subitem_name
838 : << " for a second time. This is an "
839 : "internal inconsistency bug.\n");
840 : result.subitem_to_parent_tag[subitem_name] = parent_tag_name;
841 : }
842 : });
843 :
844 : tmpl::for_each<tmpl::append<reference_item_tags, compute_item_tags>>(
845 : [&result](auto tag_v) {
846 : using compute_tag = tmpl::type_from<decltype(tag_v)>;
847 : const std::string tag_name = pretty_type::get_name<compute_tag>();
848 : const std::vector<std::string> argument_tags =
849 : pretty_type::vector_of_get_names(
850 : typename compute_tag::argument_tags{});
851 : for (const std::string& argument_tag : argument_tags) {
852 : result.tags_and_dependents[argument_tag].push_back(tag_name);
853 : }
854 : result.tags_and_reset_functions[tag_name] =
855 : &DataBox::template reset_compute_item<compute_tag>;
856 : });
857 :
858 : // Set mutation function
859 : tmpl::for_each<mutable_item_tags>([&result]<typename MutableTag>(
860 : tmpl::type_<MutableTag> /*meta*/) {
861 : tmpl::for_each<typename detail::tag_and_bases<MutableTag>::type>(
862 : [&result]<typename Tag>(tmpl::type_<Tag> /*meta*/) {
863 : const std::string tag_name = pretty_type::get_name<Tag>();
864 : detail::set_or_null_if_ambiguous(
865 : make_not_null(&result.tag_mutate_functions), tag_name,
866 : &DataBox::template get_item_as_void_pointer_for_mutate<
867 : MutableTag>);
868 : detail::set_or_null_if_ambiguous(
869 : make_not_null(&result.mutate_mutable_subitems_functions),
870 : tag_name,
871 : static_cast<void (DataBox::*)()>(
872 : &DataBox::template mutate_mutable_subitems<MutableTag>));
873 : detail::set_or_null_if_ambiguous(
874 : make_not_null(&result.reset_compute_items_after_mutate_functions),
875 : tag_name,
876 : &DataBox::template reset_compute_items_after_mutate<MutableTag>);
877 : });
878 : });
879 : return result;
880 : }
881 :
882 : template <typename... Tags>
883 : void DataBox<tmpl::list<Tags...>>::reset_compute_items(
884 : const std::string& item_name) {
885 : // If the compute tag was evaluated before reset, then we reset dependent
886 : // compute tags.
887 : const auto aliases = tag_graphs_.tag_aliases.find(item_name);
888 : ASSERT(aliases != tag_graphs_.tag_aliases.end(),
889 : "Internal error: Item " << item_name << " has no names");
890 : for (const std::string& alias : aliases->second) {
891 : const auto dependents = tag_graphs_.tags_and_dependents.find(alias);
892 : if (dependents != tag_graphs_.tags_and_dependents.end()) {
893 : for (const std::string& dependent_item_name : dependents->second) {
894 : ASSERT(tag_graphs_.tags_and_reset_functions.find(dependent_item_name) !=
895 : tag_graphs_.tags_and_reset_functions.end(),
896 : "Item " << dependent_item_name
897 : << " does not have a reset function.");
898 : if ((this->*(tag_graphs_.tags_and_reset_functions)
899 : .at(dependent_item_name))()) {
900 : // If the compute tag was evaluated, then we need to check the items
901 : // that depend on it.
902 : reset_compute_items(dependent_item_name);
903 : }
904 : }
905 : }
906 : }
907 : }
908 :
909 : template <typename... Tags>
910 : template <typename MutatedTag>
911 : void DataBox<tmpl::list<Tags...>>::reset_compute_items_after_mutate() {
912 : DEBUG_STATIC_ASSERT(
913 : tmpl::list_contains_v<mutable_item_tags, MutatedTag>,
914 : "internal inconsistency bug: reset_compute_items_after_mutate on "
915 : "invalid tag");
916 :
917 : // If a subitem is edited, reset the parent instead. (The item
918 : // itself will be reset as a subitem of the parent below.)
919 : static const std::string tag_to_reset = []() {
920 : if constexpr (tmpl::list_contains_v<mutable_subitem_tags, MutatedTag>) {
921 : const std::string mutated_tag = pretty_type::get_name<MutatedTag>();
922 : ASSERT(tag_graphs_.subitem_to_parent_tag.find(mutated_tag) !=
923 : tag_graphs_.subitem_to_parent_tag.end(),
924 : "Expected to find a parent tag of "
925 : << mutated_tag
926 : << " but did not. This is an internal inconsistency bug.");
927 : return tag_graphs_.subitem_to_parent_tag.at(mutated_tag);
928 : } else {
929 : return pretty_type::get_name<MutatedTag>();
930 : }
931 : }();
932 :
933 : reset_compute_items(tag_to_reset);
934 :
935 : // If the tag has subitems, reset those too.
936 : if (const auto parent_tag_it =
937 : tag_graphs_.parent_to_subitem_tags.find(tag_to_reset);
938 : parent_tag_it != tag_graphs_.parent_to_subitem_tags.end()) {
939 : for (const auto& subitem_tag : parent_tag_it->second) {
940 : reset_compute_items(subitem_tag);
941 : }
942 : }
943 : }
944 :
945 : template <typename... Tags>
946 : void DataBox<tmpl::list<Tags...>>::mutate_mutable_subitems(
947 : const std::string& tag_name) {
948 : ASSERT(tag_graphs_.mutate_mutable_subitems_functions.find(tag_name) !=
949 : tag_graphs_.mutate_mutable_subitems_functions.end(),
950 : "Can't find tag " << tag_name
951 : << " when mutating. Either the tag is not mutable "
952 : "or this is an internal inconsistency bug.\n");
953 : (this->*tag_graphs_.mutate_mutable_subitems_functions.at(tag_name))();
954 : }
955 :
956 : template <typename... Tags>
957 : void DataBox<tmpl::list<Tags...>>::reset_compute_items_after_mutate(
958 : const std::string& tag_name) {
959 : ASSERT(
960 : tag_graphs_.reset_compute_items_after_mutate_functions.find(tag_name) !=
961 : tag_graphs_.reset_compute_items_after_mutate_functions.end(),
962 : "Can't find tag " << tag_name
963 : << " when mutating. Either the tag is not mutable "
964 : "or this is an internal inconsistency bug.\n");
965 : (this->*tag_graphs_.reset_compute_items_after_mutate_functions.at(
966 : tag_name))();
967 : }
968 :
969 : template <typename... Tags>
970 : template <typename Tag>
971 : const void* DataBox<tmpl::list<Tags...>>::get_item_as_void_pointer() const {
972 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
973 : return reinterpret_cast<const void*>(&this->template get<Tag>());
974 : }
975 :
976 : template <typename... Tags>
977 : template <typename Tag>
978 : void* DataBox<tmpl::list<Tags...>>::get_item_as_void_pointer_for_mutate() {
979 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
980 : return reinterpret_cast<void*>(
981 : &this->template get_item<
982 : detail::first_matching_tag<tmpl::list<Tags...>, Tag>>());
983 : }
984 :
985 : template <typename... Tags>
986 : const void* DataBox<tmpl::list<Tags...>>::get_item_by_name(
987 : const std::string& tag_name) const {
988 : ASSERT(tag_graphs_.tag_retrieval_functions.find(tag_name) !=
989 : tag_graphs_.tag_retrieval_functions.end(),
990 : "Tag with name " << tag_name
991 : << " is not found in the DataBox. Known tags are:\n"
992 : << keys_of(tag_graphs_.tag_retrieval_functions));
993 : ASSERT(tag_graphs_.tag_retrieval_functions.at(tag_name) != nullptr,
994 : "Tag with name " << tag_name
995 : << " is in the DataBox more than once and so "
996 : "cannot be retrieved by name.");
997 :
998 : return (this->*tag_graphs_.tag_retrieval_functions.at(tag_name))();
999 : }
1000 :
1001 : template <typename... Tags>
1002 : void* DataBox<tmpl::list<Tags...>>::mutate_item_by_name(
1003 : const std::string& tag_name) {
1004 : ASSERT(tag_graphs_.tag_mutate_functions.find(tag_name) !=
1005 : tag_graphs_.tag_mutate_functions.end(),
1006 : "Cannot mutate tag: " << tag_name << ". Known tags are:\n"
1007 : << keys_of(tag_graphs_.tag_mutate_functions));
1008 : return (this->*tag_graphs_.tag_mutate_functions.at(tag_name))();
1009 : }
1010 :
1011 : template <typename... Tags>
1012 : bool DataBox<tmpl::list<Tags...>>::lock_box_for_mutate() {
1013 : const bool temp = mutate_locked_box_;
1014 : mutate_locked_box_ = true;
1015 : return temp;
1016 : }
1017 :
1018 : template <typename... Tags>
1019 : void DataBox<tmpl::list<Tags...>>::unlock_box_after_mutate() {
1020 : mutate_locked_box_ = false;
1021 : }
1022 :
1023 : ////////////////////////////////////////////////////////////////
1024 : // Mutating items in the DataBox
1025 : // Classes and functions necessary for db::mutate to work
1026 :
1027 : template <typename... Tags>
1028 : template <typename ImmutableItemTag>
1029 : SPECTRE_ALWAYS_INLINE bool DataBox<tmpl::list<Tags...>>::reset_compute_item() {
1030 : // reference items do not need to be reset, but it still needs to be handled
1031 : // here. Partly just so we get an error if we are calling this function
1032 : // incorrectly and because we need to call this function during resets
1033 : // to get dependency tracking correct when compute tags depend on reference
1034 : // tags.
1035 : if constexpr (db::is_compute_tag_v<ImmutableItemTag>) {
1036 : const bool was_evaluated = get_item<ImmutableItemTag>().evaluated();
1037 : get_item<ImmutableItemTag>().reset();
1038 : return was_evaluated;
1039 : } else if constexpr (db::is_reference_tag_v<ImmutableItemTag>) {
1040 : return true;
1041 : }
1042 : ERROR(
1043 : "Trying to reset a tag that is neither a compute or reference tag. This "
1044 : "is an implementation bug. A decision needs to be made as to whether the "
1045 : "tag was previously evaluated or not. If it was not, then anything that "
1046 : "depends on this tag will not be reset.");
1047 : return false;
1048 : }
1049 :
1050 : template <typename... Tags>
1051 : template <typename ParentTag, typename... Subtags>
1052 : SPECTRE_ALWAYS_INLINE constexpr void
1053 : db::DataBox<tmpl::list<Tags...>>::mutate_mutable_subitems(
1054 : tmpl::list<Subtags...> /*meta*/) {
1055 : const auto helper = [this](auto tag_v) {
1056 : (void)this; // Compiler bug warns about unused this capture
1057 : using tag = decltype(tag_v);
1058 : Subitems<ParentTag>::template create_item<tag>(
1059 : make_not_null(&get_item<ParentTag>().mutate()),
1060 : make_not_null(&get_item<tag>().mutate()));
1061 : };
1062 :
1063 : EXPAND_PACK_LEFT_TO_RIGHT(helper(Subtags{}));
1064 : }
1065 :
1066 : template <typename... Tags>
1067 : SPECTRE_ALWAYS_INLINE constexpr void
1068 : db::DataBox<tmpl::list<Tags...>>::reset_all_subitems() {
1069 : tmpl::for_each<mutable_item_tags>([this](auto tag) {
1070 : using Tag = tmpl::type_from<decltype(tag)>;
1071 : this->mutate_mutable_subitems<Tag>(typename Subitems<Tag>::type{});
1072 : });
1073 : }
1074 : /// \endcond
1075 :
1076 : /*!
1077 : * \ingroup DataBoxGroup
1078 : * \brief Allows changing the state of one or more non-computed elements in
1079 : * the DataBox
1080 : *
1081 : * `mutate()`'s second argument is the DataBox from which to retrieve the tags
1082 : * `MutateTags`. The objects corresponding to the `MutateTags` are then passed
1083 : * to `invokable`, which is a lambda or a function object taking as many
1084 : * arguments as there are `MutateTags` and with the arguments being of types
1085 : * `gsl::not_null<db::item_type<MutateTags>*>...`. Inside the `invokable` no
1086 : * items can be retrieved from the DataBox `box`. This is to avoid confusing
1087 : * subtleties with order of evaluation of compute items, as well as dangling
1088 : * references. If an `invokable` needs read access to items in `box` they should
1089 : * be passed as additional arguments to `mutate`. Capturing them by reference in
1090 : * a lambda does not work because of a bug in GCC 6.3 and earlier. For a
1091 : * function object the read-only items can also be stored as const references
1092 : * inside the object by passing `db::get<TAG>(t)` to the constructor.
1093 : *
1094 : * \example
1095 : * \snippet Test_DataBox.cpp databox_mutate_example
1096 : *
1097 : * The `invokable` may have function return values, and any returns are
1098 : * forwarded as returns to the `db::mutate` call.
1099 : *
1100 : * For convenience in generic programming, if `::Tags::DataBox` is
1101 : * passed as the sole `MutateTags` parameter, this function will
1102 : * simply call its first argument with its remaining arguments.
1103 : * Except for this case, `::Tags::DataBox` is not usable with this
1104 : * function.
1105 : *
1106 : * \warning Using `db::mutate` returns to obtain non-const references or
1107 : * pointers to box items is potentially very dangerous. The \ref DataBoxGroup
1108 : * "DataBox" cannot track any subsequent changes to quantities that have been
1109 : * "unsafely" extracted in this manner, so we consider it undefined behavior to
1110 : * return pointers or references to \ref DataBoxGroup "DataBox" contents.
1111 : */
1112 : template <typename... MutateTags, typename TagList, typename Invokable,
1113 : typename... Args>
1114 1 : decltype(auto) mutate(Invokable&& invokable,
1115 : const gsl::not_null<DataBox<TagList>*> box,
1116 : Args&&... args) {
1117 : if constexpr ((... or std::is_same_v<MutateTags, Tags::DataBox>)) {
1118 : // This branch doesn't directly access the box, so no locking or
1119 : // resetting is necessary.
1120 : static_assert(
1121 : std::is_same_v<tmpl::list<MutateTags...>, tmpl::list<Tags::DataBox>>,
1122 : "Cannot mutate other tags when obtaining the mutable DataBox.");
1123 : return invokable(box, std::forward<Args>(args)...);
1124 : } else {
1125 : static_assert(
1126 : tmpl2::flat_all_v<
1127 : detail::has_unique_matching_tag_v<TagList, MutateTags>...>,
1128 : "One of the tags being mutated could not be found in the DataBox or "
1129 : "is a base tag identifying more than one tag.");
1130 : static_assert(tmpl2::flat_all_v<tmpl::list_contains_v<
1131 : typename DataBox<TagList>::mutable_item_tags,
1132 : detail::first_matching_tag<TagList, MutateTags>>...>,
1133 : "Can only mutate mutable items");
1134 : if (UNLIKELY(box->mutate_locked_box_)) {
1135 : ERROR(
1136 : "Unable to mutate a DataBox that is already being mutated. This "
1137 : "error occurs when mutating a DataBox from inside the invokable "
1138 : "passed to the mutate function.");
1139 : }
1140 :
1141 : const CleanupRoutine unlock_box = [&box]() {
1142 : box->mutate_locked_box_ = false;
1143 : EXPAND_PACK_LEFT_TO_RIGHT(
1144 : box->template mutate_mutable_subitems<
1145 : detail::first_matching_tag<TagList, MutateTags>>());
1146 : EXPAND_PACK_LEFT_TO_RIGHT(
1147 : box->template reset_compute_items_after_mutate<
1148 : detail::first_matching_tag<TagList, MutateTags>>());
1149 : };
1150 : box->mutate_locked_box_ = true;
1151 : return invokable(
1152 : make_not_null(&box->template get_item<
1153 : detail::first_matching_tag<TagList, MutateTags>>()
1154 : .mutate())...,
1155 : std::forward<Args>(args)...);
1156 : }
1157 : }
1158 :
1159 : ////////////////////////////////////////////////////////////////
1160 : // Retrieving items from the DataBox
1161 :
1162 : /// \cond
1163 : template <typename... Tags>
1164 : template <typename ComputeTag, typename... ArgumentTags>
1165 : void DataBox<tmpl::list<Tags...>>::evaluate_compute_item(
1166 : tmpl::list<ArgumentTags...> /*meta*/) const {
1167 : get_item<ComputeTag>().evaluate(get<ArgumentTags>()...);
1168 : }
1169 :
1170 : template <typename... Tags>
1171 : template <typename ReferenceTag, typename... ArgumentTags>
1172 : const auto& DataBox<tmpl::list<Tags...>>::get_reference_item(
1173 : tmpl::list<ArgumentTags...> /*meta*/) const {
1174 : return ReferenceTag::get(get<ArgumentTags>()...);
1175 : }
1176 :
1177 : template <typename... Tags>
1178 : template <typename Tag>
1179 : const auto& DataBox<tmpl::list<Tags...>>::get() const {
1180 : if constexpr (std::is_same_v<Tag, ::Tags::DataBox>) {
1181 : if (UNLIKELY(mutate_locked_box_)) {
1182 : ERROR(
1183 : "Unable to retrieve a (compute) item 'DataBox' from the DataBox from "
1184 : "within a call to mutate. You must pass these either through the "
1185 : "capture list of the lambda or the constructor of a class, this "
1186 : "restriction exists to avoid complexity.");
1187 : }
1188 : return *this;
1189 : } else if constexpr (std::is_same_v<Tag, Parallel::Tags::Metavariables>) {
1190 : static_assert(not std::is_same_v<metavars_tag, NoSuchType>);
1191 : return get_item<metavars_tag>().get();
1192 : } else {
1193 : DEBUG_STATIC_ASSERT(
1194 : not detail::has_no_matching_tag_v<tags_list, Tag>,
1195 : "Found no tags in the DataBox that match the tag being retrieved.");
1196 : DEBUG_STATIC_ASSERT(
1197 : detail::has_unique_matching_tag_v<tags_list, Tag>,
1198 : "Found more than one tag in the DataBox that matches the tag "
1199 : "being retrieved. This happens because more than one tag with the same "
1200 : "base (class) tag was added to the DataBox.");
1201 : using item_tag = detail::first_matching_tag<tags_list, Tag>;
1202 : if (UNLIKELY(mutate_locked_box_)) {
1203 : ERROR("Unable to retrieve a (compute) item '"
1204 : << db::tag_name<item_tag>()
1205 : << "' from the DataBox from within a "
1206 : "call to mutate. You must pass these either through the capture "
1207 : "list of the lambda or the constructor of a class, this "
1208 : "restriction exists to avoid complexity.");
1209 : }
1210 : if constexpr (detail::Item<item_tag>::item_type ==
1211 : detail::ItemType::Reference) {
1212 : return get_reference_item<item_tag>(typename item_tag::argument_tags{});
1213 : } else {
1214 : if constexpr (detail::Item<item_tag>::item_type ==
1215 : detail::ItemType::Compute) {
1216 : if (not get_item<item_tag>().evaluated()) {
1217 : evaluate_compute_item<item_tag>(typename item_tag::argument_tags{});
1218 : }
1219 : }
1220 : if constexpr (tt::is_a_v<std::unique_ptr, typename item_tag::type>) {
1221 : return *(get_item<item_tag>().get());
1222 : } else {
1223 : return get_item<item_tag>().get();
1224 : }
1225 : }
1226 : }
1227 : }
1228 : /// \endcond
1229 :
1230 : /*!
1231 : * \ingroup DataBoxGroup
1232 : * \brief Retrieve the item with tag `Tag` from the DataBox
1233 : * \requires Type `Tag` is one of the Tags corresponding to an object stored in
1234 : * the DataBox
1235 : *
1236 : * \return The object corresponding to the tag `Tag`
1237 : */
1238 : template <typename Tag, typename TagList>
1239 1 : SPECTRE_ALWAYS_INLINE const auto& get(const DataBox<TagList>& box) {
1240 : return box.template get<Tag>();
1241 : }
1242 :
1243 : ////////////////////////////////////////////////////////////////
1244 : // Copy mutable creation items from the DataBox
1245 :
1246 : /// \cond
1247 : template <typename... Tags>
1248 : template <typename Tag>
1249 : SPECTRE_ALWAYS_INLINE auto DataBox<tmpl::list<Tags...>>::copy_item() const {
1250 : using item_tag = detail::first_matching_tag<tags_list, Tag>;
1251 : using item_type = typename item_tag::type;
1252 : static_assert(tmpl::list_contains_v<mutable_item_creation_tags, item_tag>,
1253 : "Can only copy mutable creation items");
1254 : return serialize_and_deserialize<item_type>(get_item<item_tag>().get());
1255 : }
1256 :
1257 : template <typename... DbTags>
1258 : template <typename... CopiedItemsTags>
1259 : tuples::TaggedTuple<CopiedItemsTags...>
1260 : DataBox<tmpl::list<DbTags...>>::copy_items(
1261 : tmpl::list<CopiedItemsTags...> /*meta*/) const {
1262 : return tuples::TaggedTuple<CopiedItemsTags...>{
1263 : copy_item<CopiedItemsTags>()...};
1264 : }
1265 : /// \endcond
1266 :
1267 : /*!
1268 : * \ingroup DataBoxGroup
1269 : * \brief Copy the items from the DataBox into a TaggedTuple
1270 : *
1271 : * \return The objects corresponding to CopiedItemsTagList
1272 : *
1273 : * \note The tags in CopiedItemsTagList must be a subset of
1274 : * the mutable_item_creation_tags of the DataBox
1275 : */
1276 : template <typename CopiedItemsTagList, typename DbTagList>
1277 1 : SPECTRE_ALWAYS_INLINE auto copy_items(const DataBox<DbTagList>& box) {
1278 : return box.copy_items(CopiedItemsTagList{});
1279 : }
1280 :
1281 : ////////////////////////////////////////////////////////////////
1282 : // Get mutable reference from the DataBox
1283 : /// \cond
1284 : template <typename... Tags>
1285 : template <typename Tag>
1286 : auto& DataBox<tmpl::list<Tags...>>::get_mutable_reference() {
1287 : DEBUG_STATIC_ASSERT(
1288 : not detail::has_no_matching_tag_v<tmpl::list<Tags...>, Tag>,
1289 : "Found no tags in the DataBox that match the tag being retrieved.");
1290 : DEBUG_STATIC_ASSERT(
1291 : detail::has_unique_matching_tag_v<tmpl::list<Tags...>, Tag>,
1292 : "Found more than one tag in the DataBox that matches the tag "
1293 : "being retrieved. This happens because more than one tag with the same "
1294 : "base (class) tag was added to the DataBox.");
1295 :
1296 : using item_tag = detail::first_matching_tag<tmpl::list<Tags...>, Tag>;
1297 :
1298 : DEBUG_STATIC_ASSERT(tmpl::list_contains_v<mutable_item_tags, item_tag>,
1299 : "Can only mutate mutable items");
1300 :
1301 : DEBUG_STATIC_ASSERT(
1302 : not (... or
1303 : tmpl::list_contains_v<typename Subitems<Tags>::type, item_tag>),
1304 : "Cannot extract references to subitems");
1305 : DEBUG_STATIC_ASSERT(not detail::has_subitems_v<item_tag>,
1306 : "Cannot extract references to items with subitems.");
1307 :
1308 : DEBUG_STATIC_ASSERT(
1309 : tmpl::none<edge_list, std::is_same<tmpl::pin<item_tag>,
1310 : tmpl::get_source<tmpl::_1>>>::value,
1311 : "Cannot extract references to items used by compute items.");
1312 :
1313 : return get_item<item_tag>().mutate();
1314 : }
1315 :
1316 : template <typename... Tags>
1317 : template <typename Consumer, typename Provider>
1318 : constexpr bool DataBox<tmpl::list<Tags...>>::tag_depends_on() {
1319 : // We need to check for things depending on the passed tag, any
1320 : // subitems, and the parent item if we were passed a subitem. These
1321 : // dependencies are handled internally by the mutation functions and
1322 : // not encoded in the graph.
1323 : using provider_aliases =
1324 : tmpl::push_front<typename Subitems<Provider>::type,
1325 : creation_tag<Provider, DataBox<tmpl::list<Tags...>>>>;
1326 :
1327 : // We have to replace subitems with their parents here because
1328 : // subitems of compute tags sometimes get graph edges from their
1329 : // parents and sometimes do not, depending on if they have
1330 : // dependencies.
1331 : using consumer_tag_to_check =
1332 : creation_tag<Consumer, DataBox<tmpl::list<Tags...>>>;
1333 :
1334 : // We cannot recursively call the function we are in, because we
1335 : // need to avoid the normalization done above. Otherwise we could
1336 : // end up in a loop when destination of an edge normalizes to its
1337 : // source.
1338 :
1339 : // Lambdas cannot capture themselves, but they can take themselves
1340 : // as an argument.
1341 : auto check_dependents = [](auto&& recurse,
1342 : auto node_depending_on_provider_v) {
1343 : using node_depending_on_provider = decltype(node_depending_on_provider_v);
1344 : if (std::is_same_v<node_depending_on_provider, consumer_tag_to_check>) {
1345 : return true;
1346 : }
1347 :
1348 : using next_nodes_to_check = tmpl::transform<
1349 : tmpl::filter<
1350 : edge_list,
1351 : tmpl::has_source<tmpl::_1, tmpl::pin<node_depending_on_provider>>>,
1352 : tmpl::get_destination<tmpl::_1>>;
1353 :
1354 : return tmpl::as_pack<next_nodes_to_check>([&](auto... nodes) {
1355 : return (... or recurse(recurse, tmpl::type_from<decltype(nodes)>{}));
1356 : });
1357 : };
1358 :
1359 : return tmpl::as_pack<provider_aliases>([&](auto... aliases) {
1360 : return (... or check_dependents(check_dependents,
1361 : tmpl::type_from<decltype(aliases)>{}));
1362 : });
1363 : }
1364 : /// \endcond
1365 :
1366 : /*!
1367 : * \ingroup DataBoxGroup
1368 : * \brief Retrieve a mutable reference to the item with tag `Tag` from the
1369 : * DataBox.
1370 : *
1371 : * The tag retrieved cannot be used by any compute tags, cannot have
1372 : * subitems, and cannot itself be a subitem. These requirements
1373 : * prevent changes to the retrieved item from affecting any other tags
1374 : * in the DataBox, so it can safely be modified without causing
1375 : * internal inconsistencies.
1376 : */
1377 : template <typename Tag, typename TagList>
1378 1 : SPECTRE_ALWAYS_INLINE auto& get_mutable_reference(
1379 : const gsl::not_null<DataBox<TagList>*> box) {
1380 : return box->template get_mutable_reference<Tag>();
1381 : }
1382 :
1383 : /// @{
1384 : /*!
1385 : * \ingroup DataBoxGroup
1386 : * \brief Check whether the tag \p Consumer depends on the tag \p
1387 : * Provider in a given \p Box.
1388 : *
1389 : * This is equivalent to the question of whether changing \p Provider
1390 : * (through `db::mutate`, updating one of its dependencies, etc.)
1391 : * could change the value of `db::get<Consumer>`. Tags depend on
1392 : * themselves, and an item and its subitems all depend on one another.
1393 : */
1394 : template <typename Consumer, typename Provider, typename Box>
1395 1 : using tag_depends_on =
1396 : std::bool_constant<Box::template tag_depends_on<Consumer, Provider>()>;
1397 :
1398 : template <typename Consumer, typename Provider, typename Box>
1399 1 : constexpr bool tag_depends_on_v =
1400 : tag_depends_on<Consumer, Provider, Box>::value;
1401 : /// @}
1402 :
1403 : /*!
1404 : * \ingroup DataBoxGroup
1405 : * \brief List of Tags to add to the DataBox
1406 : */
1407 : template <typename... Tags>
1408 1 : using AddSimpleTags = tmpl::flatten<tmpl::list<Tags...>>;
1409 :
1410 : /*!
1411 : * \ingroup DataBoxGroup
1412 : * \brief List of Compute Item Tags to add to the DataBox
1413 : */
1414 : template <typename... Tags>
1415 1 : using AddComputeTags = tmpl::flatten<tmpl::list<Tags...>>;
1416 :
1417 : namespace detail {
1418 : template <class TagList>
1419 : struct compute_dbox_type {
1420 : using immutable_item_tags = detail::expand_subitems<
1421 : tmpl::filter<TagList, db::is_immutable_item_tag<tmpl::_1>>>;
1422 : using mutable_item_tags = detail::expand_subitems<
1423 : tmpl::filter<TagList, db::is_mutable_item_tag<tmpl::_1>>>;
1424 : using type = DataBox<tmpl::append<mutable_item_tags, immutable_item_tags>>;
1425 : };
1426 : } // namespace detail
1427 :
1428 : /*!
1429 : * \ingroup DataBoxGroup
1430 : * \brief Returns the type of the DataBox that would be constructed from the
1431 : * `TagList` of tags.
1432 : */
1433 : template <typename TagList>
1434 1 : using compute_databox_type = typename detail::compute_dbox_type<TagList>::type;
1435 :
1436 : /*!
1437 : * \ingroup DataBoxGroup
1438 : * \brief Create a new DataBox
1439 : *
1440 : * \details
1441 : * Creates a new DataBox holding types Tags::type filled with the arguments
1442 : * passed to the function. Compute and reference items must be added so that
1443 : * their dependencies are added before themselves. For example, say you have
1444 : * compute items `A` and `B` where `B` depends on `A`, then you must add them
1445 : * using `db::AddImmutableItemTags<A, B>`.
1446 : *
1447 : * \example
1448 : * \snippet Test_DataBox.cpp create_databox
1449 : *
1450 : * \see create_from
1451 : *
1452 : * \tparam AddMutableItemTags the tags of the mutable items that are being added
1453 : * \tparam AddImmutableItemTags list of \ref ComputeTag "compute item tags" and
1454 : * \ref ReferenceTag "refernce item tags" to add to the DataBox
1455 : * \param args the initial values for the mutable items to add to the DataBox
1456 : */
1457 : template <typename AddMutableItemTags,
1458 : typename AddImmutableItemTags = tmpl::list<>, typename... Args>
1459 1 : SPECTRE_ALWAYS_INLINE constexpr auto create(Args&&... args) {
1460 : static_assert(tt::is_a_v<tmpl::list, AddImmutableItemTags>,
1461 : "AddImmutableItemTags must be a tmpl::list");
1462 : static_assert(tt::is_a_v<tmpl::list, AddMutableItemTags>,
1463 : "AddMutableItemTags must be a tmpl::list");
1464 : static_assert(
1465 : tmpl::all<AddMutableItemTags, is_creation_tag<tmpl::_1>>::value and
1466 : tmpl::all<AddImmutableItemTags, is_creation_tag<tmpl::_1>>::value,
1467 : "Can only add tags derived from db::SimpleTag.");
1468 : static_assert(
1469 : tmpl::all<AddMutableItemTags, is_mutable_item_tag<tmpl::_1>>::value,
1470 : "Cannot add any ComputeTags or ReferenceTags in the AddMutableTags list, "
1471 : "must use the AddImmutableItemTags list.");
1472 : static_assert(
1473 : tmpl::all<AddImmutableItemTags, is_immutable_item_tag<tmpl::_1>>::value,
1474 : "Cannot add any SimpleTags in the AddImmutableItemTags list, must use "
1475 : "the "
1476 : "AddMutableItemTags list.");
1477 :
1478 : using mutable_item_tags = detail::expand_subitems<AddMutableItemTags>;
1479 : using immutable_item_tags = detail::expand_subitems<AddImmutableItemTags>;
1480 :
1481 : return db::DataBox<tmpl::append<mutable_item_tags, immutable_item_tags>>(
1482 : AddMutableItemTags{}, AddImmutableItemTags{},
1483 : std::forward<Args>(args)...);
1484 : }
1485 :
1486 : namespace detail {
1487 : template <typename DataBoxTags, typename... TagsToRetrieve>
1488 : constexpr bool check_tags_are_in_databox(
1489 : DataBoxTags /*meta*/, tmpl::list<TagsToRetrieve...> /*meta*/) {
1490 : static_assert(
1491 : (tag_is_retrievable_v<TagsToRetrieve, DataBox<DataBoxTags>> and ...),
1492 : "A desired tag is not in the DataBox. See the first template "
1493 : "argument of tag_is_retrievable_v for the missing tag, and the "
1494 : "second for the available tags.");
1495 : return true;
1496 : }
1497 :
1498 : template <typename... ArgumentTags, typename F, typename BoxTags,
1499 : typename... Args>
1500 : static constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
1501 : tmpl::list<ArgumentTags...> /*meta*/,
1502 : Args&&... args) {
1503 : if constexpr (is_apply_callable_v<
1504 : F,
1505 : tmpl::conditional_t<
1506 : std::is_same_v<ArgumentTags, ::Tags::DataBox>,
1507 : const DataBox<BoxTags>&,
1508 : const_item_type<ArgumentTags, BoxTags>>...,
1509 : Args...>) {
1510 : return F::apply(::db::get<ArgumentTags>(box)...,
1511 : std::forward<Args>(args)...);
1512 : } else if constexpr (::tt::is_callable_v<
1513 : std::remove_pointer_t<F>,
1514 : tmpl::conditional_t<
1515 : std::is_same_v<ArgumentTags, ::Tags::DataBox>,
1516 : const DataBox<BoxTags>&,
1517 : const_item_type<ArgumentTags, BoxTags>>...,
1518 : Args...>) {
1519 : return std::forward<F>(f)(::db::get<ArgumentTags>(box)...,
1520 : std::forward<Args>(args)...);
1521 : } else {
1522 : error_function_not_callable<
1523 : std::remove_pointer_t<F>,
1524 : tmpl::conditional_t<std::is_same_v<ArgumentTags, ::Tags::DataBox>,
1525 : const DataBox<BoxTags>&,
1526 : const_item_type<ArgumentTags, BoxTags>>...,
1527 : Args...>();
1528 : }
1529 : }
1530 : } // namespace detail
1531 :
1532 : /// @{
1533 : /*!
1534 : * \ingroup DataBoxGroup
1535 : * \brief Apply the invokable `f` with argument Tags `TagsList` from
1536 : * DataBox `box`
1537 : *
1538 : * \details
1539 : * `f` must either be invokable with the arguments of type
1540 : * `db::const_item_type<TagsList>..., Args...` where the first pack expansion
1541 : * is over the elements in the type list `ArgumentTags`, or have a static
1542 : * `apply` function that is callable with the same types.
1543 : * If the class that implements the static `apply` functions also provides an
1544 : * `argument_tags` typelist, then it is used and no explicit `ArgumentTags`
1545 : * template parameter should be specified.
1546 : *
1547 : * \usage
1548 : * Given a function `func` that takes arguments of types
1549 : * `T1`, `T2`, `A1` and `A2`. Let the Tags for the quantities of types `T1` and
1550 : * `T2` in the DataBox `box` be `Tag1` and `Tag2`, and objects `a1` of type
1551 : * `A1` and `a2` of type `A2`, then
1552 : * \code
1553 : * auto result = db::apply<tmpl::list<Tag1, Tag2>>(func, box, a1, a2);
1554 : * \endcode
1555 : * \return `decltype(func(db::get<Tag1>(box), db::get<Tag2>(box), a1, a2))`
1556 : *
1557 : * \semantics
1558 : * For tags `Tags...` in a DataBox `box`, and a function `func` that takes
1559 : * `sizeof...(Tags)` arguments of types `db::const_item_type<Tags>...`, and
1560 : * `sizeof...(Args)` arguments of types `Args...`,
1561 : * \code
1562 : * result = func(box, db::get<Tags>(box)..., args...);
1563 : * \endcode
1564 : *
1565 : * \example
1566 : * \snippet Test_DataBox.cpp apply_example
1567 : * Using a struct with an `apply` method:
1568 : * \snippet Test_DataBox.cpp apply_struct_example
1569 : * If the class `F` has no state, you can also use the stateless overload of
1570 : * `apply`: \snippet Test_DataBox.cpp apply_stateless_struct_example
1571 : *
1572 : * \see DataBox
1573 : * \tparam ArgumentTags typelist of Tags in the order that they are to be passed
1574 : * to `f`
1575 : * \tparam F The invokable to apply
1576 : */
1577 : template <typename ArgumentTags, typename F, typename BoxTags, typename... Args>
1578 1 : SPECTRE_ALWAYS_INLINE constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
1579 : Args&&... args) {
1580 : detail::check_tags_are_in_databox(
1581 : BoxTags{}, tmpl::remove<ArgumentTags, ::Tags::DataBox>{});
1582 : return detail::apply(std::forward<F>(f), box, ArgumentTags{},
1583 : std::forward<Args>(args)...);
1584 : }
1585 :
1586 : template <typename F, typename BoxTags, typename... Args>
1587 1 : SPECTRE_ALWAYS_INLINE constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
1588 : Args&&... args) {
1589 : return apply<typename std::decay_t<F>::argument_tags>(
1590 : std::forward<F>(f), box, std::forward<Args>(args)...);
1591 : }
1592 :
1593 : template <typename F, typename BoxTags, typename... Args>
1594 1 : SPECTRE_ALWAYS_INLINE constexpr auto apply(const DataBox<BoxTags>& box,
1595 : Args&&... args) {
1596 : return apply(F{}, box, std::forward<Args>(args)...);
1597 : }
1598 : /// @}
1599 :
1600 : namespace detail {
1601 : template <typename Tag, typename BoxTags>
1602 : using tag_return_type =
1603 : tmpl::conditional_t<std::is_same_v<Tag, ::Tags::DataBox>,
1604 : db::DataBox<BoxTags>, typename Tag::type>;
1605 :
1606 : template <typename... ReturnTags, typename... ArgumentTags, typename F,
1607 : typename BoxTags, typename... Args>
1608 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
1609 : F&& f, const gsl::not_null<db::DataBox<BoxTags>*> box,
1610 : tmpl::list<ReturnTags...> /*meta*/, tmpl::list<ArgumentTags...> /*meta*/,
1611 : Args&&... args) {
1612 : if constexpr (sizeof...(ReturnTags) == 0) {
1613 : return apply<tmpl::list<ArgumentTags...>>(std::forward<F>(f), *box,
1614 : std::forward<Args>(args)...);
1615 : } else {
1616 : detail::check_tags_are_in_databox(BoxTags{}, tmpl::list<ReturnTags...>{});
1617 : detail::check_tags_are_in_databox(BoxTags{}, tmpl::list<ArgumentTags...>{});
1618 : static_assert(not(... or std::is_same_v<ArgumentTags, Tags::DataBox>),
1619 : "Cannot pass Tags::DataBox to mutate_apply when mutating "
1620 : "since the db::get won't work inside mutate_apply.");
1621 : if constexpr (is_apply_callable_v<
1622 : F,
1623 : const gsl::not_null<
1624 : tag_return_type<ReturnTags, BoxTags>*>...,
1625 : const_item_type<ArgumentTags, BoxTags>..., Args...>) {
1626 : return ::db::mutate<ReturnTags...>(
1627 : [](const gsl::not_null<
1628 : tag_return_type<ReturnTags, BoxTags>*>... mutated_items,
1629 : const_item_type<ArgumentTags, BoxTags>... args_items,
1630 : decltype(std::forward<Args>(args))... l_args) {
1631 : return std::decay_t<F>::apply(mutated_items..., args_items...,
1632 : std::forward<Args>(l_args)...);
1633 : },
1634 : box, db::get<ArgumentTags>(*box)..., std::forward<Args>(args)...);
1635 : } else if constexpr (
1636 : ::tt::is_callable_v<
1637 : F, const gsl::not_null<tag_return_type<ReturnTags, BoxTags>*>...,
1638 : const_item_type<ArgumentTags, BoxTags>..., Args...>) {
1639 : return ::db::mutate<ReturnTags...>(f, box, db::get<ArgumentTags>(*box)...,
1640 : std::forward<Args>(args)...);
1641 : } else {
1642 : error_function_not_callable<
1643 : F, gsl::not_null<tag_return_type<ReturnTags, BoxTags>*>...,
1644 : const_item_type<ArgumentTags, BoxTags>..., Args...>();
1645 : }
1646 : }
1647 : }
1648 : } // namespace detail
1649 :
1650 : /// @{
1651 : /*!
1652 : * \ingroup DataBoxGroup
1653 : * \brief Apply the invokable `f` mutating items `MutateTags` and taking as
1654 : * additional arguments `ArgumentTags` and `args`.
1655 : *
1656 : * \details
1657 : * `f` must either be invokable with the arguments of type
1658 : * `gsl::not_null<db::item_type<MutateTags>*>...,
1659 : * db::const_item_type<ArgumentTags>..., Args...`
1660 : * where the first two pack expansions are over the elements in the typelists
1661 : * `MutateTags` and `ArgumentTags`, or have a static `apply` function that is
1662 : * callable with the same types. If the type of `f` specifies `return_tags` and
1663 : * `argument_tags` typelists, these are used for the `MutateTags` and
1664 : * `ArgumentTags`, respectively.
1665 : *
1666 : * Any return values of the invokable `f` are forwarded as returns to the
1667 : * `mutate_apply` call.
1668 : *
1669 : * \note If `MutateTags` is empty this will forward to `db::apply`, so
1670 : * `::Tags::DataBox` will be available. Otherwise it will call
1671 : * `db::mutate`. See those functions for more details on retrieving
1672 : * the entire box.
1673 : *
1674 : * \example
1675 : * An example of using `mutate_apply` with a lambda:
1676 : * \snippet Test_DataBox.cpp mutate_apply_lambda_example
1677 : *
1678 : * An example of a class with a static `apply` function
1679 : * \snippet Test_DataBox.cpp mutate_apply_struct_definition_example
1680 : * and how to use `mutate_apply` with the above class
1681 : * \snippet Test_DataBox.cpp mutate_apply_struct_example_stateful
1682 : * Note that the class exposes `return_tags` and `argument_tags` typelists, so
1683 : * we don't specify the template parameters explicitly.
1684 : * If the class `F` has no state, like in this example,
1685 : * \snippet Test_DataBox.cpp mutate_apply_struct_definition_example
1686 : * you can also use the stateless overload of `mutate_apply`:
1687 : * \snippet Test_DataBox.cpp mutate_apply_struct_example_stateless
1688 : *
1689 : * \tparam MutateTags typelist of Tags to mutate
1690 : * \tparam ArgumentTags typelist of additional items to retrieve from the
1691 : * DataBox
1692 : * \tparam F The invokable to apply
1693 : */
1694 : template <typename MutateTags, typename ArgumentTags, typename F,
1695 : typename BoxTags, typename... Args>
1696 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
1697 : F&& f, const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) {
1698 : return detail::mutate_apply(std::forward<F>(f), box, MutateTags{},
1699 : ArgumentTags{}, std::forward<Args>(args)...);
1700 : }
1701 :
1702 : template <typename F, 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 mutate_apply<typename std::decay_t<F>::return_tags,
1706 : typename std::decay_t<F>::argument_tags>(
1707 : std::forward<F>(f), box, std::forward<Args>(args)...);
1708 : }
1709 :
1710 : template <typename F, typename BoxTags, typename... Args>
1711 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(
1712 : const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) {
1713 : return mutate_apply(F{}, box, std::forward<Args>(args)...);
1714 : }
1715 : /// @}
1716 : } // namespace db
1717 :
1718 : template <typename TagsList>
1719 0 : std::ostream& operator<<(std::ostream& os, const db::DataBox<TagsList>& box) {
1720 : os << box.print_types() << "\n";
1721 : os << box.print_items() << "\n";
1722 : return os;
1723 : }
|