TestHelpers.hpp
1 // Distributed under the MIT License.
2 // See LICENSE.txt for details.
3 
4 #pragma once
5 
7 
8 #include <algorithm>
9 #include <array>
10 #include <limits>
11 #include <memory>
12 #include <pup.h>
13 #include <string>
14 #include <tuple>
15 #include <utility>
16 #include <vector>
17 
18 #include "DataStructures/DataVector.hpp"
20 #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
21 #include "Domain/FunctionsOfTime/PiecewisePolynomial.hpp"
22 #include "Evolution/Systems/GeneralizedHarmonic/ConstraintDamping/DampingFunction.hpp"
23 #include "Framework/CheckWithRandomValues.hpp"
24 #include "Framework/SetupLocalPythonEnvironment.hpp"
27 #include "Parallel/PupStlCpp11.hpp"
28 #include "Utilities/Gsl.hpp"
29 #include "Utilities/Literals.hpp"
31 #include "Utilities/Overloader.hpp"
32 
33 namespace TestHelpers::GeneralizedHarmonic::ConstraintDamping {
34 namespace detail {
35 template <size_t VolumeDim, typename Fr, class... MemberArgs, class T>
36 void check_impl(
37  const std::unique_ptr<
39  VolumeDim, Fr>>& in_gh_damping_function,
40  const std::string& python_function_prefix, const T& used_for_size,
41  const std::array<std::pair<double, double>, 1> random_value_bounds,
42  const std::vector<std::string>& function_of_time_names,
43  const MemberArgs&... member_args) noexcept {
44  using GhDampingFunc =
46 
47  const auto member_args_tuple = std::make_tuple(member_args...);
48  const auto helper =
49  [&python_function_prefix, &random_value_bounds, &member_args_tuple,
50  &function_of_time_names, &used_for_size](
51  const std::unique_ptr<GhDampingFunc>& gh_damping_function) noexcept {
52  INFO("Testing call operator...")
53  // Make a lambda that calls the damping function's call operator
54  // with a hard-coded FunctionsOfTime, since check_with_random_values
55  // cannot convert a FunctionsOfTime into a python type.
56  // The FunctionsOfTime contains a single FunctionOfTime
57  // \f$f(t) = a_0 + a_1 (t-t_0) + a_2 (t-t_0)^2 + a_3 (t-t_0)^3\f$, where
58  // \f$a_0 = 1.0\f$, \f$a_1 = 0.2\f$, \f$a_2 = 0.03,\f$,
59  // \f$a_3 = 0.004\f$, and \f$t_0\f$ is the smallest possible value
60  // of the randomly selected time.
61  //
62  // The corresponding python function should use
63  // the same hard-coded coefficients to evaluate \f$f(t)\f$ as well
64  // as the same value of \f$t_0\f$.
65  // However, here the PiecewisePolynomial must be initialized not
66  // with the polynomial coefficients but with the values of \f$f(t)\f$
67  // and its derivatives evaluated at \f$t=t_0\f$: these are,
68  // respectively, \f$a_0,a_1,2 a_2,6 a_3\f$.
69  //
70  // Finally, note that the FunctionOfTime never expires.
71  const auto damping_function_call_operator_helper =
72  [&gh_damping_function, &random_value_bounds,
73  &function_of_time_names](
74  const tnsr::I<T, VolumeDim, Fr>& coordinates,
75  const double time) {
79  functions_of_time{};
80  for (auto function_of_time_name : function_of_time_names) {
81  // The randomly selected time will be between the
82  // random_value_bounds, so set the earliest time of the
83  // function_of_times to the lower bound in random_value_bounds.
84  functions_of_time[function_of_time_name] = std::make_unique<
86  std::min(gsl::at(random_value_bounds, 0).first,
87  gsl::at(random_value_bounds, 0).second),
88  std::array<DataVector, 4>{{{1.0}, {0.2}, {0.06}, {0.024}}},
90  }
91  // Default-construct the scalar, to test that the damping
92  // function's call operator correctly resizes it
93  // (in the case T is a DataVector) with
94  // destructive_resize_components()
95  Scalar<T> value_at_coordinates{};
96  gh_damping_function->operator()(
97  make_not_null(&value_at_coordinates), coordinates, time,
98  functions_of_time);
99  return value_at_coordinates;
100  };
101 
102  pypp::check_with_random_values<1>(
103  &decltype(damping_function_call_operator_helper)::operator(),
104  damping_function_call_operator_helper, "TestFunctions",
105  python_function_prefix + "_call_operator", random_value_bounds,
106  member_args_tuple, used_for_size);
107  INFO("Done testing call operator...")
108  INFO("Done\n\n")
109  };
110 
111  helper(in_gh_damping_function);
112  helper(serialize_and_deserialize(in_gh_damping_function));
113 }
114 } // namespace detail
115 /// @{
116 /*!
117  * \ingroup TestingFrameworkGroup
118  * \brief Test a DampingFunction by comparing to python functions
119  *
120  * The python functions must be added to TestFunctions.py in
121  * tests/Unit/Evolution/Systems/GeneralizedHarmonic/ConstraintDamping/Python.
122  * Each python function for a corresponding DampingFunction should begin
123  * with a prefix `python_function_prefix`. The prefix for each class of
124  * DampingFunction is arbitrary, but should generally be descriptive (e.g.
125  * 'gaussian_plus_constant') of the DampingFunction.
126  *
127  * The input parameter `function_of_time_name` is the name of the FunctionOfTime
128  * that will be included in the FunctionsOfTime passed to the DampingFunction's
129  * call operator. For time-dependent DampingFunctions, this parameter must be
130  * consistent with the FunctionOfTime name that the call operator of
131  * `in_gh_damping_function` expects. For time-independent DampingFunctions,
132  * `function_of_time_name` will be ignored.
133  *
134  * If a DampingFunction class has member variables set by its constructor, then
135  * these member variables must be passed in as the last arguments to the `check`
136  * function`. Each python function must take these same arguments as the
137  * trailing arguments.
138  */
139 template <class DampingFunctionType, class T, class... MemberArgs>
140 void check(std::unique_ptr<DampingFunctionType> in_gh_damping_function,
141  const std::string& python_function_prefix, const T& used_for_size,
142  const std::array<std::pair<double, double>, 1>& random_value_bounds,
143  const std::vector<std::string>& function_of_time_names,
144  const MemberArgs&... member_args) noexcept {
145  detail::check_impl(
147  DampingFunctionType::volume_dim,
148  typename DampingFunctionType::frame>>(
149  std::move(in_gh_damping_function)),
150  python_function_prefix, used_for_size, random_value_bounds,
151  function_of_time_names, member_args...);
152 }
153 
154 template <class DampingFunctionType, class T, class... MemberArgs>
155 void check(DampingFunctionType in_gh_damping_function,
156  const std::string& python_function_prefix, const T& used_for_size,
157  const std::array<std::pair<double, double>, 1>& random_value_bounds,
158  const std::vector<std::string>& function_of_time_names,
159  const MemberArgs&... member_args) noexcept {
160  detail::check_impl(
162  DampingFunctionType::volume_dim,
163  typename DampingFunctionType::frame>>(
164  std::make_unique<DampingFunctionType>(
165  std::move(in_gh_damping_function))),
166  python_function_prefix, used_for_size, random_value_bounds,
167  function_of_time_names, member_args...);
168 }
169 /// @}
170 } // namespace TestHelpers::GeneralizedHarmonic::ConstraintDamping
gsl::at
constexpr T & at(std::array< T, N > &arr, Size index)
Retrieve a entry from a container, with checks in Debug mode that the index being retrieved is valid.
Definition: Gsl.hpp:125
std::string
utility
Literals.hpp
std::pair< double, double >
domain::FunctionsOfTime::PiecewisePolynomial
A function that has a piecewise-constant MaxDerivth derivative.
Definition: PiecewisePolynomial.hpp:22
vector
TestingFramework.hpp
MakeWithRandomValues.hpp
tuple
algorithm
TestHelpers.hpp
MakeWithValue.hpp
array
TestHelpers::MathFunctions::check
void check(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:152
memory
serialize_and_deserialize
T serialize_and_deserialize(const T &t)
Serializes and deserializes an object t of type T
Definition: TestHelpers.hpp:45
PupStlCpp11.hpp
tnsr
Type aliases to construct common Tensors.
Definition: TypeAliases.hpp:31
Scalar
Tensor< T, Symmetry<>, index_list<> > Scalar
Definition: TypeAliases.hpp:21
limits
Gsl.hpp
GeneralizedHarmonic::ConstraintDamping::DampingFunction
Base class defining interface for constraint damping functions.
Definition: DampingFunction.hpp:39
Tensor.hpp
std::time
T time(T... args)
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::numeric_limits::max
T max(T... args)
std::unique_ptr
std::unordered_map
string