SpECTRE Documentation Coverage Report
Current view: top level - DataStructures/Tensor/Expressions - TensorIndexTransformation.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 100.0 % 3 3
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            1 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : /// \file
       5              : /// Defines functions useful for transforming tensor multi-indices according to
       6              : /// a different generic index order
       7              : 
       8              : #pragma once
       9              : 
      10              : #include <array>
      11              : #include <cstddef>
      12              : #include <iterator>
      13              : #include <limits>
      14              : 
      15              : #include "DataStructures/Tensor/Expressions/TensorIndex.hpp"
      16              : #include "DataStructures/Tensor/Expressions/TimeIndex.hpp"
      17              : #include "Utilities/Algorithm.hpp"
      18              : #include "Utilities/ForceInline.hpp"
      19              : #include "Utilities/Gsl.hpp"
      20              : #include "Utilities/MakeArray.hpp"
      21              : 
      22              : namespace tenex {
      23              : namespace TensorIndexTransformation_detail {
      24              : static constexpr size_t time_index_position_placeholder =
      25              :     std::numeric_limits<size_t>::max();
      26              : }  // namespace TensorIndexTransformation_detail
      27              : 
      28              : /// \brief Computes a transformation from one generic tensor index order to
      29              : /// another
      30              : ///
      31              : /// \details
      32              : /// In most cases, the elements of the transformation are simply the positions
      33              : /// of the second list of generic indices in the first list of generic indices.
      34              : /// Put another way, for some `i`,
      35              : /// `tensorindices2[i] == tensorindices1[index_transformation[i]]`.
      36              : ///
      37              : /// Here is an example of what the algorithm does:
      38              : ///
      39              : /// Transformation between (1) \f$R_{cab}\f$ and (2) \f$S_{abc}\f$
      40              : /// `tensorindices1`:
      41              : /// \code
      42              : /// {2, 0, 1} // TensorIndex values for {c, a, b}
      43              : /// \endcode
      44              : /// `tensorindices2`:
      45              : /// \code
      46              : /// {0, 1, 2} // TensorIndex values for {a, b, c}
      47              : /// \endcode
      48              : /// returned `tensorindex_transformation`:
      49              : /// \code
      50              : /// {1, 2, 0} // positions of S' indices {a, b, c} in R's indices {c, a, b}
      51              : /// \endcode
      52              : ///
      53              : /// One special case scenario to note is when concrete time indices are
      54              : /// involved in the transformation. Consider transforming a multi-index for
      55              : /// some tensor \f$R_{ab}\f$ to another tensor \f$S_{btat}\f$. This would be
      56              : /// necessary for evaluating the LHS of a simple equation such as
      57              : /// \f$R_{ab} = S_{btat}\f$. The transformation between a multi-index for
      58              : /// \f$R\f$ to the equivalent multi-index for \f$S\f$ cannot simply be the list
      59              : /// of positions of \f$S\f$' indices in \f$R\f$'s indices, as \f$R\f$ does not
      60              : /// contain all of \f$S\f$' indices, because it has no time indices. To handle
      61              : /// cases like this, a placeholder value for the position of any time index is
      62              : /// substituted for an actual position, since one may not exist. In this
      63              : /// example, the transformation would be
      64              : /// `{1, PLACEHOLDER_VALUE, 0, PLACEHOLDER_VALUE}`, where `PLACEHOLDER_VALUE` is
      65              : /// defined by
      66              : /// `TensorIndexTransformation_detail::time_index_position_placeholder`. `1` and
      67              : /// `0` are the positions of \f$b\f$ and \f$a\f$ in \f$R\f$, and the placeholder
      68              : /// is used for the positions of time indices.
      69              : ///
      70              : /// \tparam NumIndices1 the number of indices for the first generic index order
      71              : /// \tparam NumIndices2 the number of indices for the second generic index order
      72              : /// \param tensorindices1 the TensorIndex values of the first generic index
      73              : /// order
      74              : /// \param tensorindices2 the TensorIndex values of the second generic index
      75              : /// order
      76              : /// \return a transformation from the first generic index order to the second
      77              : template <size_t NumIndices1, size_t NumIndices2>
      78              : SPECTRE_ALWAYS_INLINE constexpr std::array<size_t, NumIndices2>
      79            1 : compute_tensorindex_transformation(
      80              :     const std::array<size_t, NumIndices1>& tensorindices1,
      81              :     const std::array<size_t, NumIndices2>& tensorindices2) {
      82              :   std::array<size_t, NumIndices2> tensorindex_transformation{};
      83              :   for (size_t i = 0; i < NumIndices2; i++) {
      84              :     gsl::at(tensorindex_transformation, i) =
      85              :         detail::is_time_index_value(gsl::at(tensorindices2, i))
      86              :             ? TensorIndexTransformation_detail::time_index_position_placeholder
      87              :             : static_cast<size_t>(std::distance(
      88              :                   tensorindices1.begin(),
      89              :                   alg::find(tensorindices1, gsl::at(tensorindices2, i))));
      90              :   }
      91              :   return tensorindex_transformation;
      92              : }
      93              : 
      94              : /// \brief Computes the tensor multi-index that is equivalent to a given tensor
      95              : /// multi-index, according to the differences in their generic index orders
      96              : ///
      97              : /// \details
      98              : /// Here is an example of what the algorithm does:
      99              : ///
     100              : /// Transform (input) multi-index of \f$R_{cab}\f$ to the equivalent (output)
     101              : /// multi-index of \f$S_{abc}\f$
     102              : /// `tensorindex_transformation`:
     103              : /// \code
     104              : /// {1, 2, 0} // positions of S' indices {a, b, c} in R's indices {c, a, b}
     105              : /// \endcode
     106              : /// `input_multi_index`:
     107              : /// \code
     108              : /// {3, 4, 5} // i.e. c = 3, a = 4, b = 5
     109              : /// \endcode
     110              : /// returned equivalent `output_multi_index`:
     111              : /// \code
     112              : /// {4, 5, 3} // i.e. a = 4, b = 5, c = 3
     113              : /// \endcode
     114              : ///
     115              : /// One special case scenario to note is when concrete time indices are
     116              : /// involved in the transformation. Consider transforming a multi-index for
     117              : /// some tensor \f$R_{ab}\f$ to another tensor \f$S_{btat}\f$. This would be
     118              : /// necessary for evaluating the LHS of a simple equation such as
     119              : /// \f$R_{ab} = S_{btat}\f$. The transformation between a multi-index for
     120              : /// \f$R\f$ to the equivalent multi-index for \f$S\f$ cannot simply be the list
     121              : /// of positions of \f$S\f$' indices in \f$R\f$'s indices, as \f$R\f$ does not
     122              : /// contain all of \f$S\f$' indices, because it has no time indices. To handle
     123              : /// cases like this, a placeholder value for the position of any time index
     124              : /// must be substituted for an actual position, since one may not exist. In this
     125              : /// example, the proper input transformation (`tensorindex_transformation`)
     126              : /// would need to be
     127              : /// `{1, PLACEHOLDER_VALUE, 0, PLACEHOLDER_VALUE}`, where `PLACEHOLDER_VALUE` is
     128              : /// defined by
     129              : /// `TensorIndexTransformation_detail::time_index_position_placeholder`. `1` and
     130              : /// `0` are the positions of \f$b\f$ and \f$a\f$ in \f$R\f$, and the placeholder
     131              : /// is used for the positions of time indices. In computing the output
     132              : /// transformed multi-index, the function will insert a `0` at each position
     133              : /// where this placeholder is found in the transformation. For example, if
     134              : /// `input_multi_index` is `{1, 2}`, representing \f$R_{12}\f$, the returned
     135              : /// output multi-index will be `{2, 0, 1, 0}`, representing \f$S_{2010}\f$.
     136              : ///
     137              : /// \tparam NumIndicesIn the number of indices
     138              : /// \tparam NumIndicesOut the number of indices
     139              : /// \param input_multi_index the input tensor multi-index to transform
     140              : /// \param tensorindex_transformation the positions of the output's generic
     141              : /// indices in the input's generic indices (see example in details)
     142              : /// \return the output tensor multi-index that is equivalent to
     143              : /// `input_multi_index`, according to generic index order differences
     144              : // (`tensorindex_transformation`)
     145              : template <size_t NumIndicesIn, size_t NumIndicesOut>
     146              : SPECTRE_ALWAYS_INLINE constexpr std::array<size_t, NumIndicesOut>
     147            1 : transform_multi_index(
     148              :     const std::array<size_t, NumIndicesIn>& input_multi_index,
     149              :     const std::array<size_t, NumIndicesOut>& tensorindex_transformation) {
     150              :   std::array<size_t, NumIndicesOut> output_multi_index =
     151              :       make_array<NumIndicesOut, size_t>(0);
     152              :   for (size_t i = 0; i < NumIndicesOut; i++) {
     153              :     gsl::at(output_multi_index, i) =
     154              :         // Check that the index is not a time index instead of checking that it
     155              :         // is, because we expect it to not be a time index most of the time
     156              :         (gsl::at(tensorindex_transformation, i) !=
     157              :          TensorIndexTransformation_detail::time_index_position_placeholder)
     158              :             ? gsl::at(input_multi_index, gsl::at(tensorindex_transformation, i))
     159              :             : 0;
     160              :   }
     161              :   return output_multi_index;
     162              : }
     163              : }  // namespace tenex
        

Generated by: LCOV version 2.0-1