EvaluateRank2TestHelpers.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
6 #include <cstddef>
7 #include <iterator>
8 #include <numeric>
9 #include <type_traits>
10 
11 #include "DataStructures/Tags/TempTensor.hpp"
16 #include "Utilities/GenerateInstantiations.hpp"
17 #include "Utilities/Gsl.hpp"
18 #include "Utilities/TMPL.hpp"
19 
20 namespace TestHelpers::TensorExpressions {
21 
22 /// \ingroup TestingFrameworkGroup
23 /// \brief Test that evaluating a right hand side tensor expression containing a
24 /// single rank 2 tensor correctly assigns the data to the evaluated left hand
25 /// side tensor
26 ///
27 /// \details `TensorIndexA` and `TensorIndexB` can be any type of TensorIndex
28 /// and are not necessarily `ti_a` and `ti_b`. The "A" and "B" suffixes just
29 /// denote the ordering of the generic indices of the RHS tensor expression. In
30 /// the RHS tensor expression, it means `TensorIndexA` is the first index used
31 /// and `TensorIndexB` is the second index used.
32 ///
33 /// If we consider the RHS tensor's generic indices to be (a, b), then this test
34 /// checks that the data in the evaluated LHS tensor is correct according to the
35 /// index orders of the LHS and RHS. The two possible cases that are checked are
36 /// when the LHS tensor is evaluated with index order (a, b) and when it is
37 /// evaluated with the index order (b, a).
38 ///
39 /// \tparam DataType the type of data being stored in the Tensors
40 /// \tparam RhsSymmetry the ::Symmetry of the RHS Tensor
41 /// \tparam RhsTensorIndexTypeList the RHS Tensor's typelist of
42 /// \ref SpacetimeIndex "TensorIndexType"s
43 /// \tparam TensorIndexA the first TensorIndex used on the RHS of the
44 /// TensorExpression, e.g. `ti_a`
45 /// \tparam TensorIndexB the second TensorIndex used on the RHS of the
46 /// TensorExpression, e.g. `ti_B`
47 template <typename DataType, typename RhsSymmetry,
48  typename RhsTensorIndexTypeList, auto& TensorIndexA,
49  auto& TensorIndexB>
50 void test_evaluate_rank_2_impl() noexcept {
51  const size_t used_for_size = 5;
52  Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList> R_ab(used_for_size);
53  std::iota(R_ab.begin(), R_ab.end(), 0.0);
54 
55  // L_{ab} = R_{ab}
56  // Use explicit type (vs auto) so the compiler checks the return type of
57  // `evaluate`
58  using L_ab_type = decltype(R_ab);
59  const L_ab_type L_ab_returned =
60  ::TensorExpressions::evaluate<TensorIndexA, TensorIndexB>(
61  R_ab(TensorIndexA, TensorIndexB));
62  L_ab_type L_ab_filled{};
63  ::TensorExpressions::evaluate<TensorIndexA, TensorIndexB>(
64  make_not_null(&L_ab_filled), R_ab(TensorIndexA, TensorIndexB));
65 
66  // L_{ba} = R_{ab}
67  using L_ba_tensorindextype_list =
68  tmpl::list<tmpl::at_c<RhsTensorIndexTypeList, 1>,
69  tmpl::at_c<RhsTensorIndexTypeList, 0>>;
70  using L_ba_type = Tensor<DataType, RhsSymmetry, L_ba_tensorindextype_list>;
71  const L_ba_type L_ba_returned =
72  ::TensorExpressions::evaluate<TensorIndexB, TensorIndexA>(
73  R_ab(TensorIndexA, TensorIndexB));
74  L_ba_type L_ba_filled{};
75  ::TensorExpressions::evaluate<TensorIndexB, TensorIndexA>(
76  make_not_null(&L_ba_filled), R_ab(TensorIndexA, TensorIndexB));
77 
78  const size_t dim_a = tmpl::at_c<RhsTensorIndexTypeList, 0>::dim;
79  const size_t dim_b = tmpl::at_c<RhsTensorIndexTypeList, 1>::dim;
80 
81  for (size_t i = 0; i < dim_a; ++i) {
82  for (size_t j = 0; j < dim_b; ++j) {
83  // For L_{ab} = R_{ab}, check that L_{ij} == R_{ij}
84  CHECK(L_ab_returned.get(i, j) == R_ab.get(i, j));
85  CHECK(L_ab_filled.get(i, j) == R_ab.get(i, j));
86  // For L_{ba} = R_{ab}, check that L_{ji} == R_{ij}
87  CHECK(L_ba_returned.get(j, i) == R_ab.get(i, j));
88  CHECK(L_ba_filled.get(j, i) == R_ab.get(i, j));
89  }
90  }
91 
92  // Test with TempTensor for LHS tensor
93  if constexpr (not std::is_same_v<DataType, double>) {
94  // L_{ab} = R_{ab}
95  Variables<tmpl::list<::Tags::TempTensor<1, L_ab_type>>> L_ab_var{
96  used_for_size};
97  L_ab_type& L_ab_temp = get<::Tags::TempTensor<1, L_ab_type>>(L_ab_var);
98  ::TensorExpressions::evaluate<TensorIndexA, TensorIndexB>(
99  make_not_null(&L_ab_temp), R_ab(TensorIndexA, TensorIndexB));
100 
101  // L_{ba} = R_{ab}
102  Variables<tmpl::list<::Tags::TempTensor<1, L_ba_type>>> L_ba_var{
103  used_for_size};
104  L_ba_type& L_ba_temp = get<::Tags::TempTensor<1, L_ba_type>>(L_ba_var);
105  ::TensorExpressions::evaluate<TensorIndexB, TensorIndexA>(
106  make_not_null(&L_ba_temp), R_ab(TensorIndexA, TensorIndexB));
107 
108  for (size_t i = 0; i < dim_a; ++i) {
109  for (size_t j = 0; j < dim_b; ++j) {
110  // For L_{ab} = R_{ab}, check that L_{ij} == R_{ij}
111  CHECK(L_ab_temp.get(i, j) == R_ab.get(i, j));
112  // For L_{ba} = R_{ab}, check that L_{ji} == R_{ij}
113  CHECK(L_ba_temp.get(j, i) == R_ab.get(i, j));
114  }
115  }
116  }
117 }
118 
119 /// \ingroup TestingFrameworkGroup
120 /// \brief Iterate testing of evaluating single rank 2 Tensors on multiple Frame
121 /// types and dimension combinations
122 ///
123 /// We test nonsymmetric indices and symmetric indices across two functions to
124 /// ensure that the code works correctly with symmetries. This function tests
125 /// one of the following symmetries:
126 /// - <2, 1> (`test_evaluate_rank_2_no_symmetry`)
127 /// - <1, 1> (`test_evaluate_rank_2_symmetric`)
128 ///
129 /// \details `TensorIndexA` and `TensorIndexB` can be any type of TensorIndex
130 /// and are not necessarily `ti_a` and `ti_b`. The "A" and "B" suffixes just
131 /// denote the ordering of the generic indices of the RHS tensor expression. In
132 /// the RHS tensor expression, it means `TensorIndexA` is the first index used
133 /// and `TensorIndexB` is the second index used.
134 ///
135 /// Note: `test_evaluate_rank_2_symmetric` has fewer template parameters due to
136 /// the two indices having a shared \ref SpacetimeIndex "TensorIndexType" and
137 /// and valence
138 ///
139 /// \tparam DataType the type of data being stored in the Tensors
140 /// \tparam TensorIndexTypeA the \ref SpacetimeIndex "TensorIndexType" of the
141 /// first index of the RHS Tensor
142 /// \tparam TensorIndexTypeB the \ref SpacetimeIndex "TensorIndexType" of the
143 /// second index of the RHS Tensor
144 /// \tparam ValenceA the valence of the first index used on the RHS of the
145 /// TensorExpression
146 /// \tparam ValenceB the valence of the second index used on the RHS of the
147 /// TensorExpression
148 /// \tparam TensorIndexA the first TensorIndex used on the RHS of the
149 /// TensorExpression, e.g. `ti_a`
150 /// \tparam TensorIndexB the second TensorIndex used on the RHS of the
151 /// TensorExpression, e.g. `ti_B`
152 template <typename DataType,
153  template <size_t, UpLo, typename> class TensorIndexTypeA,
154  template <size_t, UpLo, typename> class TensorIndexTypeB,
155  UpLo ValenceA, UpLo ValenceB, auto& TensorIndexA, auto& TensorIndexB>
157 #define DIM_A(data) BOOST_PP_TUPLE_ELEM(0, data)
158 #define DIM_B(data) BOOST_PP_TUPLE_ELEM(1, data)
159 #define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)
160 
161 #define CALL_TEST_EVALUATE_RANK_2_IMPL(_, data) \
162  test_evaluate_rank_2_impl< \
163  DataType, Symmetry<2, 1>, \
164  index_list<TensorIndexTypeA<DIM_A(data), ValenceA, FRAME(data)>, \
165  TensorIndexTypeB<DIM_B(data), ValenceB, FRAME(data)>>, \
166  TensorIndexA, TensorIndexB>();
167 
168  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_2_IMPL, (1, 2, 3), (1, 2, 3),
170 
171 #undef CALL_TEST_EVALUATE_RANK_2_IMPL
172 #undef FRAME
173 #undef DIM_B
174 #undef DIM_A
175 }
176 
177 /// \ingroup TestingFrameworkGroup
178 /// \copydoc test_evaluate_rank_2_no_symmetry()
179 template <typename DataType,
180  template <size_t, UpLo, typename> class TensorIndexType, UpLo Valence,
181  auto& TensorIndexA, auto& TensorIndexB>
183 #define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)
184 #define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)
185 
186 #define CALL_TEST_EVALUATE_RANK_2_IMPL(_, data) \
187  test_evaluate_rank_2_impl< \
188  DataType, Symmetry<1, 1>, \
189  index_list<TensorIndexType<DIM(data), Valence, FRAME(data)>, \
190  TensorIndexType<DIM(data), Valence, FRAME(data)>>, \
191  TensorIndexA, TensorIndexB>();
192 
193  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_2_IMPL, (1, 2, 3),
195 
196 #undef CALL_TEST_EVALUATE_RANK_2_IMPL
197 #undef FRAME
198 #undef DIM
199 }
200 
201 } // namespace TestHelpers::TensorExpressions
Frame::Inertial
Definition: IndexType.hpp:44
TensorExpression.hpp
UpLo
UpLo
Definition: IndexType.hpp:20
Frame::Grid
Definition: IndexType.hpp:43
iterator
TestHelpers::TensorExpressions::test_evaluate_rank_2_no_symmetry
void test_evaluate_rank_2_no_symmetry() noexcept
Iterate testing of evaluating single rank 2 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank2TestHelpers.hpp:156
TestHelpers::TensorExpressions::test_evaluate_rank_2_impl
void test_evaluate_rank_2_impl() noexcept
Test that evaluating a right hand side tensor expression containing a single rank 2 tensor correctly ...
Definition: EvaluateRank2TestHelpers.hpp:50
cstddef
Variables.hpp
Gsl.hpp
Tensor.hpp
make_not_null
gsl::not_null< T * > make_not_null(T *ptr) noexcept
Construct a not_null from a pointer. Often this will be done as an implicit conversion,...
Definition: Gsl.hpp:880
numeric
TestHelpers::TensorExpressions::test_evaluate_rank_2_symmetric
void test_evaluate_rank_2_symmetric() noexcept
Iterate testing of evaluating single rank 2 Tensors on multiple Frame types and dimension combination...
Definition: EvaluateRank2TestHelpers.hpp:182
type_traits
GENERATE_INSTANTIATIONS
#define GENERATE_INSTANTIATIONS(INSTANTIATION_MACRO,...)
Macro useful for generating many explicit instantiations of function or class templates.
Definition: GenerateInstantiations.hpp:160
TMPL.hpp
Evaluate.hpp
cpp2b::iota
constexpr void iota(ForwardIterator first, ForwardIterator last, T value)
Definition: Numeric.hpp:20