SpECTRE Documentation Coverage Report
Current view: top level - DataStructures/Tensor - Tensor.hpp Hit Total Coverage
Commit: 923cd4a8ea30f5a5589baa60b0a93e358ca9f8e8 Lines: 40 83 48.2 %
Date: 2025-11-07 19:37:56
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 for Tensor
       6             : 
       7             : #pragma once
       8             : 
       9             : #include <concepts>
      10             : #include <cstddef>
      11             : #include <pup.h>
      12             : #include <pup_stl.h>
      13             : 
      14             : #include "DataStructures/ComplexDataVector.hpp"
      15             : #include "DataStructures/ComplexModalVector.hpp"
      16             : #include "DataStructures/DataVector.hpp"
      17             : #include "DataStructures/ModalVector.hpp"
      18             : #include "DataStructures/SpinWeighted.hpp"
      19             : #include "DataStructures/Tensor/Expressions/AddSubtract.hpp"
      20             : #include "DataStructures/Tensor/Expressions/Contract.hpp"
      21             : #include "DataStructures/Tensor/Expressions/DataTypeSupport.hpp"
      22             : #include "DataStructures/Tensor/Expressions/Divide.hpp"
      23             : #include "DataStructures/Tensor/Expressions/Evaluate.hpp"
      24             : #include "DataStructures/Tensor/Expressions/IndexPropertyCheck.hpp"
      25             : #include "DataStructures/Tensor/Expressions/LhsTensorSymmAndIndices.hpp"
      26             : #include "DataStructures/Tensor/Expressions/Negate.hpp"
      27             : #include "DataStructures/Tensor/Expressions/NumberAsExpression.hpp"
      28             : #include "DataStructures/Tensor/Expressions/Product.hpp"
      29             : #include "DataStructures/Tensor/Expressions/SpatialSpacetimeIndex.hpp"
      30             : #include "DataStructures/Tensor/Expressions/SquareRoot.hpp"
      31             : #include "DataStructures/Tensor/Expressions/TensorAsExpression.hpp"
      32             : #include "DataStructures/Tensor/Expressions/TensorExpression.hpp"
      33             : #include "DataStructures/Tensor/Expressions/TensorIndex.hpp"
      34             : #include "DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp"
      35             : #include "DataStructures/Tensor/Expressions/TimeIndex.hpp"
      36             : #include "DataStructures/Tensor/IndexType.hpp"
      37             : #include "DataStructures/Tensor/Structure.hpp"
      38             : #include "DataStructures/Tensor/TypeAliases.hpp"
      39             : #include "Utilities/ErrorHandling/Error.hpp"
      40             : #include "Utilities/ForceInline.hpp"
      41             : #include "Utilities/Gsl.hpp"
      42             : #include "Utilities/MakeArray.hpp"
      43             : #include "Utilities/MakeWithValue.hpp"
      44             : #include "Utilities/PrettyType.hpp"
      45             : #include "Utilities/SetNumberOfGridPoints.hpp"
      46             : #include "Utilities/Simd/Simd.hpp"
      47             : #include "Utilities/StdHelpers.hpp"
      48             : #include "Utilities/TMPL.hpp"
      49             : #include "Utilities/TypeTraits.hpp"
      50             : #include "Utilities/TypeTraits/IsStreamable.hpp"
      51             : // Not all the TensorExpression includes are necessary, but we include them so
      52             : // that Tensor.hpp provides a uniform interface to Tensors and TensorExpressions
      53             : 
      54             : /// \cond
      55             : template <typename X, typename Symm = Symmetry<>,
      56             :           typename IndexList = index_list<>>
      57             : class Tensor;
      58             : /// \endcond
      59             : 
      60             : namespace Tensor_detail {
      61             : template <typename T, typename = void>
      62             : struct is_tensor : std::false_type {};
      63             : template <typename X, typename Symm, typename IndexList>
      64             : struct is_tensor<Tensor<X, Symm, IndexList>> : std::true_type {};
      65             : }  // namespace Tensor_detail
      66             : 
      67             : /*!
      68             :  * \ingroup TensorGroup
      69             :  * \brief Represents an object with multiple components
      70             :  *
      71             :  * \details
      72             :  * Tensor is a container that represents indexable geometric objects. Each index
      73             :  * has its own dimension, valence, and frame and must be either spatial or
      74             :  * spacetime. Note that the dimension passed to `SpatialIndex` and
      75             :  * `SpacetimeIndex` is always the spatial dimension of the index. Tensors with
      76             :  * symmetric indices are stored only once and must be of the same
      77             :  * type. A list of available type aliases can be found in the ::tnsr namespace
      78             :  * where the adopted conventions are:
      79             :  *
      80             :  * 1. Upper case for contravariant or upper indices, lower case for covariant or
      81             :  * lower indices.
      82             :  *
      83             :  * 2. `a, b, c, d` are used for spacetime indices while `i, j, k, l` are used
      84             :  * for spatial indices.
      85             :  *
      86             :  * 3. `::Scalar` is not inside the `::tnsr` namespace but is used to represent
      87             :  * a scalar with no indices.
      88             :  *
      89             :  * \example
      90             :  * \snippet Test_Tensor.cpp scalar
      91             :  * \snippet Test_Tensor.cpp spatial_vector
      92             :  * \snippet Test_Tensor.cpp spacetime_vector
      93             :  * \snippet Test_Tensor.cpp rank_3_122
      94             :  *
      95             :  * \tparam X the type held
      96             :  * \tparam Symm the ::Symmetry of the indices
      97             :  * \tparam IndexList a typelist of \ref SpacetimeIndex "TensorIndexType"'s
      98             :  */
      99             : template <typename X, typename Symm, template <typename...> class IndexList,
     100             :           typename... Indices>
     101           1 : class Tensor<X, Symm, IndexList<Indices...>> {
     102             :   static_assert(sizeof...(Indices) < 5,
     103             :                 "If you are sure you need rank 5 or higher Tensor's please "
     104             :                 "file an issue on GitHub or discuss with a core developer of "
     105             :                 "SpECTRE.");
     106             :   static_assert(
     107             :       std::is_same_v<X, std::complex<double>> or std::is_same_v<X, double> or
     108             :           std::is_same_v<X, ComplexDataVector> or
     109             :           std::is_same_v<X, ComplexModalVector> or
     110             :           std::is_same_v<X, DataVector> or std::is_same_v<X, ModalVector> or
     111             :           is_spin_weighted_of_v<ComplexDataVector, X> or
     112             :           is_spin_weighted_of_v<ComplexModalVector, X> or
     113             :           simd::is_batch<X>::value,
     114             :       "Unsupported type. While other types are technically possible it is not "
     115             :       "clear that Tensor is the correct container for them. Please seek advice "
     116             :       "on the topic by discussing with the SpECTRE developers.");
     117             : 
     118             :  public:
     119             :   /// The type of the sequence that holds the data
     120           1 :   using storage_type =
     121             :       std::array<X, Tensor_detail::Structure<Symm, Indices...>::size()>;
     122             :   /// The type that is stored by the Tensor
     123           1 :   using type = X;
     124             :   /// Typelist of the symmetry of the Tensor
     125             :   ///
     126             :   /// \details
     127             :   /// For a rank-3 tensor symmetric in the last two indices,
     128             :   /// \f$T_{a(bc)}\f$, the ::Symmetry is `<2, 1, 1>`. For a non-symmetric rank-2
     129             :   /// tensor the ::Symmetry is `<2, 1>`.
     130           1 :   using symmetry = Symm;
     131             :   /// Typelist of the \ref SpacetimeIndex "TensorIndexType"'s that the
     132             :   /// Tensor has
     133           1 :   using index_list = tmpl::list<Indices...>;
     134             :   /// The number of indices that the Tensor has
     135           1 :   static constexpr size_t num_tensor_indices = sizeof...(Indices);
     136             :   /// The Tensor_detail::Structure for the particular tensor index structure
     137             :   ///
     138             :   /// Each tensor index structure, e.g. \f$T_{ab}\f$, \f$T_a{}^b\f$ or
     139             :   /// \f$T^{ab}\f$ has its own Tensor_detail::TensorStructure that holds
     140             :   /// information about how the data is stored, what the multiplicity of the
     141             :   /// stored indices are, the number of (independent) components, etc.
     142           1 :   using structure = Tensor_detail::Structure<Symm, Indices...>;
     143             :   /// The type of the TensorExpression that would represent this Tensor in a
     144             :   /// tensor expression.
     145             :   template <typename ArgsList>
     146           1 :   using TE = tenex::TensorAsExpression<Tensor<X, Symm, IndexList<Indices...>>,
     147             :                                        ArgsList>;
     148             : 
     149           0 :   Tensor() = default;
     150           0 :   ~Tensor() = default;
     151           0 :   Tensor(const Tensor&) = default;
     152           0 :   Tensor(Tensor&&) = default;
     153           0 :   Tensor& operator=(const Tensor&) = default;
     154           0 :   Tensor& operator=(Tensor&&) = default;
     155             : 
     156             :   /// Initialize a vector or scalar from an array
     157             :   ///
     158             :   /// \example
     159             :   /// \snippet Test_Tensor.cpp init_vector
     160             :   /// \param data the values of the individual components of the Vector
     161           1 :   explicit Tensor(storage_type data)
     162             :     requires(sizeof...(Indices) <= 1);
     163             : 
     164             :   /// Constructor that passes "args" to constructor of X and initializes each
     165             :   /// component to be the same
     166             :   template <typename Arg0, typename... Args>
     167           1 :   explicit Tensor(Arg0&& arg0, Args&&... args)
     168             :       // NOLINTNEXTLINE(readability-simplify-boolean-expr)
     169             :     requires(not(sizeof...(Args) == 0 and
     170             :                  std::same_as<std::decay_t<Arg0>, Tensor>) and
     171             :              std::constructible_from<X, Arg0, Args...>);
     172             : 
     173           0 :   using value_type = typename storage_type::value_type;
     174           0 :   using reference = typename storage_type::reference;
     175           0 :   using const_reference = typename storage_type::const_reference;
     176           0 :   using iterator = typename storage_type::iterator;
     177           0 :   using const_iterator = typename storage_type::const_iterator;
     178           0 :   using pointer = typename storage_type::pointer;
     179           0 :   using const_pointer = typename storage_type::const_pointer;
     180           0 :   using reverse_iterator = typename storage_type::reverse_iterator;
     181           0 :   using const_reverse_iterator = typename storage_type::const_reverse_iterator;
     182             : 
     183           0 :   iterator begin() { return data_.begin(); }
     184           0 :   const_iterator begin() const { return data_.begin(); }
     185           0 :   const_iterator cbegin() const { return data_.begin(); }
     186             : 
     187           0 :   iterator end() { return data_.end(); }
     188           0 :   const_iterator end() const { return data_.end(); }
     189           0 :   const_iterator cend() const { return data_.end(); }
     190             : 
     191           0 :   reverse_iterator rbegin() { return data_.rbegin(); }
     192           0 :   const_reverse_iterator rbegin() const { return data_.rbegin(); }
     193           0 :   const_reverse_iterator crbegin() const { return data_.rbegin(); }
     194             : 
     195           0 :   reverse_iterator rend() { return data_.rend(); }
     196           0 :   const_reverse_iterator rend() const { return data_.rend(); }
     197           0 :   const_reverse_iterator crend() const { return data_.rend(); }
     198             : 
     199             :   /// @{
     200             :   /// Get data entry using an array representing a tensor index
     201             :   ///
     202             :   /// \details
     203             :   /// Let \f$T_{abc}\f$ be a Tensor.
     204             :   /// Then `get({{0, 2, 1}})` returns the \f$T_{0 2 1}\f$ component.
     205             :   /// \param tensor_index the index at which to get the data
     206             :   template <typename T>
     207           1 :   SPECTRE_ALWAYS_INLINE constexpr reference get(
     208             :       const std::array<T, sizeof...(Indices)>& tensor_index) {
     209             :     return gsl::at(data_, structure::get_storage_index(tensor_index));
     210             :   }
     211             :   template <typename T>
     212           1 :   SPECTRE_ALWAYS_INLINE constexpr const_reference get(
     213             :       const std::array<T, sizeof...(Indices)>& tensor_index) const {
     214             :     return gsl::at(data_, structure::get_storage_index(tensor_index));
     215             :   }
     216             :   /// @}
     217             :   /// @{
     218             :   /// Get data entry using a list of integers representing a tensor index
     219             :   ///
     220             :   /// \details
     221             :   /// Let \f$T_{abc}\f$ be a Tensor.
     222             :   /// Then `get(0, 2, 1)` returns the \f$T_{0 2 1}\f$ component.
     223             :   /// \param n the index at which to get the data
     224             :   template <typename... N>
     225           1 :   constexpr reference get(N... n) {
     226             :     static_assert(
     227             :         sizeof...(Indices) == sizeof...(N),
     228             :         "the number of tensor indices specified must match the rank of "
     229             :         "the tensor");
     230             :     return gsl::at(data_, structure::get_storage_index(n...));
     231             :   }
     232             :   template <typename... N>
     233           1 :   constexpr const_reference get(N... n) const {
     234             :     static_assert(
     235             :         sizeof...(Indices) == sizeof...(N),
     236             :         "the number of tensor indices specified must match the rank of "
     237             :         "the tensor");
     238             :     return gsl::at(data_, structure::get_storage_index(n...));
     239             :   }
     240             :   /// @}
     241             : 
     242             :   /// @{
     243             :   /// Retrieve the index `N...` by computing the storage index at compile time
     244             :   // clang-tidy: redundant declaration (bug in clang-tidy)
     245             :   template <int... N, typename... Args>
     246             :   friend SPECTRE_ALWAYS_INLINE constexpr typename Tensor<Args...>::reference
     247           1 :   get(Tensor<Args...>& t);  // NOLINT
     248             :   // clang-tidy: redundant declaration (bug in clang-tidy)
     249             :   template <int... N, typename... Args>
     250             :   friend SPECTRE_ALWAYS_INLINE constexpr
     251             :       typename Tensor<Args...>::const_reference
     252           1 :       get(const Tensor<Args...>& t);  // NOLINT
     253             :                                       /// @}
     254             : 
     255             :   /// @{
     256             :   /// Retrieve a TensorExpression object with the index structure passed in
     257             :   template <typename... TensorIndices>
     258           1 :   SPECTRE_ALWAYS_INLINE constexpr auto operator()(
     259             :       TensorIndices... /*meta*/) const {
     260             :     static_assert((... and tt::is_tensor_index<TensorIndices>::value),
     261             :                   "The tensor expression must be created using TensorIndex "
     262             :                   "objects to represent generic indices, e.g. ti::a, ti::b, "
     263             :                   "etc.");
     264             :     static_assert(
     265             :         tenex::tensorindex_list_is_valid<tmpl::list<TensorIndices...>>::value,
     266             :         "Cannot create a tensor expression with a repeated generic index. "
     267             :         "(Note that the concrete time indices, ti::T and ti::t, can be "
     268             :         "repeated.) If you intend to contract, ensure that the indices to "
     269             :         "contract have opposite valences.");
     270             :     static_assert(
     271             :         std::is_same_v<tmpl::integral_list<UpLo, TensorIndices::valence...>,
     272             :                        tmpl::integral_list<UpLo, Indices::ul...>>,
     273             :         "The valences of the generic indices in the expression do "
     274             :         "not match the valences of the indices in the Tensor.");
     275             :     static_assert((... and (not(TensorIndices::is_spacetime and
     276             :                                 (Indices::index_type == IndexType::Spatial)))),
     277             :                   "Cannot use a spacetime index for a spatial index. e.g. "
     278             :                   "Cannot do R(ti::a), where R's index is spatial, because "
     279             :                   "ti::a denotes a generic spacetime index.");
     280             :     return tenex::contract(TE<tmpl::list<TensorIndices...>>{*this});
     281             :   }
     282             :   /// @}
     283             : 
     284             :   /// @{
     285             :   /// Return i'th component of storage vector
     286           1 :   constexpr reference operator[](const size_t storage_index) {
     287             :     return gsl::at(data_, storage_index);
     288             :   }
     289           1 :   constexpr const_reference operator[](const size_t storage_index) const {
     290             :     return gsl::at(data_, storage_index);
     291             :   }
     292             :   /// @}
     293             : 
     294             :   /// Return the number of independent components of the Tensor
     295             :   ///
     296             :   /// \details
     297             :   /// Returns the number of independent components of the Tensor taking into
     298             :   /// account symmetries. For example, let \f$T_{ab}\f$ be a n-dimensional
     299             :   /// rank-2 symmetric tensor, then the number of independent components is
     300             :   /// \f$n(n+1)/2\f$.
     301           1 :   SPECTRE_ALWAYS_INLINE static constexpr size_t size() {
     302             :     return structure::size();
     303             :   }
     304             : 
     305             :   /// Returns the rank of the Tensor
     306             :   ///
     307             :   /// \details
     308             :   /// The rank of a tensor is the number of indices it has. For example, the
     309             :   /// tensor \f$v^a\f$ is rank-1, the tensor \f$\phi\f$ is rank-0, and the
     310             :   /// tensor \f$T_{abc}\f$ is rank-3.
     311           1 :   SPECTRE_ALWAYS_INLINE static constexpr size_t rank() {
     312             :     return sizeof...(Indices);
     313             :   }
     314             : 
     315             :   /// @{
     316             :   /// Given an iterator or storage index, get the canonical tensor index.
     317             :   /// For scalars this is defined to be std::array<int, 1>{{0}}
     318             :   SPECTRE_ALWAYS_INLINE constexpr std::array<size_t, sizeof...(Indices)>
     319           1 :   get_tensor_index(const const_iterator& iter) const {
     320             :     return structure::get_canonical_tensor_index(
     321             :         static_cast<size_t>(iter - begin()));
     322             :   }
     323             :   SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t, sizeof...(Indices)>
     324           1 :   get_tensor_index(const size_t storage_index) {
     325             :     return structure::get_canonical_tensor_index(storage_index);
     326             :   }
     327             :   /// @}
     328             : 
     329             :   /// @{
     330             :   /// Get the storage index of the tensor index. Should only be used when
     331             :   /// optimizing code in which computing the storage index is a bottleneck.
     332             :   template <typename... N>
     333           1 :   SPECTRE_ALWAYS_INLINE static constexpr size_t get_storage_index(
     334             :       const N... args) {
     335             :     return structure::get_storage_index(args...);
     336             :   }
     337             :   template <typename I>
     338           1 :   SPECTRE_ALWAYS_INLINE static constexpr size_t get_storage_index(
     339             :       const std::array<I, sizeof...(Indices)>& tensor_index) {
     340             :     return structure::get_storage_index(tensor_index);
     341             :   }
     342             :   /// @}
     343             : 
     344             :   /// @{
     345             :   /// Given an iterator or storage index, get the multiplicity of an index
     346             :   ///
     347             :   /// \see TensorMetafunctions::compute_multiplicity
     348           1 :   SPECTRE_ALWAYS_INLINE constexpr size_t multiplicity(
     349             :       const const_iterator& iter) const {
     350             :     return structure::multiplicity(static_cast<size_t>(iter - begin()));
     351             :   }
     352           1 :   SPECTRE_ALWAYS_INLINE static constexpr size_t multiplicity(
     353             :       const size_t storage_index) {
     354             :     return structure::multiplicity(storage_index);
     355             :   }
     356             :   /// @}
     357             : 
     358             :   /// @{
     359             :   /// Get dimensionality of i'th tensor index
     360             :   ///
     361             :   /// \snippet Test_Tensor.cpp index_dim
     362             :   /// \see ::index_dim
     363           1 :   SPECTRE_ALWAYS_INLINE static constexpr size_t index_dim(const size_t i) {
     364             :     static_assert(sizeof...(Indices),
     365             :                   "A scalar does not have any indices from which you can "
     366             :                   "retrieve the dimensionality.");
     367             :     return gsl::at(structure::dims(), i);
     368             :   }
     369             :   /// @}
     370             : 
     371             :   /// @{
     372             :   /// Return an array corresponding to the ::Symmetry of the Tensor
     373             :   SPECTRE_ALWAYS_INLINE static constexpr std::array<int, sizeof...(Indices)>
     374           1 :   symmetries() {
     375             :     return structure::symmetries();
     376             :   }
     377             :   /// @}
     378             : 
     379             :   /// @{
     380             :   /// Return array of the ::IndexType's (spatial or spacetime)
     381             :   SPECTRE_ALWAYS_INLINE static constexpr std::array<IndexType,
     382             :                                                     sizeof...(Indices)>
     383           1 :   index_types() {
     384             :     return structure::index_types();
     385             :   }
     386             :   /// @}
     387             : 
     388             :   /// @{
     389             :   /// Return array of dimensionality of each index
     390             :   ///
     391             :   /// \snippet Test_Tensor.cpp index_dim
     392             :   /// \see index_dim ::index_dim
     393             :   SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t, sizeof...(Indices)>
     394           1 :   index_dims() {
     395             :     return structure::dims();
     396             :   }
     397             :   /// @}
     398             : 
     399             :   /// @{
     400             :   /// Return array of the valence of each index (::UpLo)
     401             :   SPECTRE_ALWAYS_INLINE static constexpr std::array<UpLo, sizeof...(Indices)>
     402           1 :   index_valences() {
     403             :     return structure::index_valences();
     404             :   }
     405             :   /// @}
     406             : 
     407             :   /// @{
     408             :   /// Returns std::tuple of the ::Frame of each index
     409           1 :   SPECTRE_ALWAYS_INLINE static constexpr auto index_frames() {
     410             :     return Tensor_detail::Structure<Symm, Indices...>::index_frames();
     411             :   }
     412             :   /// @}
     413             : 
     414             :   /// @{
     415             :   /// \brief Given a tensor index, get the canonical label associated with the
     416             :   /// canonical \ref SpacetimeIndex "TensorIndexType"
     417             :   ///
     418             :   /// \param tensor_index The index of the tensor component to label
     419             :   /// \param axis_labels The labels for the indices. Defaults to "t", "x", "y"
     420             :   /// and "z" for spacetime indices and "x", "y" and "z" for spatial indices.
     421             :   /// Note that a tensor can have indices of different types, so we specify
     422             :   /// labels for each index individually.
     423             :   template <typename T = int>
     424           1 :   static std::string component_name(
     425             :       const std::array<T, rank()>& tensor_index = std::array<T, rank()>{},
     426             :       const std::array<std::string, rank()>& axis_labels =
     427             :           make_array<rank()>(std::string(""))) {
     428             :     return structure::component_name(tensor_index, axis_labels);
     429             :   }
     430             :   /// @}
     431             : 
     432             :   /// @{
     433             :   /// \brief Suffix to append to the tensor name that indicates the component
     434             :   ///
     435             :   /// The suffix is empty for scalars, otherwise it is an underscore followed by
     436             :   /// the `Tensor::component_name` of either the `tensor_index` or the canonical
     437             :   /// tensor index obtained from the `storage_index`. Use `axis_labels` to
     438             :   /// overwrite the default labels for each component (see
     439             :   /// `Tensor::component_name`).
     440             :   ///
     441             :   /// An example use case for the suffix is to label tensor components in
     442             :   /// data files.
     443             :   ///
     444             :   /// \see Tensor::component_name
     445             :   template <typename IndexType = int>
     446           1 :   static std::string component_suffix(
     447             :       const std::array<IndexType, rank()>& tensor_index =
     448             :           std::array<IndexType, rank()>{},
     449             :       const std::array<std::string, rank()>& axis_labels =
     450             :           make_array<rank()>(std::string(""))) {
     451             :     return rank() == 0 ? "" : "_" + component_name(tensor_index, axis_labels);
     452             :   }
     453             : 
     454           1 :   static std::string component_suffix(
     455             :       const size_t storage_index,
     456             :       const std::array<std::string, rank()>& axis_labels) {
     457             :     return component_suffix(get_tensor_index(storage_index), axis_labels);
     458             :   }
     459             : 
     460           1 :   static std::string component_suffix(size_t storage_index);
     461             :   /// @}
     462             : 
     463             :   /// Copy tensor data into an `std::vector<X>` along with the
     464             :   /// component names into a `std::vector<std::string>`
     465             :   /// \requires `std::is_same<X, DataVector>::%value` is true
     466           1 :   std::pair<std::vector<std::string>, std::vector<X>> get_vector_of_data()
     467             :       const;
     468             : 
     469             :   /// \cond HIDDEN_SYMBOLS
     470             :   /// Serialization function used by Charm++
     471             :   // NOLINTNEXTLINE(google-runtime-references)
     472             :   void pup(PUP::er& p) { p | data_; }
     473             :   /// \endcond
     474             : 
     475             :  private:
     476             :   // clang-tidy: redundant declaration
     477             :   /// \cond
     478             :   template <int I, class... Ts>
     479             :   friend SPECTRE_ALWAYS_INLINE constexpr size_t index_dim(  // NOLINT
     480             :       const Tensor<Ts...>& /*t*/);
     481             :   /// \endcond
     482             : 
     483           0 :   storage_type data_;
     484             : };
     485             : 
     486             : // ================================================================
     487             : // Template Definitions - Variadic templates must be in header
     488             : // ================================================================
     489             : 
     490             : template <typename X, typename Symm, template <typename...> class IndexList,
     491             :           typename... Indices>
     492             : // Implementation note: we explicitly prevent inlining and mark the function
     493             : // as used so that GDB's pretty printing facilities have access to this
     494             : // function.
     495           0 : __attribute__((noinline)) __attribute__((used)) std::string
     496             : Tensor<X, Symm, IndexList<Indices...>>::component_suffix(
     497             :     const size_t storage_index) {
     498             :   return component_suffix(get_tensor_index(storage_index),
     499             :                           make_array<rank()>(std::string("")));
     500             : }
     501             : 
     502             : template <typename X, typename Symm, template <typename...> class IndexList,
     503             :           typename... Indices>
     504             : Tensor<X, Symm, IndexList<Indices...>>::Tensor(storage_type data)
     505             :   requires(sizeof...(Indices) <= 1)
     506             :     : data_(std::move(data)) {}
     507             : 
     508             : template <typename X, typename Symm, template <typename...> class IndexList,
     509             :           typename... Indices>
     510             : template <typename Arg0, typename... Args>
     511             : Tensor<X, Symm, IndexList<Indices...>>::Tensor(Arg0&& arg0, Args&&... args)
     512             :     // NOLINTNEXTLINE(readability-simplify-boolean-expr)
     513             :   requires(not(sizeof...(Args) == 0 and
     514             :                std::same_as<std::decay_t<Arg0>, Tensor>) and
     515             :            std::constructible_from<X, Arg0, Args...>)
     516             :     : data_(make_array<size(), X>(std::forward<Arg0>(arg0),
     517             :                                   std::forward<Args>(args)...)) {}
     518             : 
     519             : template <typename X, typename Symm, template <typename...> class IndexList,
     520             :           typename... Indices>
     521             : std::pair<std::vector<std::string>, std::vector<X>>
     522             : Tensor<X, Symm, IndexList<Indices...>>::get_vector_of_data() const {
     523             :   std::vector<value_type> serialized_tensor(size());
     524             :   std::vector<std::string> component_names(size());
     525             :   for (size_t i = 0; i < data_.size(); ++i) {
     526             :     component_names[i] = component_name(get_tensor_index(i));
     527             :     serialized_tensor[i] = gsl::at(data_, i);
     528             :   }
     529             :   return std::make_pair(component_names, serialized_tensor);
     530             : }
     531             : 
     532             : template <int... N, typename... Args>
     533           0 : SPECTRE_ALWAYS_INLINE constexpr typename Tensor<Args...>::reference get(
     534             :     Tensor<Args...>& t) {
     535             :   static_assert(Tensor<Args...>::rank() == sizeof...(N),
     536             :                 "the number of tensor indices specified must match the rank "
     537             :                 "of the tensor");
     538             :   return gsl::at(
     539             :       t.data_, Tensor<Args...>::structure::template get_storage_index<N...>());
     540             : }
     541             : 
     542             : template <int... N, typename... Args>
     543           0 : SPECTRE_ALWAYS_INLINE constexpr typename Tensor<Args...>::const_reference get(
     544             :     const Tensor<Args...>& t) {
     545             :   static_assert(Tensor<Args...>::rank() == sizeof...(N),
     546             :                 "the number of tensor indices specified must match the rank "
     547             :                 "of the tensor");
     548             :   return gsl::at(
     549             :       t.data_, Tensor<Args...>::structure::template get_storage_index<N...>());
     550             : }
     551             : 
     552             : template <typename X, typename Symm, template <typename...> class IndexList,
     553             :           typename... Indices>
     554           0 : bool operator==(const Tensor<X, Symm, IndexList<Indices...>>& lhs,
     555             :                 const Tensor<X, Symm, IndexList<Indices...>>& rhs) {
     556             :   return std::equal(lhs.begin(), lhs.end(), rhs.begin());
     557             : }
     558             : template <typename X, typename Symm, template <typename...> class IndexList,
     559             :           typename... Indices>
     560           0 : bool operator!=(const Tensor<X, Symm, IndexList<Indices...>>& lhs,
     561             :                 const Tensor<X, Symm, IndexList<Indices...>>& rhs) {
     562             :   return not(lhs == rhs);
     563             : }
     564             : 
     565             : /// \ingroup TensorGroup
     566             : /// Get dimensionality of i'th tensor index
     567             : ///
     568             : /// \snippet Test_Tensor.cpp index_dim
     569             : template <int I, class... Ts>
     570           1 : SPECTRE_ALWAYS_INLINE constexpr size_t index_dim(const Tensor<Ts...>& /*t*/) {
     571             :   return Tensor<Ts...>::structure::template dim<I>();
     572             : }
     573             : 
     574             : // We place the stream operators in the header file so they do not need to be
     575             : // explicitly instantiated.
     576             : template <typename X, typename Symm, template <typename...> class IndexList,
     577             :           typename... Indices>
     578           0 : std::ostream& operator<<(std::ostream& os,
     579             :                          const Tensor<X, Symm, IndexList<Indices...>>& x) {
     580             :   static_assert(tt::is_streamable_v<decltype(os), X>,
     581             :                 "operator<< is not defined for the type you are trying to "
     582             :                 "stream in Tensor");
     583             :   for (size_t i = 0; i < x.size() - 1; ++i) {
     584             :     os << "T" << x.get_tensor_index(i) << "=" << x[i] << "\n";
     585             :   }
     586             :   size_t i = x.size() - 1;
     587             :   os << "T" << x.get_tensor_index(i) << "=" << x[i];
     588             :   return os;
     589             : }
     590             : 
     591             : namespace MakeWithValueImpls {
     592             : template <typename T, typename... Structure>
     593           0 : struct NumberOfPoints<Tensor<T, Structure...>> {
     594             :   static SPECTRE_ALWAYS_INLINE size_t
     595           0 :   apply(const Tensor<T, Structure...>& input) {
     596             :     return number_of_points(*input.begin());
     597             :   }
     598             : };
     599             : 
     600             : template <typename T, typename... Structure>
     601           0 : struct MakeWithSize<Tensor<T, Structure...>> {
     602             :   template <typename U>
     603           0 :   static SPECTRE_ALWAYS_INLINE Tensor<T, Structure...> apply(const size_t size,
     604             :                                                              const U value) {
     605             :     return Tensor<T, Structure...>(make_with_value<T>(size, value));
     606             :   }
     607             : };
     608             : 
     609             : template <typename... Structure, typename T>
     610           0 : struct MakeWithValueImpl<Tensor<double, Structure...>, T> {
     611           0 :   static SPECTRE_ALWAYS_INLINE Tensor<double, Structure...> apply(
     612             :       const T& /*input*/, const double value) {
     613             :     return Tensor<double, Structure...>(value);
     614             :   }
     615             : };
     616             : 
     617             : template <typename... Structure, typename T>
     618             : struct MakeWithValueImpl<Tensor<std::complex<double>, Structure...>, T> {
     619             :   static SPECTRE_ALWAYS_INLINE Tensor<std::complex<double>, Structure...> apply(
     620             :       const T& /*input*/, const std::complex<double> value) {
     621             :     return Tensor<std::complex<double>, Structure...>(value);
     622             :   }
     623             : };
     624             : }  // namespace MakeWithValueImpls
     625             : 
     626             : template <typename T, typename... Structure>
     627           0 : struct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<
     628             :     Tensor<T, Structure...>> {
     629           0 :   static constexpr bool is_trivial = SetNumberOfGridPointsImpl<T>::is_trivial;
     630           0 :   static SPECTRE_ALWAYS_INLINE void apply(
     631             :       const gsl::not_null<Tensor<T, Structure...>*> result, const size_t size) {
     632             :     for (auto& component : *result) {
     633             :       set_number_of_grid_points(make_not_null(&component), size);
     634             :     }
     635             :   }
     636             : };

Generated by: LCOV version 1.14