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 "Parallel/PupStlCpp11.hpp"
15 #include "PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp"
16 #include "Utilities/Literals.hpp"
18 #include "Utilities/Overloader.hpp"
19 #include "tests/Unit/Pypp/CheckWithRandomValues.hpp"
20 #include "tests/Unit/Pypp/SetupLocalPythonEnvironment.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 helper = [&](const std::unique_ptr<EoS>& eos) noexcept {
55  // need func variable to work around GCC bug
56  Function func{&EoS::pressure_from_density};
57  INFO("Testing pressure_from_density...")
58  pypp::check_with_random_values<1>(
59  func, *eos, "TestFunctions",
60  python_function_prefix + "_pressure_from_density", random_value_bounds,
61  std::make_tuple(member_args...), used_for_size);
62  INFO("Done\nTesting rest_mass_density_from_enthalpy...")
63  pypp::check_with_random_values<1>(
64  func = &EoS::rest_mass_density_from_enthalpy, *eos, "TestFunctions",
65  IsRelativistic ? std::string(python_function_prefix +
66  "_rel_rest_mass_density_from_enthalpy")
67  : std::string(python_function_prefix +
68  "_newt_rest_mass_density_from_enthalpy"),
69  {{{1, 1.0e4}}}, std::make_tuple(member_args...), used_for_size);
70  INFO("Done\nTesting specific_enthalpy_from_density...")
71  pypp::check_with_random_values<1>(
72  func = &EoS::specific_enthalpy_from_density, *eos, "TestFunctions",
73  IsRelativistic ? std::string(python_function_prefix +
74  "_rel_specific_enthalpy_from_density")
75  : std::string(python_function_prefix +
76  "_newt_specific_enthalpy_from_density"),
77  random_value_bounds, std::make_tuple(member_args...), used_for_size);
78  INFO("Done\nTesting specific_internal_energy_from_density...")
79  pypp::check_with_random_values<1>(
80  func = &EoS::specific_internal_energy_from_density, *eos,
81  "TestFunctions",
82  python_function_prefix + "_specific_internal_energy_from_density",
83  random_value_bounds, std::make_tuple(member_args...), used_for_size);
84  INFO("Done\nTesting chi_from_density...")
85  pypp::check_with_random_values<1>(
86  func = &EoS::chi_from_density, *eos, "TestFunctions",
87  python_function_prefix + "_chi_from_density", random_value_bounds,
88  std::make_tuple(member_args...), used_for_size);
89  INFO("Done\nTesting kappa_times_p_over_rho_squared_from_density...")
90  pypp::check_with_random_values<1>(
91  func = &EoS::kappa_times_p_over_rho_squared_from_density, *eos,
92  "TestFunctions",
93  python_function_prefix + "_kappa_times_p_over_rho_squared_from_density",
94  random_value_bounds, std::make_tuple(member_args...), used_for_size);
95  INFO(
96  "Done\nTesting that rest_mass_density_from_enthalpy and "
97  "specific_enthalpy_from_density are inverses of each other...")
98  MAKE_GENERATOR(generator);
99  std::uniform_real_distribution<> distribution(1.0, 1.0e+04);
101  make_not_null(&generator), make_not_null(&distribution), used_for_size);
104  eos->specific_enthalpy_from_density(
105  eos->rest_mass_density_from_enthalpy(specific_enthalpy)));
106  INFO("Done\n\n")
107  };
108  helper(in_eos);
109  helper(serialize_and_deserialize(in_eos));
110 }
111 
112 template <bool IsRelativistic, class... MemberArgs, class T>
113 void check_impl(
114  const std::unique_ptr<
115  ::EquationsOfState::EquationOfState<IsRelativistic, 2>>& in_eos,
116  const std::string& python_function_prefix, const T& used_for_size,
117  const MemberArgs&... member_args) noexcept {
118  // Bounds for: density, specific internal energy
119  const std::array<std::pair<double, double>, 2> random_value_bounds{
120  {{1.0e-4, 4.0}, {0.0, 1.0e4}}};
122  using Function = typename CreateMemberFunctionPointer<2>::template f<T, EoS>;
123  INFO("Testing "s + (IsRelativistic ? "relativistic"s : "Newtonian"s) +
124  " equation of state"s)
125  const auto helper = [&](const std::unique_ptr<EoS>& eos) noexcept {
126  // need func variable to work around GCC bug
127  Function func{&EoS::pressure_from_density_and_energy};
128  INFO("Testing pressure_from_density_and_energy...")
129  pypp::check_with_random_values<2>(
130  func, *eos, "TestFunctions",
131  python_function_prefix + "_pressure_from_density_and_energy",
132  random_value_bounds, std::make_tuple(member_args...), used_for_size);
133  INFO("Done\nTesting pressure_from_density_and_enthalpy...")
134  pypp::check_with_random_values<2>(
135  func = &EoS::pressure_from_density_and_enthalpy, *eos, "TestFunctions",
136  IsRelativistic
137  ? std::string(python_function_prefix +
138  "_rel_pressure_from_density_and_enthalpy")
139  : std::string(python_function_prefix +
140  "_newt_pressure_from_density_and_enthalpy"),
141  {{{1.0e-4, 4.0}, {1.0, 1.0e4}}}, std::make_tuple(member_args...),
142  used_for_size);
143  INFO("Done\nTesting specific_enthalpy_from_density_and_energy...")
144  pypp::check_with_random_values<2>(
145  func = &EoS::specific_enthalpy_from_density_and_energy, *eos,
146  "TestFunctions",
147  IsRelativistic
148  ? std::string(python_function_prefix +
149  "_rel_specific_enthalpy_from_density_and_energy")
150  : std::string(python_function_prefix +
151  "_newt_specific_enthalpy_from_density_and_energy"),
152  random_value_bounds, std::make_tuple(member_args...), used_for_size);
153  INFO("Done\nTesting specific_internal_energy_from_density_and_pressure...")
154  pypp::check_with_random_values<2>(
155  func = &EoS::specific_internal_energy_from_density_and_pressure, *eos,
156  "TestFunctions",
157  python_function_prefix +
158  "_specific_internal_energy_from_density_and_pressure",
159  random_value_bounds, std::make_tuple(member_args...), used_for_size);
160  INFO("Done\nTesting chi_from_density_and_energy...")
161  pypp::check_with_random_values<2>(
162  func = &EoS::chi_from_density_and_energy, *eos, "TestFunctions",
163  python_function_prefix + "_chi_from_density_and_energy",
164  random_value_bounds, std::make_tuple(member_args...), used_for_size);
165  INFO(
166  "Done\nTesting "
167  "kappa_times_p_over_rho_squared_from_density_and_energy...")
168  pypp::check_with_random_values<2>(
169  func = &EoS::kappa_times_p_over_rho_squared_from_density_and_energy,
170  *eos, "TestFunctions",
171  python_function_prefix +
172  "_kappa_times_p_over_rho_squared_from_density_and_energy",
173  random_value_bounds, std::make_tuple(member_args...), used_for_size);
174  INFO("Done\n\n")
175  };
176  helper(in_eos);
177  helper(serialize_and_deserialize(in_eos));
178 }
179 } // namespace detail
180 
181 // @{
182 /*!
183  * \ingroup TestingFrameworkGroup
184  * \brief Test an equation of state by comparing to python functions
185  *
186  * The python functions must be added to
187  * tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/TestFunctions.py. The
188  * prefix for each class of equation of state is arbitrary, but should generally
189  * be something like "polytropic" for polytropic fluids.
190  *
191  * The `python_function_prefix` argument passed to `check` must be `PREFIX`. If
192  * an EoS class has member variables (these must be `double`s currently) that
193  * are used to compute the quantities, such as the polytropic constant and
194  * polytropic exponent for a fluid, then they must be passed in as the last
195  * arguments to the `check` function`. Each python function must take these same
196  * arguments as the trailing arguments.
197  */
198 template <class EosType, class T, class... MemberArgs>
199 void check(std::unique_ptr<EosType> in_eos,
200  const std::string& python_function_prefix, const T& used_for_size,
201  const MemberArgs&... member_args) noexcept {
203  EosType::is_relativistic, EosType::thermodynamic_dim>>(
204  std::move(in_eos)),
205  python_function_prefix, used_for_size, member_args...);
206 }
207 
208 template <class EosType, class T, class... MemberArgs>
209 void check(EosType in_eos, const std::string& python_function_prefix,
210  const T& used_for_size, const MemberArgs&... member_args) noexcept {
212  EosType::is_relativistic, EosType::thermodynamic_dim>>(
213  std::make_unique<EosType>(std::move(in_eos))),
214  python_function_prefix, used_for_size, member_args...);
215 }
216 // @}
217 } // namespace EquationsOfState
218 } // namespace TestHelpers
Definition: VectorImplTestHelper.hpp:30
Base class for equations of state which need two independent thermodynamic variables in order to dete...
Definition: EquationOfState.hpp:171
Base class for equations of state depending on whether or not the system is relativistic, and the number of independent thermodynamic variables (ThermodynamicDim) needed to determine the pressure.
Definition: EquationOfState.hpp:63
Contains all functions for calling python from C++.
Definition: CheckWithRandomValues.hpp:59
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:160
Contains all equations of state, including base class.
Definition: DarkEnergyFluid.hpp:26
Scalar< DataType > specific_enthalpy(const Scalar< DataType > &rest_mass_density, const Scalar< DataType > &specific_internal_energy, const Scalar< DataType > &pressure) noexcept
Computes the relativistic specific enthalpy as: where is the specific internal energy...
#define MAKE_GENERATOR(...)
MAKE_GENERATOR(NAME [, SEED]) declares a variable of name NAME containing a generator of type std::mt...
Definition: TestHelpers.hpp:387
Definition: Determinant.hpp:11
PUP routines for new C+11 STL containers and other standard library objects Charm does not provide im...
Defines useful literals.
void check(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:209
Defines classes for Tensor.
#define CHECK_ITERABLE_APPROX(a, b)
A wrapper around Catch&#39;s CHECK macro that checks approximate equality of entries in iterable containe...
Definition: TestingFramework.hpp:110
Commonly used routines, functions and definitions shared amongst unit tests.
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, but it may be necessary to perform the conversion explicitly when type deduction is desired.
Definition: Gsl.hpp:863
Code to wrap or improve the Catch testing framework used for unit tests.
T serialize_and_deserialize(const T &t)
Serializes and deserializes an object t of type T
Definition: TestHelpers.hpp:42
Base class for equations of state which need one thermodynamic variable in order to determine the pre...
Definition: EquationOfState.hpp:74
Tensor< T, Symmetry<>, index_list<> > Scalar
Scalar type.
Definition: TypeAliases.hpp:21
Defines make_with_value.