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

Generated by: LCOV version 1.14