SpECTRE Documentation Coverage Report
Current view: top level - DataStructures/Tensor/Expressions - TensorIndexTransformation.hpp Hit Total Coverage
Commit: 37c384043430860f87787999aa7399d01bb3d213 Lines: 3 3 100.0 %
Date: 2024-04-20 02:24:02
Legend: Lines: hit not hit

          Line data    Source code
       1           1 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : /// \file
       5             : /// Defines 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 1.14