SpECTRE Documentation Coverage Report
Current view: top level - DataStructures/DataBox - DataBox.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 50.6 % 85 43
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            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              : }
        

Generated by: LCOV version 2.0-1