SpECTRE Documentation Coverage Report
Current view: top level - DataStructures/DataBox - DataBox.hpp Hit Total Coverage
Commit: 2db722c93a8e9b106e406b439b79c8e05c2057fb Lines: 21 47 44.7 %
Date: 2021-03-03 22:01:00
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 <functional>
      11             : #include <initializer_list>
      12             : #include <ostream>
      13             : #include <pup.h>
      14             : #include <string>
      15             : #include <tuple>
      16             : #include <utility>
      17             : 
      18             : #include "DataStructures/DataBox/DataBoxTag.hpp"
      19             : #include "DataStructures/DataBox/Item.hpp"
      20             : #include "DataStructures/DataBox/SubitemTag.hpp"
      21             : #include "DataStructures/DataBox/Subitems.hpp"
      22             : #include "DataStructures/DataBox/TagName.hpp"
      23             : #include "DataStructures/DataBox/TagTraits.hpp"
      24             : #include "Utilities/ErrorHandling/Error.hpp"
      25             : #include "Utilities/ErrorHandling/StaticAssert.hpp"
      26             : #include "Utilities/ForceInline.hpp"
      27             : #include "Utilities/Gsl.hpp"
      28             : #include "Utilities/NoSuchType.hpp"
      29             : #include "Utilities/Overloader.hpp"
      30             : #include "Utilities/Requires.hpp"
      31             : #include "Utilities/TMPL.hpp"
      32             : #include "Utilities/TypeTraits.hpp"
      33             : #include "Utilities/TypeTraits/CreateIsCallable.hpp"
      34             : #include "Utilities/TypeTraits/IsA.hpp"
      35             : #include "Utilities/TypeTraits/IsCallable.hpp"
      36             : 
      37             : // IWYU pragma: no_forward_declare brigand::get_destination
      38             : // IWYU pragma: no_forward_declare brigand::get_source
      39             : 
      40             : /*!
      41             :  * \ingroup DataBoxGroup
      42             :  * \brief Namespace for DataBox related things
      43             :  */
      44           1 : namespace db {
      45             : 
      46             : // Forward declarations
      47             : /// \cond
      48             : template <typename TagsList>
      49             : class DataBox;
      50             : /// \endcond
      51             : 
      52             : // @{
      53             : /// \ingroup DataBoxGroup
      54             : /// Equal to `true` if `Tag` can be retrieved from a `DataBox` of type
      55             : /// `DataBoxType`.
      56             : template <typename Tag, typename DataBoxType>
      57           1 : using tag_is_retrievable = tmpl::any<typename DataBoxType::tags_list,
      58             :                                      std::is_base_of<tmpl::pin<Tag>, tmpl::_1>>;
      59             : 
      60             : template <typename Tag, typename DataBoxType>
      61           0 : constexpr bool tag_is_retrievable_v =
      62             :     tag_is_retrievable<Tag, DataBoxType>::value;
      63             : // @}
      64             : 
      65             : namespace detail {
      66             : template <typename Tag>
      67             : using has_subitems =
      68             :     tmpl::not_<std::is_same<typename Subitems<Tag>::type, tmpl::list<>>>;
      69             : 
      70             : template <typename Tag>
      71             : constexpr bool has_subitems_v = has_subitems<Tag>::value;
      72             : 
      73             : template <typename Tag, typename ParentTag>
      74             : struct make_subitem_tag {
      75             :   using type = ::Tags::Subitem<Tag, ParentTag>;
      76             : };
      77             : 
      78             : template <typename Tag, typename = std::nullptr_t>
      79             : struct append_subitem_tags {
      80             :   using type = tmpl::push_front<typename db::Subitems<Tag>::type, Tag>;
      81             : };
      82             : 
      83             : template <typename ParentTag>
      84             : struct append_subitem_tags<ParentTag,
      85             :                            Requires<has_subitems_v<ParentTag> and
      86             :                                     is_immutable_item_tag_v<ParentTag>>> {
      87             :   using type = tmpl::push_front<
      88             :       tmpl::transform<typename Subitems<ParentTag>::type,
      89             :                       make_subitem_tag<tmpl::_1, tmpl::pin<ParentTag>>>,
      90             :       ParentTag>;
      91             : };
      92             : 
      93             : template <typename Tag>
      94             : struct append_subitem_tags<Tag, Requires<not has_subitems_v<Tag>>> {
      95             :   using type = tmpl::list<Tag>;
      96             : };
      97             : 
      98             : template <typename TagsList>
      99             : using expand_subitems =
     100             :     tmpl::flatten<tmpl::transform<TagsList, append_subitem_tags<tmpl::_1>>>;
     101             : 
     102             : template <typename ComputeTag, typename ArgumentTag,
     103             :           typename FoundArgumentTagInBox>
     104             : struct report_missing_compute_item_argument {
     105             :   static_assert(std::is_same_v<ComputeTag, void>,
     106             :                 "A compute item's argument could not be found in the DataBox "
     107             :                 "or was found multiple times.  See the first template argument "
     108             :                 "of report_missing_compute_item_argument for the compute item "
     109             :                 "and the second for the missing (or duplicated) argument.");
     110             : };
     111             : 
     112             : template <typename ComputeTag, typename ArgumentTag>
     113             : struct report_missing_compute_item_argument<ComputeTag, ArgumentTag,
     114             :                                             std::true_type> {
     115             :   using type = void;
     116             : };
     117             : 
     118             : template <typename TagsList, typename ComputeTag>
     119             : struct create_compute_tag_argument_edges {
     120             : #ifdef SPECTRE_DEBUG
     121             :   using argument_check_assertion = tmpl::transform<
     122             :       typename ComputeTag::argument_tags,
     123             :       report_missing_compute_item_argument<
     124             :           tmpl::pin<ComputeTag>, tmpl::_1,
     125             :           has_unique_matching_tag<tmpl::pin<TagsList>, tmpl::_1>>>;
     126             : #endif  // SPECTRE_DEBUG
     127             :   // These edges record that a compute item's value depends on the
     128             :   // values of it's arguments.
     129             :   using type = tmpl::transform<
     130             :       typename ComputeTag::argument_tags,
     131             :       tmpl::bind<tmpl::edge,
     132             :                  tmpl::bind<first_matching_tag, tmpl::pin<TagsList>, tmpl::_1>,
     133             :                  tmpl::pin<ComputeTag>>>;
     134             : };
     135             : 
     136             : template <typename Tag>
     137             : struct create_subitem_reverse_edge {
     138             :   // This edge records that the value of a subitem of a compute
     139             :   // item depend on the value of the compute item itself.
     140             :   using type = tmpl::edge<typename Tag::parent_tag, Tag>;
     141             : };
     142             : 
     143             : template <typename TagsList, typename ComputeTagsList>
     144             : struct create_dependency_graph {
     145             :   using compute_tag_argument_edges =
     146             :       tmpl::join<tmpl::transform<ComputeTagsList,
     147             :                                  detail::create_compute_tag_argument_edges<
     148             :                                      tmpl::pin<TagsList>, tmpl::_1>>>;
     149             :   using subitems_that_need_reverse_edges = tmpl::transform<
     150             :       tmpl::filter<compute_tag_argument_edges,
     151             :                    db::is_reference_tag<tmpl::get_source<tmpl::_1>>>,
     152             :       tmpl::get_source<tmpl::_1>>;
     153             :   using subitem_reverse_edges =
     154             :       tmpl::transform<subitems_that_need_reverse_edges,
     155             :                       create_subitem_reverse_edge<tmpl::_1>>;
     156             :   using type = tmpl::append<compute_tag_argument_edges, subitem_reverse_edges>;
     157             : };
     158             : }  // namespace detail
     159             : 
     160             : /*!
     161             :  * \ingroup DataBoxGroup
     162             :  * \brief A DataBox stores objects that can be retrieved by using Tags
     163             :  * \warning
     164             :  * The order of the tags in DataBoxes returned by db::create and
     165             :  * db::create_from depends on implementation-defined behavior, and
     166             :  * therefore should not be specified in source files. If explicitly
     167             :  * naming a DataBox type is necessary they should be generated using
     168             :  * db::compute_databox_type.
     169             :  *
     170             :  * \see db::create db::create_from
     171             :  *
     172             :  * @tparam Tags list of DataBoxTag's
     173             :  */
     174             : template <typename... Tags>
     175           1 : class DataBox<tmpl::list<Tags...>> : private detail::Item<Tags>... {
     176             : 
     177             :  public:
     178             :   /*!
     179             :    * \brief A typelist (`tmpl::list`) of Tags that the DataBox holds
     180             :    */
     181           1 :   using tags_list = tmpl::list<Tags...>;
     182             : 
     183             :   /// A list of all the immutable item tags, including their subitems
     184           1 :   using immutable_item_tags =
     185             :       tmpl::filter<tags_list, db::is_immutable_item_tag<tmpl::_1>>;
     186             : 
     187             :   /// A list of all the immutable item tags used to create the DataBox
     188             :   ///
     189             :   /// \note This does not include subitems of immutable items
     190           1 :   using immutable_item_creation_tags =
     191             :       tmpl::remove_if<immutable_item_tags, tt::is_a<::Tags::Subitem, tmpl::_1>>;
     192             : 
     193             :   /// A list of all the mutable item tags, including their subitems
     194           1 :   using mutable_item_tags =
     195             :       tmpl::filter<tags_list, db::is_mutable_item_tag<tmpl::_1>>;
     196             : 
     197             :   /// A list of the expanded simple subitems, not including the main Subitem
     198             :   /// tags themselves.
     199             :   ///
     200             :   /// Specifically, if there is a `Variables<Tag0, Tag1>`, then this list would
     201             :   /// contain `Tag0, Tag1`.
     202           1 :   using mutable_subitem_tags = tmpl::flatten<
     203             :       tmpl::transform<mutable_item_tags, db::Subitems<tmpl::_1>>>;
     204             : 
     205             :   /// A list of all the compute item tags
     206           1 :   using compute_item_tags =
     207             :       tmpl::filter<immutable_item_tags, db::is_compute_tag<tmpl::_1>>;
     208             : 
     209             :   /// \cond HIDDEN_SYMBOLS
     210             :   /*!
     211             :    * \note the default constructor is only used for serialization
     212             :    */
     213             :   DataBox() = default;
     214             :   DataBox(DataBox&& rhs) noexcept(
     215             :       tmpl2::flat_all_v<
     216             :           std::is_nothrow_move_constructible_v<detail::Item<Tags>>...>) =
     217             :       default;
     218             :   DataBox& operator=(DataBox&& rhs) noexcept(
     219             :       tmpl2::flat_all_v<
     220             :           std::is_nothrow_move_assignable_v<detail::Item<Tags>>...>) {
     221             :     if (&rhs != this) {
     222             : #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
     223             : #pragma GCC diagnostic push
     224             : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
     225             : #endif  // defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
     226             :       ::expand_pack(
     227             :           (get_item<Tags>() = std::move(rhs.template get_item<Tags>()))...);
     228             : #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
     229             : #pragma GCC diagnostic pop
     230             : #endif  // defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
     231             :     }
     232             :     return *this;
     233             :   }
     234             :   DataBox(const DataBox& rhs) = delete;
     235             :   DataBox& operator=(const DataBox& rhs) = delete;
     236             :   ~DataBox() = default;
     237             : 
     238             :   /// \endcond
     239             : 
     240             :   /// Retrieve the tag `Tag`, should be called by the free function db::get
     241             :   template <typename Tag>
     242           1 :   const auto& get() const noexcept;
     243             : 
     244             :   // clang-tidy: no non-const references
     245           0 :   void pup(PUP::er& p) noexcept {  // NOLINT
     246             :     using non_subitems_tags =
     247             :         tmpl::list_difference<mutable_item_tags,
     248             :                               mutable_subitem_tags>;
     249             : 
     250             :     // We do not send subitems for both simple items and compute items since
     251             :     // they can be reconstructed very cheaply.
     252             :     pup_impl(p, non_subitems_tags{}, immutable_item_creation_tags{});
     253             :   }
     254             : 
     255             :   template <typename Box, typename KeepTagsList, typename... AddMutableItemTags,
     256             :             typename... AddImmutableItemTags, typename... Args>
     257           0 :   constexpr DataBox(Box&& old_box, KeepTagsList /*meta*/,
     258             :                     tmpl::list<AddMutableItemTags...> /*meta*/,
     259             :                     tmpl::list<AddImmutableItemTags...> /*meta*/,
     260             :                     Args&&... args) noexcept;
     261             : 
     262             :   template <typename... AddMutableItemTags, typename AddImmutableItemTagsList,
     263             :             typename... Args>
     264           0 :   constexpr DataBox(tmpl::list<AddMutableItemTags...> /*meta*/,
     265             :                     AddImmutableItemTagsList /*meta*/, Args&&... args) noexcept;
     266             : 
     267             :  private:
     268             :   template <typename... MutateTags, typename TagList, typename Invokable,
     269             :             typename... Args>
     270             :   // clang-tidy: redundant declaration
     271           0 :   friend void mutate(gsl::not_null<DataBox<TagList>*> box,             // NOLINT
     272             :                      Invokable&& invokable, Args&&... args) noexcept;  // NOLINT
     273             : 
     274             :   // evaluates the compute item corresponding to ComputeTag passing along
     275             :   // items fetched via ArgumentTags
     276             :   template <typename ComputeTag, typename... ArgumentTags>
     277           0 :   void evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const
     278             :       noexcept;
     279             : 
     280             :   // get a constant reference to the item corresponding to Tag
     281             :   template <typename Tag>
     282           0 :   const auto& get_item() const noexcept {
     283             :     return static_cast<const detail::Item<Tag>&>(*this);
     284             :   }
     285             : 
     286             :   // get a mutable reference to the item corresponding to Tag
     287             :   template <typename Tag>
     288           0 :   auto& get_item() noexcept {
     289             :     return static_cast<detail::Item<Tag>&>(*this);
     290             :   }
     291             : 
     292             :   template <typename ParentTag>
     293           0 :   constexpr void add_mutable_subitems_to_box(tmpl::list<> /*meta*/) noexcept {}
     294             : 
     295             :   // add mutable items for the subitems of the item corresponding to ParentTag
     296             :   template <typename ParentTag, typename... SubitemTags>
     297           0 :   constexpr void add_mutable_subitems_to_box(
     298             :       tmpl::list<SubitemTags...> /*meta*/) noexcept;
     299             : 
     300             :   // sets the mutable item corresponding to Tag with the ArgsIndex object in
     301             :   // items
     302             :   template <size_t ArgsIndex, typename Tag, typename... Ts>
     303           0 :   constexpr char add_mutable_item_to_box(std::tuple<Ts...>& items) noexcept;
     304             : 
     305             :   // set the mutable items corresponding to AddMutableItemTags to the
     306             :   // appropriate objects from items, and checks the dependencies of the
     307             :   // immutable items corresponding to AddImmutableItemTags
     308             :   template <typename... Ts, typename... AddMutableItemTags,
     309             :             typename... AddImmutableItemTags, size_t... Is>
     310           0 :   void add_items_to_box(std::tuple<Ts...>& items,
     311             :                         tmpl::list<AddMutableItemTags...> /*meta*/,
     312             :                         std::index_sequence<Is...> /*meta*/,
     313             :                         tmpl::list<AddImmutableItemTags...> /*meta*/) noexcept;
     314             : 
     315             :   // Merging DataBox's using create_from requires that all instantiations of
     316             :   // DataBox be friends with each other.
     317             :   template <typename OtherTags>
     318           0 :   friend class DataBox;
     319             : 
     320             :   template <typename Box, typename... TagsToCopy>
     321           0 :   constexpr void merge_old_box(Box&& old_box,
     322             :                                tmpl::list<TagsToCopy...> /*meta*/) noexcept;
     323             : 
     324             :   // clang-tidy: no non-const references
     325             :   template <typename... NonSubitemsTags, typename... ComputeTags>
     326           0 :   void pup_impl(PUP::er& p, tmpl::list<NonSubitemsTags...> /*meta*/,  // NOLINT
     327             :                 tmpl::list<ComputeTags...> /*meta*/) noexcept;
     328             : 
     329             :   // Mutating items in the DataBox
     330             :   template <typename ParentTag>
     331           0 :   constexpr void mutate_mutable_subitems(tmpl::list<> /*meta*/) noexcept {}
     332             : 
     333             :   template <typename ParentTag, typename... Subtags>
     334           0 :   constexpr void mutate_mutable_subitems(
     335             :       tmpl::list<Subtags...> /*meta*/) noexcept;
     336             : 
     337             :   template <typename ImmutableItemTag>
     338           0 :   constexpr void reset_compute_item() noexcept;
     339             : 
     340             :   template <typename... TagsOfImmutableItemsToReset>
     341           0 :   SPECTRE_ALWAYS_INLINE constexpr void reset_compute_items_after_mutate(
     342             :       tmpl::list<TagsOfImmutableItemsToReset...> /*meta*/) noexcept;
     343             : 
     344           0 :   SPECTRE_ALWAYS_INLINE constexpr void reset_compute_items_after_mutate(
     345             :       tmpl::list<> /*meta*/) noexcept {}
     346             :   // End mutating items in the DataBox
     347             : 
     348           0 :   using edge_list =
     349             :       typename detail::create_dependency_graph<tags_list,
     350             :                                                compute_item_tags>::type;
     351             : 
     352           0 :   bool mutate_locked_box_{false};
     353             : };
     354             : 
     355             : namespace detail {
     356             : // This function exists so that the user can look at the template
     357             : // arguments to find out what triggered the static_assert.
     358             : template <typename ImmutableItemTag, typename ArgumentTag, typename TagsList>
     359             : constexpr char check_immutable_item_tag_dependency() noexcept {
     360             :   using immutable_item_tag_index = tmpl::index_of<TagsList, ImmutableItemTag>;
     361             :   static_assert(
     362             :       tmpl::less<tmpl::index_if<TagsList,
     363             :                                 std::is_same<tmpl::pin<ArgumentTag>, tmpl::_1>,
     364             :                                 immutable_item_tag_index>,
     365             :                  immutable_item_tag_index>::value,
     366             :       "The argument_tags of an immutable item tag must be added before itself. "
     367             :       "This is done to ensure no cyclic dependencies arise.  See the first and "
     368             :       "second template arguments of check_immutable_item_tag_dependency for "
     369             :       "the immutable item tag and its missing (or incorrectly added) argument "
     370             :       "tag.  The third template argument is the TagsList of the DataBox (in "
     371             :       "which the argument tag should precede the immutable item tag)");
     372             :   return '0';
     373             : }
     374             : 
     375             : template <typename ImmutableItemTag, typename TagsList,
     376             :           typename... ArgumentsTags>
     377             : SPECTRE_ALWAYS_INLINE constexpr void
     378             : check_immutable_item_tag_dependencies_impl(
     379             :     tmpl::list<ArgumentsTags...> /*meta*/) noexcept {
     380             :   DEBUG_STATIC_ASSERT(
     381             :       tmpl2::flat_all_v<is_tag_v<ArgumentsTags>...>,
     382             :       "Cannot have non-DataBoxTag arguments to a ComputeItem or ReferenceItem. "
     383             :       "Please make sure all the specified argument_tags derive from "
     384             :       "db::SimpleTag or db::BaseTag.");
     385             :   DEBUG_STATIC_ASSERT(
     386             :       not tmpl2::flat_any_v<std::is_same_v<ArgumentsTags, ImmutableItemTag>...>,
     387             :       "A ComputeItem cannot take its own Tag as an argument.");
     388             :   expand_pack(detail::check_immutable_item_tag_dependency<
     389             :               ImmutableItemTag, ArgumentsTags, TagsList>()...);
     390             : }
     391             : 
     392             : template <typename ImmutableItemTag, typename TagsList>
     393             : SPECTRE_ALWAYS_INLINE constexpr void
     394             : check_immutable_item_tag_dependencies() noexcept {
     395             :   check_immutable_item_tag_dependencies_impl<ImmutableItemTag, TagsList>(
     396             :       tmpl::transform<typename ImmutableItemTag::argument_tags,
     397             :                       tmpl::bind<detail::first_matching_tag,
     398             :                                  tmpl::pin<TagsList>, tmpl::_1>>{});
     399             : }
     400             : }  // namespace detail
     401             : 
     402             : template <typename... Tags>
     403             : template <typename ParentTag, typename... SubitemTags>
     404             : SPECTRE_ALWAYS_INLINE constexpr void
     405             : db::DataBox<tmpl::list<Tags...>>::add_mutable_subitems_to_box(
     406             :     tmpl::list<SubitemTags...> /*meta*/) noexcept {
     407             :   const auto add_mutable_subitem_to_box = [this](auto tag_v) {
     408             :     (void)this;  // Compiler bug warns this is unused
     409             :     using subitem_tag = decltype(tag_v);
     410             :     get_item<subitem_tag>() =
     411             :         detail::Item<subitem_tag>(typename subitem_tag::type{});
     412             :     Subitems<ParentTag>::template create_item<subitem_tag>(
     413             :         make_not_null(&get_item<ParentTag>().mutate()),
     414             :         make_not_null(&get_item<subitem_tag>().mutate()));
     415             :   };
     416             : 
     417             :   EXPAND_PACK_LEFT_TO_RIGHT(add_mutable_subitem_to_box(SubitemTags{}));
     418             : }
     419             : 
     420             : template <typename... Tags>
     421             : template <size_t ArgsIndex, typename MutableItemTag, typename... Ts>
     422             : SPECTRE_ALWAYS_INLINE constexpr char
     423             : db::DataBox<tmpl::list<Tags...>>::add_mutable_item_to_box(
     424             :     std::tuple<Ts...>& items) noexcept {
     425             :   using ArgType = std::tuple_element_t<ArgsIndex, std::tuple<Ts...>>;
     426             :   get_item<MutableItemTag>() = detail::Item<MutableItemTag>(
     427             :       std::forward<ArgType>(std::get<ArgsIndex>(items)));
     428             :   add_mutable_subitems_to_box<MutableItemTag>(
     429             :       typename Subitems<MutableItemTag>::type{});
     430             :   return '0';  // must return in constexpr function
     431             : }
     432             : 
     433             : // Add items or compute items to the TaggedDeferredTuple `data`. If
     434             : // `AddItemTags...` is an empty pack then only compute items are added, while if
     435             : // `AddComputeTags...` is an empty pack only items are added. Items are
     436             : // always added before compute items.
     437             : template <typename... Tags>
     438             : template <typename... Ts, typename... AddMutableItemTags,
     439             :           typename... AddImmutableItemTags, size_t... Is>
     440             : SPECTRE_ALWAYS_INLINE void DataBox<tmpl::list<Tags...>>::add_items_to_box(
     441             :     std::tuple<Ts...>& items, tmpl::list<AddMutableItemTags...> /*meta*/,
     442             :     std::index_sequence<Is...> /*meta*/,
     443             :     tmpl::list<AddImmutableItemTags...> /*meta*/) noexcept {
     444             :   expand_pack(add_mutable_item_to_box<Is, AddMutableItemTags>(items)...);
     445             :   EXPAND_PACK_LEFT_TO_RIGHT(
     446             :       detail::check_immutable_item_tag_dependencies<AddImmutableItemTags,
     447             :                                                     tags_list>());
     448             : }
     449             : 
     450             : namespace detail {
     451             : // This function (and its unused template argument) exist so that
     452             : // users can see what tag has the wrong type when the static_assert
     453             : // fails.
     454             : template <typename Tag, typename TagType, typename SuppliedType>
     455             : constexpr int check_initialization_argument_type() noexcept {
     456             :   static_assert(std::is_same_v<TagType, SuppliedType>,
     457             :                 "The type of each Tag must be the same as the type being "
     458             :                 "passed into the function creating the new DataBox.  See the "
     459             :                 "template parameters of check_initialization_argument_type for "
     460             :                 "the tag, expected type, and supplied type.");
     461             :   return 0;
     462             : }
     463             : }  // namespace detail
     464             : 
     465             : /// \cond
     466             : template <typename... Tags>
     467             : template <typename... AddMutableItemTags, typename AddImmutableItemTagsList,
     468             :           typename... Args>
     469             : constexpr DataBox<tmpl::list<Tags...>>::DataBox(
     470             :     tmpl::list<AddMutableItemTags...> /*meta*/,
     471             :     AddImmutableItemTagsList /*meta*/, Args&&... args) noexcept {
     472             :   DEBUG_STATIC_ASSERT(sizeof...(AddMutableItemTags) == sizeof...(Args),
     473             :                       "Must pass in as many arguments as AddTags");
     474             : #ifdef SPECTRE_DEBUG
     475             :   // The check_argument_type call is very expensive compared to the majority of
     476             :   // DataBox
     477             :   expand_pack(detail::check_initialization_argument_type<
     478             :               AddMutableItemTags, typename AddMutableItemTags::type,
     479             :               std::decay_t<Args>>()...);
     480             : #endif  // SPECTRE_DEBUG
     481             : 
     482             :   std::tuple<Args&&...> args_tuple(std::forward<Args>(args)...);
     483             :   add_items_to_box(args_tuple, tmpl::list<AddMutableItemTags...>{},
     484             :                    std::make_index_sequence<sizeof...(AddMutableItemTags)>{},
     485             :                    AddImmutableItemTagsList{});
     486             : }
     487             : 
     488             : ////////////////////////////////////////////////////////////////
     489             : // Construct DataBox from an existing one
     490             : template <typename... Tags>
     491             : template <typename Box, typename... TagsToCopy>
     492             : constexpr void DataBox<tmpl::list<Tags...>>::merge_old_box(
     493             :     Box&& old_box, tmpl::list<TagsToCopy...> /*meta*/) noexcept {
     494             :   EXPAND_PACK_LEFT_TO_RIGHT(get_item<TagsToCopy>() = std::move(
     495             :                                 old_box.template get_item<TagsToCopy>()));
     496             : }
     497             : 
     498             : template <typename... Tags>
     499             : template <typename Box, typename KeepTagsList, typename... AddMutableItemTags,
     500             :           typename... AddImmutableItemTags, typename... Args>
     501             : constexpr DataBox<tmpl::list<Tags...>>::DataBox(
     502             :     Box&& old_box, KeepTagsList /*meta*/,
     503             :     tmpl::list<AddMutableItemTags...> /*meta*/,
     504             :     tmpl::list<AddImmutableItemTags...> /*meta*/, Args&&... args) noexcept {
     505             : #ifdef SPECTRE_DEBUG
     506             :   expand_pack(detail::check_initialization_argument_type<
     507             :               AddMutableItemTags, typename AddMutableItemTags::type,
     508             :               std::decay_t<Args>>()...);
     509             : #endif  // SPECTRE_DEBUG
     510             : 
     511             :   merge_old_box(std::forward<Box>(old_box), KeepTagsList{});
     512             : 
     513             :   std::tuple<Args&&...> args_tuple(std::forward<Args>(args)...);
     514             : 
     515             :   add_items_to_box(args_tuple, tmpl::list<AddMutableItemTags...>{},
     516             :                    std::make_index_sequence<sizeof...(AddMutableItemTags)>{},
     517             :                    tmpl::list<AddImmutableItemTags...>{});
     518             : }
     519             : /// \endcond
     520             : 
     521             : ////////////////////////////////////////////////////////////////
     522             : // Serialization of DataBox
     523             : 
     524             : template <typename... Tags>
     525             : template <typename... NonSubitemsTags, typename... ComputeTags>
     526             : void DataBox<tmpl::list<Tags...>>::pup_impl(
     527             :     PUP::er& p, tmpl::list<NonSubitemsTags...> /*meta*/,
     528             :     tmpl::list<ComputeTags...> /*meta*/) noexcept {
     529             :   const auto pup_simple_item = [&p, this ](auto current_tag) noexcept {
     530             :     (void)this;  // Compiler bug warning this capture is not used
     531             :     using tag = decltype(current_tag);
     532             :     if (p.isUnpacking()) {
     533             :       typename tag::type t{};
     534             :       p | t;
     535             :       get_item<tag>() = detail::Item<tag>(std::move(t));
     536             :       add_mutable_subitems_to_box<tag>(typename Subitems<tag>::type{});
     537             :     } else {
     538             :       p | get_item<tag>().mutate();
     539             :     }
     540             :   };
     541             :   (void)pup_simple_item;  // Silence GCC warning about unused variable
     542             :   EXPAND_PACK_LEFT_TO_RIGHT(pup_simple_item(NonSubitemsTags{}));
     543             : 
     544             :   EXPAND_PACK_LEFT_TO_RIGHT(get_item<ComputeTags>().pup(p));
     545             : }
     546             : 
     547             : ////////////////////////////////////////////////////////////////
     548             : // Mutating items in the DataBox
     549             : // Classes and functions necessary for db::mutate to work
     550             : template <typename... Tags>
     551             : template <typename ImmutableItemTag>
     552             : SPECTRE_ALWAYS_INLINE constexpr void
     553             : DataBox<tmpl::list<Tags...>>::reset_compute_item() noexcept {
     554             :   // reference items do not need to be reset
     555             :   if constexpr (db::is_compute_tag_v<ImmutableItemTag>) {
     556             :     get_item<ImmutableItemTag>().reset();
     557             :   }
     558             : }
     559             : 
     560             : // This function recursively calls itself to reset all compute items
     561             : // that depended upon the mutated items.  It starts with
     562             : // TagsOfImmutableItemsToReset as the tags of the immutable items that directly
     563             : // depended upon the mutated items.  This function resets the compute items in
     564             : // TagsOfImmutableItemsToReset and then constructs the list of immutable items
     565             : // that directly depend on TagsOfImmutableItemsToReset which will become the
     566             : // TagsOfImmutableItemsToReset on the next invocation of the function.  The
     567             : // recursion terminates when TagsOfImmutableItemsToReset becomes an empty list
     568             : // (using the function overload inlined above in the definition of DataBox).
     569             : template <typename... Tags>
     570             : template <typename... TagsOfImmutableItemsToReset>
     571             : SPECTRE_ALWAYS_INLINE constexpr void
     572             : db::DataBox<tmpl::list<Tags...>>::reset_compute_items_after_mutate(
     573             :     tmpl::list<TagsOfImmutableItemsToReset...> /*meta*/) noexcept {
     574             :   EXPAND_PACK_LEFT_TO_RIGHT(reset_compute_item<TagsOfImmutableItemsToReset>());
     575             :   using current_tags_to_reset = tmpl::list<TagsOfImmutableItemsToReset...>;
     576             :   using next_compute_tags_to_reset = tmpl::list_difference<
     577             :       tmpl::remove_duplicates<tmpl::transform<
     578             :           tmpl::append<
     579             :               tmpl::filter<typename DataBox<tmpl::list<Tags...>>::edge_list,
     580             :                            std::is_same<tmpl::pin<TagsOfImmutableItemsToReset>,
     581             :                                         tmpl::get_source<tmpl::_1>>>...>,
     582             :           tmpl::get_destination<tmpl::_1>>>,
     583             :       current_tags_to_reset>;
     584             :   reset_compute_items_after_mutate(next_compute_tags_to_reset{});
     585             : }
     586             : 
     587             : template <typename... Tags>
     588             : template <typename ParentTag, typename... Subtags>
     589             : SPECTRE_ALWAYS_INLINE constexpr void
     590             : db::DataBox<tmpl::list<Tags...>>::mutate_mutable_subitems(
     591             :     tmpl::list<Subtags...> /*meta*/) noexcept {
     592             :   const auto helper = [this](auto tag_v) noexcept {
     593             :     (void)this;  // Compiler bug warns about unused this capture
     594             :     using tag = decltype(tag_v);
     595             :       Subitems<ParentTag>::template create_item<tag>(
     596             :           make_not_null(&get_item<ParentTag>().mutate()),
     597             :           make_not_null(&get_item<tag>().mutate()));
     598             :   };
     599             : 
     600             :   EXPAND_PACK_LEFT_TO_RIGHT(helper(Subtags{}));
     601             : }
     602             : 
     603             : /*!
     604             :  * \ingroup DataBoxGroup
     605             :  * \brief Allows changing the state of one or more non-computed elements in
     606             :  * the DataBox
     607             :  *
     608             :  * `mutate()`'s first argument is the DataBox from which to retrieve the tags
     609             :  * `MutateTags`. The objects corresponding to the `MutateTags` are then passed
     610             :  * to `invokable`, which is a lambda or a function object taking as many
     611             :  * arguments as there are `MutateTags` and with the arguments being of types
     612             :  * `gsl::not_null<db::item_type<MutateTags>*>...`. Inside the `invokable` no
     613             :  * items can be retrieved from the DataBox `box`. This is to avoid confusing
     614             :  * subtleties with order of evaluation of compute items, as well as dangling
     615             :  * references. If an `invokable` needs read access to items in `box` they should
     616             :  * be passed as additional arguments to `mutate`. Capturing them by reference in
     617             :  * a lambda does not work because of a bug in GCC 6.3 and earlier. For a
     618             :  * function object the read-only items can also be stored as const references
     619             :  * inside the object by passing `db::get<TAG>(t)` to the constructor.
     620             :  *
     621             :  * \example
     622             :  * \snippet Test_DataBox.cpp databox_mutate_example
     623             :  */
     624             : template <typename... MutateTags, typename TagList, typename Invokable,
     625             :           typename... Args>
     626           1 : void mutate(const gsl::not_null<DataBox<TagList>*> box, Invokable&& invokable,
     627             :             Args&&... args) noexcept {
     628             :   static_assert(
     629             :       tmpl2::flat_all_v<
     630             :           detail::has_unique_matching_tag_v<TagList, MutateTags>...>,
     631             :       "One of the tags being mutated could not be found in the DataBox or "
     632             :       "is a base tag identifying more than one tag.");
     633             :   static_assert(tmpl2::flat_all_v<tmpl::list_contains_v<
     634             :                     typename DataBox<TagList>::mutable_item_tags,
     635             :                     detail::first_matching_tag<TagList, MutateTags>>...>,
     636             :                 "Can only mutate mutable items");
     637             :   if (UNLIKELY(box->mutate_locked_box_)) {
     638             :     ERROR(
     639             :         "Unable to mutate a DataBox that is already being mutated. This "
     640             :         "error occurs when mutating a DataBox from inside the invokable "
     641             :         "passed to the mutate function.");
     642             :   }
     643             :   box->mutate_locked_box_ = true;
     644             :   invokable(
     645             :       make_not_null(&box->template get_item<
     646             :                             detail::first_matching_tag<TagList, MutateTags>>()
     647             :                          .mutate())...,
     648             :       std::forward<Args>(args)...);
     649             :   using mutate_tags_list =
     650             :       tmpl::list<detail::first_matching_tag<TagList, MutateTags>...>;
     651             :   // For all the tags in the DataBox, check if one of their subtags is
     652             :   // being mutated and if so add the parent to the list of tags
     653             :   // being mutated. Then, remove any tags that would be passed
     654             :   // multiple times.
     655             :   using extra_mutated_tags = tmpl::list_difference<
     656             :       tmpl::filter<TagList,
     657             :                    tmpl::bind<tmpl::found, Subitems<tmpl::_1>,
     658             :                               tmpl::pin<tmpl::bind<tmpl::list_contains,
     659             :                                                    tmpl::pin<mutate_tags_list>,
     660             :                                                    tmpl::_1>>>>,
     661             :       mutate_tags_list>;
     662             :   // Extract the subtags inside the MutateTags and reset compute items
     663             :   // depending on those too.
     664             :   using full_mutated_items =
     665             :       tmpl::append<detail::expand_subitems<mutate_tags_list>,
     666             :                    extra_mutated_tags>;
     667             : 
     668             :   using first_compute_items_to_reset = tmpl::remove_duplicates<
     669             :       tmpl::transform<tmpl::filter<typename DataBox<TagList>::edge_list,
     670             :                                    tmpl::bind<tmpl::list_contains,
     671             :                                               tmpl::pin<full_mutated_items>,
     672             :                                               tmpl::get_source<tmpl::_1>>>,
     673             :                       tmpl::get_destination<tmpl::_1>>>;
     674             : 
     675             :   EXPAND_PACK_LEFT_TO_RIGHT(box->template mutate_mutable_subitems<MutateTags>(
     676             :       typename Subitems<MutateTags>::type{}));
     677             :   box->template reset_compute_items_after_mutate(
     678             :       first_compute_items_to_reset{});
     679             : 
     680             :   box->mutate_locked_box_ = false;
     681             : }
     682             : 
     683             : ////////////////////////////////////////////////////////////////
     684             : // Retrieving items from the DataBox
     685             : 
     686             : /// \cond
     687             : template <typename... Tags>
     688             : template <typename ComputeTag, typename... ArgumentTags>
     689             : void DataBox<tmpl::list<Tags...>>::evaluate_compute_item(
     690             :     tmpl::list<ArgumentTags...> /*meta*/) const noexcept {
     691             :   get_item<ComputeTag>().evaluate(get<ArgumentTags>()...);
     692             : }
     693             : 
     694             : template <typename... Tags>
     695             : template <typename Tag>
     696             : const auto& DataBox<tmpl::list<Tags...>>::get() const noexcept {
     697             :   if constexpr (std::is_same_v<Tag, ::Tags::DataBox>) {
     698             :     if (UNLIKELY(mutate_locked_box_)) {
     699             :       ERROR(
     700             :           "Unable to retrieve a (compute) item 'DataBox' from the DataBox from "
     701             :           "within a call to mutate. You must pass these either through the "
     702             :           "capture list of the lambda or the constructor of a class, this "
     703             :           "restriction exists to avoid complexity.");
     704             :     }
     705             :     return *this;
     706             :   } else {
     707             :     DEBUG_STATIC_ASSERT(
     708             :         not detail::has_no_matching_tag_v<tags_list, Tag>,
     709             :         "Found no tags in the DataBox that match the tag being retrieved.");
     710             :     DEBUG_STATIC_ASSERT(
     711             :         detail::has_unique_matching_tag_v<tags_list, Tag>,
     712             :         "Found more than one tag in the DataBox that matches the tag "
     713             :         "being retrieved. This happens because more than one tag with the same "
     714             :         "base (class) tag was added to the DataBox.");
     715             :     using item_tag = detail::first_matching_tag<tags_list, Tag>;
     716             :     if (UNLIKELY(mutate_locked_box_)) {
     717             :       ERROR("Unable to retrieve a (compute) item '"
     718             :             << db::tag_name<item_tag>()
     719             :             << "' from the DataBox from within a "
     720             :                "call to mutate. You must pass these either through the capture "
     721             :                "list of the lambda or the constructor of a class, this "
     722             :                "restriction exists to avoid complexity.");
     723             :     }
     724             :     if constexpr (detail::Item<item_tag>::item_type ==
     725             :                   detail::ItemType::Reference) {
     726             :       return item_tag::get(get<typename item_tag::parent_tag>());
     727             :     } else {
     728             :       if constexpr (detail::Item<item_tag>::item_type ==
     729             :                     detail::ItemType::Compute) {
     730             :         if (not get_item<item_tag>().evaluated()) {
     731             :           evaluate_compute_item<item_tag>(typename item_tag::argument_tags{});
     732             :         }
     733             :       }
     734             :       if constexpr (tt::is_a_v<std::unique_ptr, typename item_tag::type>) {
     735             :         return *(get_item<item_tag>().get());
     736             :       } else {
     737             :         return get_item<item_tag>().get();
     738             :       }
     739             :     }
     740             :   }
     741             : }
     742             : /// \endcond
     743             : 
     744             : /*!
     745             :  * \ingroup DataBoxGroup
     746             :  * \brief Retrieve the item with tag `Tag` from the DataBox
     747             :  * \requires Type `Tag` is one of the Tags corresponding to an object stored in
     748             :  * the DataBox
     749             :  *
     750             :  * \return The object corresponding to the tag `Tag`
     751             :  */
     752             : template <typename Tag, typename TagList>
     753           1 : SPECTRE_ALWAYS_INLINE const auto& get(const DataBox<TagList>& box) noexcept {
     754             :   return box.template get<Tag>();
     755             : }
     756             : 
     757             : /*!
     758             :  * \ingroup DataBoxGroup
     759             :  * \brief List of Tags to remove from the DataBox
     760             :  */
     761             : template <typename... Tags>
     762           1 : using RemoveTags = tmpl::flatten<tmpl::list<Tags...>>;
     763             : 
     764             : /*!
     765             :  * \ingroup DataBoxGroup
     766             :  * \brief List of Tags to add to the DataBox
     767             :  */
     768             : template <typename... Tags>
     769           1 : using AddSimpleTags = tmpl::flatten<tmpl::list<Tags...>>;
     770             : 
     771             : /*!
     772             :  * \ingroup DataBoxGroup
     773             :  * \brief List of Compute Item Tags to add to the DataBox
     774             :  */
     775             : template <typename... Tags>
     776           1 : using AddComputeTags = tmpl::flatten<tmpl::list<Tags...>>;
     777             : 
     778             : namespace detail {
     779             : template <class TagList>
     780             : struct compute_dbox_type {
     781             :   using immutable_item_tags = detail::expand_subitems<
     782             :       tmpl::filter<TagList, db::is_immutable_item_tag<tmpl::_1>>>;
     783             :   using mutable_item_tags = detail::expand_subitems<
     784             :       tmpl::filter<TagList, db::is_mutable_item_tag<tmpl::_1>>>;
     785             :   using type = DataBox<tmpl::append<mutable_item_tags, immutable_item_tags>>;
     786             : };
     787             : }  // namespace detail
     788             : 
     789             : /*!
     790             :  * \ingroup DataBoxGroup
     791             :  * \brief Returns the type of the DataBox that would be constructed from the
     792             :  * `TagList` of tags.
     793             :  */
     794             : template <typename TagList>
     795           1 : using compute_databox_type = typename detail::compute_dbox_type<TagList>::type;
     796             : 
     797             : /*!
     798             :  * \ingroup DataBoxGroup
     799             :  * \brief Create a new DataBox
     800             :  *
     801             :  * \details
     802             :  * Creates a new DataBox holding types Tags::type filled with the arguments
     803             :  * passed to the function. Compute and reference items must be added so that
     804             :  * their dependencies are added before themselves. For example, say you have
     805             :  * compute items `A` and `B` where `B` depends on `A`, then you must add them
     806             :  * using `db::AddImmutableItemTags<A, B>`.
     807             :  *
     808             :  * \example
     809             :  * \snippet Test_DataBox.cpp create_databox
     810             :  *
     811             :  * \see create_from
     812             :  *
     813             :  * \tparam AddMutableItemTags the tags of the mutable items that are being added
     814             :  * \tparam AddImmutableItemTags list of \ref ComputeTag "compute item tags" and
     815             :  *         \ref ReferenceTag "refernce item tags" to add to the DataBox
     816             :  * \param args the initial values for the mutable items to add to the DataBox
     817             :  */
     818             : template <typename AddMutableItemTags,
     819             :           typename AddImmutableItemTags = tmpl::list<>, typename... Args>
     820           1 : SPECTRE_ALWAYS_INLINE constexpr auto create(Args&&... args) {
     821             :   static_assert(tt::is_a_v<tmpl::list, AddImmutableItemTags>,
     822             :                 "AddImmutableItemTags must be a tmpl::list");
     823             :   static_assert(tt::is_a_v<tmpl::list, AddMutableItemTags>,
     824             :                 "AddMutableItemTags must be a tmpl::list");
     825             :   static_assert(
     826             :       tmpl::all<AddMutableItemTags, is_mutable_item_tag<tmpl::_1>>::value,
     827             :       "Cannot add any ComputeTags or ReferenceTags in the AddMutableTags list, "
     828             :       "must use the AddImmutableItemTags list.");
     829             :   static_assert(
     830             :       tmpl::all<AddImmutableItemTags, is_immutable_item_tag<tmpl::_1>>::value,
     831             :       "Cannot add any SimpleTags in the AddImmutableItemTags list, must use "
     832             :       "the "
     833             :       "AddMutableItemTags list.");
     834             : 
     835             :   using mutable_item_tags = detail::expand_subitems<AddMutableItemTags>;
     836             :   using immutable_item_tags = detail::expand_subitems<AddImmutableItemTags>;
     837             : 
     838             :   return db::DataBox<tmpl::append<mutable_item_tags, immutable_item_tags>>(
     839             :       AddMutableItemTags{}, AddImmutableItemTags{},
     840             :       std::forward<Args>(args)...);
     841             : }
     842             : 
     843             : namespace detail {
     844             : template <typename RemoveTags, typename AddMutableItemTags,
     845             :           typename AddImmutableItemTags, typename Box, typename... Args>
     846             : SPECTRE_ALWAYS_INLINE constexpr auto create_from(Box&& box,
     847             :                                                  Args&&... args) noexcept {
     848             :   static_assert(tmpl::size<AddMutableItemTags>::value == sizeof...(Args),
     849             :                 "Must pass in as many arguments as AddMutableItemTags to "
     850             :                 "db::create_from");
     851             : 
     852             :   // 1. Full list of old tags, and the derived tags list of the RemoveTags
     853             :   using old_tags = typename std::decay_t<Box>::tags_list;
     854             :   static_assert(
     855             :       tmpl::all<RemoveTags, has_unique_matching_tag<tmpl::pin<old_tags>,
     856             :                                                     tmpl::_1>>::value,
     857             :       "One of the tags being removed could not be found in the DataBox or "
     858             :       "is a base tag identifying more than one tag.");
     859             :   using remove_tags =
     860             :       tmpl::transform<RemoveTags, tmpl::bind<first_matching_tag,
     861             :                                              tmpl::pin<old_tags>, tmpl::_1>>;
     862             : 
     863             :   // 2. Expand subitems of tags to remove
     864             :   using immutable_item_tags_to_remove = expand_subitems<
     865             :       tmpl::filter<remove_tags, db::is_immutable_item_tag<tmpl::_1>>>;
     866             :   using mutable_item_tags_to_remove = expand_subitems<
     867             :       tmpl::filter<remove_tags, db::is_mutable_item_tag<tmpl::_1>>>;
     868             : 
     869             :   // 3. Expand subitems of tags to add
     870             :   using mutable_item_tags_to_add = expand_subitems<AddMutableItemTags>;
     871             :   using immutable_item_tags_to_add = expand_subitems<AddImmutableItemTags>;
     872             : 
     873             :   // 4. Create lists of tags to keep
     874             :   using mutable_item_tags_to_keep =
     875             :       tmpl::list_difference<typename std::decay_t<Box>::mutable_item_tags,
     876             :                             mutable_item_tags_to_remove>;
     877             :   using immutable_item_tags_to_keep =
     878             :       tmpl::list_difference<typename std::decay_t<Box>::immutable_item_tags,
     879             :                             immutable_item_tags_to_remove>;
     880             :   using old_tags_to_keep =
     881             :       tmpl::append<mutable_item_tags_to_keep, immutable_item_tags_to_keep>;
     882             : 
     883             :   // 5. List of the new tags
     884             :   using new_tags =
     885             :       tmpl::append<mutable_item_tags_to_keep, mutable_item_tags_to_add,
     886             :                    immutable_item_tags_to_keep, immutable_item_tags_to_add>;
     887             : 
     888             :   DEBUG_STATIC_ASSERT(
     889             :       tmpl::size<
     890             :           tmpl::list_difference<AddMutableItemTags, RemoveTags>>::value ==
     891             :           tmpl::size<AddMutableItemTags>::value,
     892             :       "Use db::mutate to mutate mutable items, do not remove and add them with "
     893             :       "db::create_from.");
     894             : 
     895             : #ifdef SPECTRE_DEBUG
     896             :   // Check that we're not removing a subitem itself, should remove the parent.
     897             :   using old_immutable_subitem_tags =
     898             :       tmpl::filter<typename std::decay_t<Box>::immutable_item_creation_tags,
     899             :                    tt::is_a<::Tags::Subitem, tmpl::_1>>;
     900             :   using old_subitem_tags =
     901             :       tmpl::append<typename std::decay_t<Box>::mutable_subitem_tags,
     902             :                    old_immutable_subitem_tags>;
     903             :   using remove_tags_minus_old_subitem_tags =
     904             :       tmpl::list_difference<remove_tags, old_subitem_tags>;
     905             :   static_assert(tmpl::size<remove_tags_minus_old_subitem_tags>::value ==
     906             :                     tmpl::size<remove_tags>::value,
     907             :                 "You are not allowed to remove a subitem of an item from the "
     908             :                 "DataBox using db::create_from.");
     909             : #endif  // ifdef SPECTRE_DEBUG
     910             : 
     911             :   return DataBox<new_tags>(std::forward<Box>(box), old_tags_to_keep{},
     912             :                            AddMutableItemTags{}, AddImmutableItemTags{},
     913             :                            std::forward<Args>(args)...);
     914             : }
     915             : }  // namespace detail
     916             : 
     917             : /*!
     918             :  * \ingroup DataBoxGroup
     919             :  * \brief Create a new DataBox from an existing one adding or removing items
     920             :  *
     921             :  * \example
     922             :  * Removing an item is done using:
     923             :  * \snippet Test_DataBox.cpp create_from_remove
     924             :  * Adding a mutable item is done using:
     925             :  * \snippet Test_DataBox.cpp create_from_add_item
     926             :  * Adding an immutable item is done using:
     927             :  * \snippet Test_DataBox.cpp create_from_add_compute_item
     928             :  *
     929             :  * \see create DataBox
     930             :  *
     931             :  * \tparam RemoveTags typelist of Tags to remove
     932             :  * \tparam AddMutableItemTags typelist of Tags for mutable items corresponding
     933             :  *         to the arguments to be added
     934             :  * \tparam AddImmutableItemTags list of \ref ComputeTag "compute item tags" and
     935             :  *         \ref ReferenceTag "refernce item tags" to add to the DataBox
     936             :  * \param box the DataBox the new box should be based off
     937             :  * \param args the initial values for the mutable items to add to the DataBox
     938             :  * \return the new DataBox
     939             :  */
     940             : template <typename RemoveTags, typename AddMutableItemTags = tmpl::list<>,
     941             :           typename AddImmutableItemTags = tmpl::list<>, typename TagsList,
     942             :           typename... Args>
     943           1 : SPECTRE_ALWAYS_INLINE constexpr auto create_from(db::DataBox<TagsList>&& box,
     944             :                                                  Args&&... args) noexcept {
     945             :   return detail::create_from<RemoveTags, AddMutableItemTags,
     946             :                              AddImmutableItemTags>(std::move(box),
     947             :                                                    std::forward<Args>(args)...);
     948             : }
     949             : 
     950             : namespace detail {
     951             : CREATE_IS_CALLABLE(apply)
     952             : CREATE_IS_CALLABLE_V(apply)
     953             : 
     954             : template <typename Func, typename... Args>
     955             : constexpr void error_function_not_callable() noexcept {
     956             :   static_assert(std::is_same_v<Func, void>,
     957             :                 "The function is not callable with the expected arguments.  "
     958             :                 "See the first template parameter of "
     959             :                 "error_function_not_callable for the function type and "
     960             :                 "the remaining arguments for the parameters that cannot be "
     961             :                 "passed.");
     962             : }
     963             : 
     964             : template <typename DataBoxTags, typename... TagsToRetrieve>
     965             : constexpr bool check_tags_are_in_databox(
     966             :     DataBoxTags /*meta*/, tmpl::list<TagsToRetrieve...> /*meta*/) noexcept {
     967             :   static_assert(
     968             :       (tag_is_retrievable_v<TagsToRetrieve, DataBox<DataBoxTags>> and ...),
     969             :       "A desired tag is not in the DataBox.  See the first template "
     970             :       "argument of tag_is_retrievable_v for the missing tag, and the "
     971             :       "second for the available tags.");
     972             :   return true;
     973             : }
     974             : 
     975             : template <typename... ArgumentTags, typename F, typename BoxTags,
     976             :           typename... Args>
     977             : static constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
     978             :                             tmpl::list<ArgumentTags...> /*meta*/,
     979             :                             Args&&... args) noexcept {
     980             :   if constexpr (is_apply_callable_v<
     981             :                     F, const_item_type<ArgumentTags, BoxTags>..., Args...>) {
     982             :     return F::apply(::db::get<ArgumentTags>(box)...,
     983             :                     std::forward<Args>(args)...);
     984             :   } else if constexpr (::tt::is_callable_v<
     985             :                            std::remove_pointer_t<F>,
     986             :                            tmpl::conditional_t<
     987             :                                std::is_same_v<ArgumentTags, ::Tags::DataBox>,
     988             :                                const DataBox<BoxTags>&,
     989             :                                const_item_type<ArgumentTags, BoxTags>>...,
     990             :                            Args...>) {
     991             :     return std::forward<F>(f)(::db::get<ArgumentTags>(box)...,
     992             :                               std::forward<Args>(args)...);
     993             :   } else {
     994             :     error_function_not_callable<
     995             :         std::remove_pointer_t<F>,
     996             :         tmpl::conditional_t<std::is_same_v<ArgumentTags, ::Tags::DataBox>,
     997             :                             const DataBox<BoxTags>&,
     998             :                             const_item_type<ArgumentTags, BoxTags>>...,
     999             :         Args...>();
    1000             :   }
    1001             : }
    1002             : }  // namespace detail
    1003             : 
    1004             : // @{
    1005             : /*!
    1006             :  * \ingroup DataBoxGroup
    1007             :  * \brief Apply the invokable `f` with argument Tags `TagsList` from
    1008             :  * DataBox `box`
    1009             :  *
    1010             :  * \details
    1011             :  * `f` must either be invokable with the arguments of type
    1012             :  * `db::const_item_type<TagsList>..., Args...` where the first pack expansion
    1013             :  * is over the elements in the type list `ArgumentTags`, or have a static
    1014             :  * `apply` function that is callable with the same types.
    1015             :  * If the class that implements the static `apply` functions also provides an
    1016             :  * `argument_tags` typelist, then it is used and no explicit `ArgumentTags`
    1017             :  * template parameter should be specified.
    1018             :  *
    1019             :  * \usage
    1020             :  * Given a function `func` that takes arguments of types
    1021             :  * `T1`, `T2`, `A1` and `A2`. Let the Tags for the quantities of types `T1` and
    1022             :  * `T2` in the DataBox `box` be `Tag1` and `Tag2`, and objects `a1` of type
    1023             :  * `A1` and `a2` of type `A2`, then
    1024             :  * \code
    1025             :  * auto result = db::apply<tmpl::list<Tag1, Tag2>>(func, box, a1, a2);
    1026             :  * \endcode
    1027             :  * \return `decltype(func(db::get<Tag1>(box), db::get<Tag2>(box), a1, a2))`
    1028             :  *
    1029             :  * \semantics
    1030             :  * For tags `Tags...` in a DataBox `box`, and a function `func` that takes
    1031             :  * `sizeof...(Tags)` arguments of types `db::const_item_type<Tags>...`,  and
    1032             :  * `sizeof...(Args)` arguments of types `Args...`,
    1033             :  * \code
    1034             :  * result = func(box, db::get<Tags>(box)..., args...);
    1035             :  * \endcode
    1036             :  *
    1037             :  * \example
    1038             :  * \snippet Test_DataBox.cpp apply_example
    1039             :  * Using a struct with an `apply` method:
    1040             :  * \snippet Test_DataBox.cpp apply_struct_example
    1041             :  * If the class `F` has no state, you can also use the stateless overload of
    1042             :  * `apply`: \snippet Test_DataBox.cpp apply_stateless_struct_example
    1043             :  *
    1044             :  * \see DataBox
    1045             :  * \tparam ArgumentTags typelist of Tags in the order that they are to be passed
    1046             :  * to `f`
    1047             :  * \tparam F The invokable to apply
    1048             :  */
    1049             : template <typename ArgumentTags, typename F, typename BoxTags, typename... Args>
    1050           1 : SPECTRE_ALWAYS_INLINE constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
    1051             :                                            Args&&... args) noexcept {
    1052             :   detail::check_tags_are_in_databox(
    1053             :       BoxTags{}, tmpl::remove<ArgumentTags, ::Tags::DataBox>{});
    1054             :   return detail::apply(std::forward<F>(f), box, ArgumentTags{},
    1055             :                        std::forward<Args>(args)...);
    1056             : }
    1057             : 
    1058             : template <typename F, typename BoxTags, typename... Args>
    1059           0 : SPECTRE_ALWAYS_INLINE constexpr auto apply(F&& f, const DataBox<BoxTags>& box,
    1060             :                                            Args&&... args) noexcept {
    1061             :   return apply<typename std::decay_t<F>::argument_tags>(
    1062             :       std::forward<F>(f), box, std::forward<Args>(args)...);
    1063             : }
    1064             : 
    1065             : template <typename F, typename BoxTags, typename... Args>
    1066           0 : SPECTRE_ALWAYS_INLINE constexpr auto apply(const DataBox<BoxTags>& box,
    1067             :                                            Args&&... args) noexcept {
    1068             :   return apply(F{}, box, std::forward<Args>(args)...);
    1069             : }
    1070             : // @}
    1071             : 
    1072             : namespace detail {
    1073             : template <typename... ReturnTags, typename... ArgumentTags, typename F,
    1074             :           typename BoxTags, typename... Args>
    1075             : SPECTRE_ALWAYS_INLINE constexpr void mutate_apply(
    1076             :     F&& f, const gsl::not_null<db::DataBox<BoxTags>*> box,
    1077             :     tmpl::list<ReturnTags...> /*meta*/, tmpl::list<ArgumentTags...> /*meta*/,
    1078             :     Args&&... args) noexcept {
    1079             :   static_assert(
    1080             :       not tmpl2::flat_any_v<std::is_same_v<ArgumentTags, Tags::DataBox>...> and
    1081             :           not tmpl2::flat_any_v<std::is_same_v<ReturnTags, Tags::DataBox>...>,
    1082             :       "Cannot pass a DataBox to mutate_apply since the db::get won't work "
    1083             :       "inside mutate_apply.");
    1084             :   if constexpr (is_apply_callable_v<
    1085             :                     F, const gsl::not_null<item_type<ReturnTags, BoxTags>*>...,
    1086             :                     const_item_type<ArgumentTags, BoxTags>..., Args...>) {
    1087             :     ::db::mutate<ReturnTags...>(
    1088             :         box,
    1089             :         [
    1090             :         ](const gsl::not_null<item_type<ReturnTags, BoxTags>*>... mutated_items,
    1091             :           const_item_type<ArgumentTags, BoxTags>... args_items,
    1092             :           decltype(std::forward<Args>(args))... l_args) noexcept {
    1093             :           return std::decay_t<F>::apply(mutated_items..., args_items...,
    1094             :                                         std::forward<Args>(l_args)...);
    1095             :         },
    1096             :         db::get<ArgumentTags>(*box)..., std::forward<Args>(args)...);
    1097             :   } else if constexpr (
    1098             :       ::tt::is_callable_v<
    1099             :           F, const gsl::not_null<item_type<ReturnTags, BoxTags>*>...,
    1100             :           const_item_type<ArgumentTags, BoxTags>..., Args...>) {
    1101             :     ::db::mutate<ReturnTags...>(
    1102             :         box,
    1103             :         [&f](const gsl::not_null<
    1104             :                  item_type<ReturnTags, BoxTags>*>... mutated_items,
    1105             :              const_item_type<ArgumentTags, BoxTags>... args_items,
    1106             :              decltype(std::forward<Args>(args))... l_args) noexcept {
    1107             :           return f(mutated_items..., args_items...,
    1108             :                    std::forward<Args>(l_args)...);
    1109             :         },
    1110             :         db::get<ArgumentTags>(*box)..., std::forward<Args>(args)...);
    1111             :   } else {
    1112             :     error_function_not_callable<
    1113             :         F, gsl::not_null<item_type<ReturnTags, BoxTags>*>...,
    1114             :         const_item_type<ArgumentTags, BoxTags>..., Args...>();
    1115             :   }
    1116             : }
    1117             : }  // namespace detail
    1118             : 
    1119             : // @{
    1120             : /*!
    1121             :  * \ingroup DataBoxGroup
    1122             :  * \brief Apply the invokable `f` mutating items `MutateTags` and taking as
    1123             :  * additional arguments `ArgumentTags` and `args`.
    1124             :  *
    1125             :  * \details
    1126             :  * `f` must either be invokable with the arguments of type
    1127             :  * `gsl::not_null<db::item_type<MutateTags>*>...,
    1128             :  * db::const_item_type<ArgumentTags>..., Args...`
    1129             :  * where the first two pack expansions are over the elements in the typelists
    1130             :  * `MutateTags` and `ArgumentTags`, or have a static `apply` function that is
    1131             :  * callable with the same types. If the type of `f` specifies `return_tags` and
    1132             :  * `argument_tags` typelists, these are used for the `MutateTags` and
    1133             :  * `ArgumentTags`, respectively.
    1134             :  *
    1135             :  * \example
    1136             :  * An example of using `mutate_apply` with a lambda:
    1137             :  * \snippet Test_DataBox.cpp mutate_apply_lambda_example
    1138             :  *
    1139             :  * An example of a class with a static `apply` function
    1140             :  * \snippet Test_DataBox.cpp mutate_apply_struct_definition_example
    1141             :  * and how to use `mutate_apply` with the above class
    1142             :  * \snippet Test_DataBox.cpp mutate_apply_struct_example_stateful
    1143             :  * Note that the class exposes `return_tags` and `argument_tags` typelists, so
    1144             :  * we don't specify the template parameters explicitly.
    1145             :  * If the class `F` has no state, like in this example,
    1146             :  * \snippet Test_DataBox.cpp mutate_apply_struct_definition_example
    1147             :  * you can also use the stateless overload of `mutate_apply`:
    1148             :  * \snippet Test_DataBox.cpp mutate_apply_struct_example_stateless
    1149             :  *
    1150             :  * \tparam MutateTags typelist of Tags to mutate
    1151             :  * \tparam ArgumentTags typelist of additional items to retrieve from the
    1152             :  * DataBox
    1153             :  * \tparam F The invokable to apply
    1154             :  */
    1155             : template <typename MutateTags, typename ArgumentTags, typename F,
    1156             :           typename BoxTags, typename... Args>
    1157           1 : SPECTRE_ALWAYS_INLINE constexpr void mutate_apply(
    1158             :     F&& f, const gsl::not_null<DataBox<BoxTags>*> box,
    1159             :     Args&&... args) noexcept {
    1160             :   detail::check_tags_are_in_databox(BoxTags{}, MutateTags{});
    1161             :   detail::check_tags_are_in_databox(BoxTags{}, ArgumentTags{});
    1162             :   detail::mutate_apply(std::forward<F>(f), box, MutateTags{}, ArgumentTags{},
    1163             :                        std::forward<Args>(args)...);
    1164             : }
    1165             : 
    1166             : template <typename F, typename BoxTags, typename... Args>
    1167           0 : SPECTRE_ALWAYS_INLINE constexpr void mutate_apply(
    1168             :     F&& f, const gsl::not_null<DataBox<BoxTags>*> box,
    1169             :     Args&&... args) noexcept {
    1170             :   mutate_apply<typename std::decay_t<F>::return_tags,
    1171             :                typename std::decay_t<F>::argument_tags>(
    1172             :       std::forward<F>(f), box, std::forward<Args>(args)...);
    1173             : }
    1174             : 
    1175             : template <typename F, typename BoxTags, typename... Args>
    1176           0 : SPECTRE_ALWAYS_INLINE constexpr void mutate_apply(
    1177             :     const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) noexcept {
    1178             :   mutate_apply(F{}, box, std::forward<Args>(args)...);
    1179             : }
    1180             : // @}
    1181             : }  // namespace db

Generated by: LCOV version 1.14