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 
14 #include "Framework/CheckWithRandomValues.hpp"
15 #include "Framework/SetupLocalPythonEnvironment.hpp"
17 #include "Parallel/PupStlCpp11.hpp"
18 #include "PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp"
19 #include "Utilities/Literals.hpp"
21 #include "Utilities/Overloader.hpp"
22 
23 namespace TestHelpers {
24 namespace EquationsOfState {
25 namespace detail {
26 template <size_t ThermodynamicDim,
28 struct CreateMemberFunctionPointer;
29 template <size_t ThermodynamicDim, size_t... Is>
30 struct CreateMemberFunctionPointer<ThermodynamicDim,
31  std::index_sequence<Is...>> {
32  template <class DataType, class EoS>
33  using f = Scalar<DataType> (EoS::*)(
34  const Scalar<std::remove_pointer_t<decltype(
35  (void)Is, std::add_pointer_t<DataType>{nullptr})>>&...) const;
36 };
37 
38 template <class T, typename EoS>
39 using Function = Scalar<T> (EoS::*)(const Scalar<T>&, const Scalar<T>&) const;
40 
41 template <bool IsRelativistic, class... MemberArgs, class T>
42 void check_impl(
43  const std::unique_ptr<
45  const std::string& python_function_prefix, const T& used_for_size,
46  const MemberArgs&... member_args) noexcept {
47  // Bounds for: density
48  const std::array<std::pair<double, double>, 1> random_value_bounds{
49  {{1.0e-4, 4.0}}};
51  using Function = typename CreateMemberFunctionPointer<1>::template f<T, EoS>;
52  INFO("Testing "s + (IsRelativistic ? "relativistic"s : "Newtonian"s) +
53  " equation of state"s)
54  const auto member_args_tuple = std::make_tuple(member_args...);
55  const auto helper = [&](const std::unique_ptr<EoS>& eos) noexcept {
56  // need func variable to work around GCC bug
57  Function func{&EoS::pressure_from_density};
58  INFO("Testing pressure_from_density...")
59  pypp::check_with_random_values<1>(
60  func, *eos, "TestFunctions",
61  python_function_prefix + "_pressure_from_density", random_value_bounds,
62  member_args_tuple, used_for_size);
63  INFO("Done\nTesting rest_mass_density_from_enthalpy...")
64  pypp::check_with_random_values<1>(
65  func = &EoS::rest_mass_density_from_enthalpy, *eos, "TestFunctions",
66  IsRelativistic ? std::string(python_function_prefix +
67  "_rel_rest_mass_density_from_enthalpy")
68  : std::string(python_function_prefix +
69  "_newt_rest_mass_density_from_enthalpy"),
70  {{{1, 1.0e4}}}, member_args_tuple, used_for_size);
71  INFO("Done\nTesting specific_enthalpy_from_density...")
72  pypp::check_with_random_values<1>(
73  func = &EoS::specific_enthalpy_from_density, *eos, "TestFunctions",
74  IsRelativistic ? std::string(python_function_prefix +
75  "_rel_specific_enthalpy_from_density")
76  : std::string(python_function_prefix +
77  "_newt_specific_enthalpy_from_density"),
78  random_value_bounds, member_args_tuple, used_for_size);
79  INFO("Done\nTesting specific_internal_energy_from_density...")
80  pypp::check_with_random_values<1>(
81  func = &EoS::specific_internal_energy_from_density, *eos,
82  "TestFunctions",
83  python_function_prefix + "_specific_internal_energy_from_density",
84  random_value_bounds, member_args_tuple, used_for_size);
85  INFO("Done\nTesting chi_from_density...")
86  pypp::check_with_random_values<1>(
87  func = &EoS::chi_from_density, *eos, "TestFunctions",
88  python_function_prefix + "_chi_from_density", random_value_bounds,
89  member_args_tuple, used_for_size);
90  INFO("Done\nTesting kappa_times_p_over_rho_squared_from_density...")
91  pypp::check_with_random_values<1>(
92  func = &EoS::kappa_times_p_over_rho_squared_from_density, *eos,
93  "TestFunctions",
94  python_function_prefix + "_kappa_times_p_over_rho_squared_from_density",
95  random_value_bounds, member_args_tuple, used_for_size);
96  INFO(
97  "Done\nTesting that rest_mass_density_from_enthalpy and "
98  "specific_enthalpy_from_density are inverses of each other...")
99  MAKE_GENERATOR(generator);
100  std::uniform_real_distribution<> distribution(1.0, 1.0e+04);
101  const auto specific_enthalpy = make_with_random_values<Scalar<T>>(
102  make_not_null(&generator), make_not_null(&distribution), used_for_size);
104  specific_enthalpy,
105  eos->specific_enthalpy_from_density(
106  eos->rest_mass_density_from_enthalpy(specific_enthalpy)));
107  INFO("Done\n\n")
108  };
109  helper(in_eos);
110  helper(serialize_and_deserialize(in_eos));
111 }
112 
113 template <bool IsRelativistic, class... MemberArgs, class T>
114 void check_impl(
115  const std::unique_ptr<
116  ::EquationsOfState::EquationOfState<IsRelativistic, 2>>& in_eos,
117  const std::string& python_function_prefix, const T& used_for_size,
118  const MemberArgs&... member_args) noexcept {
119  // Bounds for: density, specific internal energy
120  const std::array<std::pair<double, double>, 2> random_value_bounds{
121  {{1.0e-4, 4.0}, {0.0, 1.0e4}}};
123  using Function = typename CreateMemberFunctionPointer<2>::template f<T, EoS>;
124  INFO("Testing "s + (IsRelativistic ? "relativistic"s : "Newtonian"s) +
125  " equation of state"s)
126  const auto member_args_tuple = std::make_tuple(member_args...);
127  const auto helper = [&](const std::unique_ptr<EoS>& eos) noexcept {
128  // need func variable to work around GCC bug
129  Function func{&EoS::pressure_from_density_and_energy};
130  INFO("Testing pressure_from_density_and_energy...")
131  pypp::check_with_random_values<2>(
132  func, *eos, "TestFunctions",
133  python_function_prefix + "_pressure_from_density_and_energy",
134  random_value_bounds, member_args_tuple, used_for_size);
135  INFO("Done\nTesting pressure_from_density_and_enthalpy...")
136  pypp::check_with_random_values<2>(
137  func = &EoS::pressure_from_density_and_enthalpy, *eos, "TestFunctions",
138  IsRelativistic
139  ? std::string(python_function_prefix +
140  "_rel_pressure_from_density_and_enthalpy")
141  : std::string(python_function_prefix +
142  "_newt_pressure_from_density_and_enthalpy"),
143  {{{1.0e-4, 4.0}, {1.0, 1.0e4}}}, member_args_tuple, used_for_size);
144  INFO("Done\nTesting specific_enthalpy_from_density_and_energy...")
145  pypp::check_with_random_values<2>(
146  func = &EoS::specific_enthalpy_from_density_and_energy, *eos,
147  "TestFunctions",
148  IsRelativistic
149  ? std::string(python_function_prefix +
150  "_rel_specific_enthalpy_from_density_and_energy")
151  : std::string(python_function_prefix +
152  "_newt_specific_enthalpy_from_density_and_energy"),
153  random_value_bounds, member_args_tuple, used_for_size);
154  INFO("Done\nTesting specific_internal_energy_from_density_and_pressure...")
155  pypp::check_with_random_values<2>(
156  func = &EoS::specific_internal_energy_from_density_and_pressure, *eos,
157  "TestFunctions",
158  python_function_prefix +
159  "_specific_internal_energy_from_density_and_pressure",
160  random_value_bounds, member_args_tuple, used_for_size);
161  INFO("Done\nTesting chi_from_density_and_energy...")
162  pypp::check_with_random_values<2>(
163  func = &EoS::chi_from_density_and_energy, *eos, "TestFunctions",
164  python_function_prefix + "_chi_from_density_and_energy",
165  random_value_bounds, member_args_tuple, used_for_size);
166  INFO(
167  "Done\nTesting "
168  "kappa_times_p_over_rho_squared_from_density_and_energy...")
169  pypp::check_with_random_values<2>(
170  func = &EoS::kappa_times_p_over_rho_squared_from_density_and_energy,
171  *eos, "TestFunctions",
172  python_function_prefix +
173  "_kappa_times_p_over_rho_squared_from_density_and_energy",
174  random_value_bounds, member_args_tuple, used_for_size);
175  INFO("Done\n\n")
176  };
177  helper(in_eos);
178  helper(serialize_and_deserialize(in_eos));
179 }
180 } // namespace detail
181 
182 // @{
183 /*!
184  * \ingroup TestingFrameworkGroup
185  * \brief Test an equation of state by comparing to python functions
186  *
187  * The python functions must be added to
188  * tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/TestFunctions.py. The
189  * prefix for each class of equation of state is arbitrary, but should generally
190  * be something like "polytropic" for polytropic fluids.
191  *
192  * The `python_function_prefix` argument passed to `check` must be `PREFIX`. If
193  * an EoS class has member variables (these must be `double`s currently) that
194  * are used to compute the quantities, such as the polytropic constant and
195  * polytropic exponent for a fluid, then they must be passed in as the last
196  * arguments to the `check` function`. Each python function must take these same
197  * arguments as the trailing arguments.
198  */
199 template <class EosType, class T, class... MemberArgs>
200 void check(std::unique_ptr<EosType> in_eos,
201  const std::string& python_function_prefix, const T& used_for_size,
202  const MemberArgs&... member_args) noexcept {
204  EosType::is_relativistic, EosType::thermodynamic_dim>>(
205  std::move(in_eos)),
206  python_function_prefix, used_for_size, member_args...);
207 }
208 
209 template <class EosType, class T, class... MemberArgs>
210 void check(EosType in_eos, const std::string& python_function_prefix,
211  const T& used_for_size, const MemberArgs&... member_args) noexcept {
213  EosType::is_relativistic, EosType::thermodynamic_dim>>(
214  std::make_unique<EosType>(std::move(in_eos))),
215  python_function_prefix, used_for_size, member_args...);
216 }
217 // @}
218 } // namespace EquationsOfState
219 } // namespace TestHelpers
EquationsOfState
Contains all equations of state, including base class.
Definition: DarkEnergyFluid.hpp:26
std::string
EquationsOfState::EquationOfState
Base class for equations of state depending on whether or not the system is relativistic,...
Definition: EquationOfState.hpp:63
Literals.hpp
std::make_index_sequence
TestingFramework.hpp
tuple
TestHelpers::EquationsOfState::check
void check(std::unique_ptr< EosType > in_eos, const std::string &python_function_prefix, const T &used_for_size, const MemberArgs &... member_args) noexcept
Test an equation of state by comparing to python functions.
Definition: TestHelpers.hpp:200
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
TestHelpers.hpp
EquationsOfState::EquationOfState< IsRelativistic, 1 >
Base class for equations of state which need one thermodynamic variable in order to determine the pre...
Definition: EquationOfState.hpp:74
std::add_pointer_t
MakeWithValue.hpp
std::array
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
std::remove_pointer_t
Scalar
Tensor< T, Symmetry<>, index_list<> > Scalar
Definition: TypeAliases.hpp:21
make_with_random_values
ReturnType make_with_random_values(const gsl::not_null< UniformRandomBitGenerator * > generator, const gsl::not_null< RandomNumberDistribution * > distribution, const T &used_for_size) noexcept
Make a data structure and fill it with random values.
Definition: MakeWithRandomValues.hpp:164
EquationsOfState::EquationOfState< IsRelativistic, 2 >
Base class for equations of state which need two independent thermodynamic variables in order to dete...
Definition: EquationOfState.hpp:171
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