TestHelpers.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
7 
8 #include <memory>
9 #include <pup.h>
10 #include <string>
11 #include <tuple>
12 #include <utility>
13 
15 #include "Framework/CheckWithRandomValues.hpp"
16 #include "Framework/SetupLocalPythonEnvironment.hpp"
19 #include "Parallel/PupStlCpp11.hpp"
21 #include "Utilities/Gsl.hpp"
22 #include "Utilities/Literals.hpp"
24 #include "Utilities/Overloader.hpp"
25 
26 namespace TestHelpers {
27 namespace MathFunctions {
28 namespace detail {
29 template <size_t VolumeDim, typename Fr, class... MemberArgs, class T>
30 void check_impl(
31  const std::unique_ptr<MathFunction<VolumeDim, Fr>>& in_math_function,
32  const std::string& python_function_prefix, const T& used_for_size,
33  const std::array<std::pair<double, double>, 1> random_value_bounds,
34  const MemberArgs&... member_args) noexcept {
35  using MathFunc = MathFunction<VolumeDim, Fr>;
36  using CallOperatorFunction =
37  Scalar<T> (MathFunc::*)(const tnsr::I<T, VolumeDim, Fr>&) const noexcept;
38  using FirstDerivFunction =
39  tnsr::i<T, VolumeDim, Fr> (MathFunc::*)(const tnsr::I<T, VolumeDim, Fr>&)
40  const noexcept;
41  using SecondDerivFunction =
42  tnsr::ii<T, VolumeDim, Fr> (MathFunc::*)(const tnsr::I<T, VolumeDim, Fr>&)
43  const noexcept;
44  using ThirdDerivFunction = tnsr::iii<T, VolumeDim, Fr> (MathFunc::*)(
45  const tnsr::I<T, VolumeDim, Fr>&) const noexcept;
46 
47  const auto member_args_tuple = std::make_tuple(member_args...);
48  const auto helper =
49  [&](const std::unique_ptr<MathFunc>& math_function) noexcept {
50  // need func variable to work around GCC bug
51  CallOperatorFunction func{&MathFunc::operator()};
52 
53  INFO("Testing call operator...")
54  pypp::check_with_random_values<1>(
55  func, *math_function, "TestFunctions",
56  python_function_prefix + "_call_operator", random_value_bounds,
57  member_args_tuple, used_for_size);
58  INFO("Done testing call operator...")
59 
60  FirstDerivFunction d_func{&MathFunc::first_deriv};
61  INFO("Testing first derivative...")
62  pypp::check_with_random_values<1>(
63  d_func, *math_function, "TestFunctions",
64  python_function_prefix + "_first_deriv", random_value_bounds,
65  member_args_tuple, used_for_size);
66  INFO("Done testing first derivative...")
67 
68  SecondDerivFunction d2_func{&MathFunc::second_deriv};
69  INFO("Testing second derivative...")
70  pypp::check_with_random_values<1>(
71  d2_func, *math_function, "TestFunctions",
72  python_function_prefix + "_second_deriv", random_value_bounds,
73  member_args_tuple, used_for_size);
74  INFO("Done testing second derivative...")
75 
76  ThirdDerivFunction d3_func{&MathFunc::third_deriv};
77  INFO("Testing third derivative...")
78  pypp::check_with_random_values<1>(
79  d3_func, *math_function, "TestFunctions",
80  python_function_prefix + "_third_deriv", random_value_bounds,
81  member_args_tuple, used_for_size);
82  INFO("Done testing third derivative...")
83 
84  INFO("Done\n\n")
85  };
86  helper(in_math_function);
87  helper(serialize_and_deserialize(in_math_function));
88 
89  if constexpr (VolumeDim == 1) {
90  // Check that the tensor interface agrees with the double/DataVector
91  // interface
92 
93  MAKE_GENERATOR(gen);
94  std::uniform_real_distribution<> real_dis(-1, 1);
95  const auto nn_generator = make_not_null(&gen);
96  const auto nn_distribution = make_not_null(&real_dis);
97 
98  auto coords_tensor = make_with_random_values<tnsr::I<T, VolumeDim, Fr>>(
99  nn_generator, nn_distribution, used_for_size);
100  T coords_T = get<0>(coords_tensor);
101 
102  CHECK_ITERABLE_APPROX(get((*in_math_function)(coords_tensor)),
103  (*in_math_function)(coords_T));
104 
105  const T deriv_from_tensor =
106  std::move(get<0>(in_math_function->first_deriv(coords_tensor)));
107  CHECK_ITERABLE_APPROX(deriv_from_tensor,
108  in_math_function->first_deriv(coords_T));
109 
110  const T second_deriv_from_tensor =
111  std::move(get<0, 0>(in_math_function->second_deriv(coords_tensor)));
112  CHECK_ITERABLE_APPROX(second_deriv_from_tensor,
113  in_math_function->second_deriv(coords_T));
114 
115  const T third_deriv_from_tensor =
116  std::move(get<0, 0, 0>(in_math_function->third_deriv(coords_tensor)));
117  CHECK_ITERABLE_APPROX(third_deriv_from_tensor,
118  in_math_function->third_deriv(coords_T));
119  }
120 }
121 } // namespace detail
122 // @{
123 /*!
124  * \ingroup TestingFrameworkGroup
125  * \brief Test a MathFunction by comparing to python functions
126  *
127  * The python functions must be added to
128  * tests/Unit/PointwiseFunctions/MathFunctions/Python/TestFunctions.py. The
129  * prefix for each class of MathFunction is arbitrary, but should generally
130  * be descriptive (e.g. 'gaussian', 'sinusoid', 'pow_x') of the MathFunction.
131  *
132  * The `python_function_prefix` argument passed to `check` must be `PREFIX`. If
133  * a MathFunction class has member variables set by its constructor, then these
134  * member variables must be passed in as the last arguments to the `check`
135  * function`. Each python function must take these same arguments as the
136  * trailing arguments.
137  */
138 template <class MathFunctionType, class T, class... MemberArgs>
140  const std::string& python_function_prefix, const T& used_for_size,
141  const std::array<std::pair<double, double>, 1> random_value_bounds,
142  const MemberArgs&... member_args) noexcept {
143  detail::check_impl(
144  std::unique_ptr<MathFunction<MathFunctionType::volume_dim,
145  typename MathFunctionType::frame>>(
146  std::move(in_math_function)),
147  python_function_prefix, used_for_size, random_value_bounds,
148  member_args...);
149 }
150 
151 template <class MathFunctionType, class T, class... MemberArgs>
152 void check(MathFunctionType in_math_function,
153  const std::string& python_function_prefix, const T& used_for_size,
154  const std::array<std::pair<double, double>, 1> random_value_bounds,
155  const MemberArgs&... member_args) noexcept {
156  detail::check_impl(
157  std::unique_ptr<MathFunction<MathFunctionType::volume_dim,
158  typename MathFunctionType::frame>>(
159  std::make_unique<MathFunctionType>(std::move(in_math_function))),
160  python_function_prefix, used_for_size, random_value_bounds,
161  member_args...);
162 }
163 // @}
164 } // namespace MathFunctions
165 } // namespace TestHelpers
std::string
utility
MathFunctions
Definition: Gaussian.cpp:16
get
constexpr Tag::type & get(Variables< TagList > &v) noexcept
Return Tag::type pointing into the contiguous array.
Definition: Variables.hpp:639
Literals.hpp
std::pair
TestingFramework.hpp
MakeWithRandomValues.hpp
tuple
pypp::call
R call(const std::string &module_name, const std::string &function_name, const Args &... t)
Calls a Python function from a module/file with given parameters.
Definition: Pypp.hpp:336
CHECK_ITERABLE_APPROX
#define CHECK_ITERABLE_APPROX(a, b)
A wrapper around Catch's CHECK macro that checks approximate equality of entries in iterable containe...
Definition: TestingFramework.hpp:139
MathFunction
Definition: MathFunction.hpp:32
std::uniform_real_distribution
TestHelpers.hpp
TestHelpers::MathFunctions::check
void check(std::unique_ptr< MathFunctionType > in_math_function, const std::string &python_function_prefix, const T &used_for_size, const std::array< std::pair< double, double >, 1 > random_value_bounds, const MemberArgs &... member_args) noexcept
Test a MathFunction by comparing to python functions.
Definition: TestHelpers.hpp:139
MakeWithValue.hpp
std::array
MathFunction.hpp
memory
serialize_and_deserialize
T serialize_and_deserialize(const T &t)
Serializes and deserializes an object t of type T
Definition: TestHelpers.hpp:45
MAKE_GENERATOR
#define MAKE_GENERATOR(...)
MAKE_GENERATOR(NAME [, SEED]) declares a variable of name NAME containing a generator of type std::mt...
Definition: TestHelpers.hpp:417
PupStlCpp11.hpp
Scalar
Tensor< T, Symmetry<>, index_list<> > Scalar
Definition: TypeAliases.hpp:21
Gsl.hpp
Tensor.hpp
pypp
Contains all functions for pypp.
Definition: CheckWithRandomValues.hpp:63
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
std::unique_ptr
string