Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <boost/preprocessor/arithmetic/dec.hpp> 7 : #include <boost/preprocessor/arithmetic/inc.hpp> 8 : #include <boost/preprocessor/control/expr_iif.hpp> 9 : #include <boost/preprocessor/list/adt.hpp> 10 : #include <boost/preprocessor/repetition/for.hpp> 11 : #include <boost/preprocessor/repetition/repeat.hpp> 12 : #include <boost/preprocessor/tuple/to_list.hpp> 13 : #include <limits> 14 : #include <pup.h> 15 : #include <vector> 16 : 17 : #include "DataStructures/Tensor/TypeAliases.hpp" 18 : #include "Options/String.hpp" 19 : #include "PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp" 20 : #include "PointwiseFunctions/Hydro/Units.hpp" 21 : #include "Utilities/Math.hpp" 22 : #include "Utilities/Serialization/CharmPupable.hpp" 23 : #include "Utilities/TMPL.hpp" 24 : 25 : /// \cond 26 : class DataVector; 27 : 28 : /// \endcond 29 : 30 : namespace EquationsOfState { 31 : 32 : /*! 33 : * \ingroup EquationsOfStateGroup 34 : * \brief An equation of state given by parametrized enthalpy 35 : * 36 : * This equation of state is determined as a function of \f$x = 37 : * \ln(\rho/\rho_0)\f$ where \f$\rho\f$ is the rest mass density and 38 : * \f$\rho_0\f$ is the provided reference density. 39 : * The pseudo-enthalpy \f$h \equiv (p + rho + u)/rho\f$ 40 : * is expanded as 41 : * 42 : * \f{equation} 43 : * h(x) = \sum_i a_i x^i + \sum_j b_j \sin(jkx) + c_j \cos(jkx) 44 : * \f} 45 : * 46 : * This form allows for convenient calculation of thermodynamic 47 : * quantities for a cold equation of state. For example 48 : * 49 : * \f{equation} 50 : * h(x) = \frac{d e} {d \rho} |_{x = \log(\rho/\rho_0)} 51 : * \f} 52 : * 53 : * where \f$e\f$ is the total energy density. At the same time \f$ dx = 54 : * d\rho/\rho \f$ so \f$ \rho_0 e^x dx = d \rho \f$ Therefore, 55 : * 56 : * \f{equation} 57 : * e(x) - e(x_0) = \int_{x_0}^x h(x') e^{x'} dx ' 58 : * \f} 59 : * 60 : * This can be computed analytically because 61 : * 62 : * \f{equation} 63 : * \int a_i \frac{x^i}{i!} e^{x} dx = \sum_{j \leq i} a_i (-1)^{i-j} 64 : * \frac{(x)^{j}}{j!} 65 : * + C \f} 66 : * 67 : * and 68 : * 69 : * \f{equation} 70 : * \int b_j \sin(j k x) e^x dx = b_j e^x \frac{\sin(jkx) - j k \cos(jkx)}{j^2 71 : * k^2 + 1} \f} 72 : * 73 : * \f{equation} 74 : * \int c_j \cos(j k x) e^x dx = b_j e^x \frac{\cos(jkx) + j k \sin(jkx)}{j^2 75 : * k^2 + 1} \f} 76 : * 77 : * From this most other thermodynamic quantities can be computed 78 : * analytically 79 : * 80 : * The internal energy density 81 : * \f{equation} 82 : * \epsilon(x)\rho(x) = e(x) - \rho(x) 83 : * \f} 84 : * 85 : * The pressure 86 : * \f{equation} 87 : * p(x) = \rho(x) h(x) - e(x) 88 : * \f} 89 : * 90 : * The derivative of the pressure with respect to the rest mass density 91 : * \f{equation} 92 : * \chi(x) = \frac{dp}{d\rho} |_{x = x(\rho)} = \frac{dh}{dx} 93 : * \f} 94 : * 95 : * Below the minimum density, a spectral parameterization 96 : * is used. 97 : * 98 : * 99 : * 100 : */ 101 : template <typename LowDensityEoS> 102 1 : class Enthalpy : public EquationOfState<true, 1> { 103 : private: 104 0 : struct Coefficients { 105 0 : std::vector<double> polynomial_coefficients; 106 0 : std::vector<double> sin_coefficients; 107 0 : std::vector<double> cos_coefficients; 108 0 : double trig_scale; 109 0 : double reference_density; 110 0 : bool has_exponential_prefactor; 111 0 : double exponential_external_constant; 112 0 : Coefficients() = default; 113 0 : ~Coefficients() = default; 114 0 : Coefficients(const Coefficients& coefficients) = default; 115 0 : Coefficients(std::vector<double> in_polynomial_coefficients, 116 : std::vector<double> in_sin_coefficients, 117 : std::vector<double> in_cos_coefficients, double in_trig_scale, 118 : double in_reference_density, 119 : double in_exponential_constant = 120 : std::numeric_limits<double>::quiet_NaN()); 121 0 : bool operator==(const Coefficients& rhs) const; 122 : 123 0 : Enthalpy<LowDensityEoS>::Coefficients compute_exponential_integral( 124 : const std::pair<double, double>& initial_condition, 125 : const double minimum_density); 126 0 : Enthalpy<LowDensityEoS>::Coefficients compute_derivative(); 127 0 : void pup(PUP::er& p); 128 : }; 129 : 130 : public: 131 0 : static constexpr size_t thermodynamic_dim = 1; 132 0 : static constexpr bool is_relativistic = true; 133 : 134 0 : static std::string name() { 135 : return "Enthalpy(" + pretty_type::name<LowDensityEoS>() + ")"; 136 : } 137 : 138 0 : struct ReferenceDensity { 139 0 : using type = double; 140 0 : static constexpr Options::String help = {"Reference density rho_0"}; 141 0 : static double lower_bound() { return 0.0; } 142 : }; 143 : 144 0 : struct MinimumDensity { 145 0 : using type = double; 146 0 : static constexpr Options::String help = { 147 : "Minimum valid density rho_min," 148 : " for this parametrization"}; 149 0 : static double lower_bound() { return 0.0; } 150 : }; 151 0 : struct MaximumDensity { 152 0 : using type = double; 153 0 : static constexpr Options::String help = {"Maximum density for this EoS"}; 154 0 : static double lower_bound() { return 0.0; } 155 : }; 156 : 157 0 : struct PolynomialCoefficients { 158 0 : using type = std::vector<double>; 159 0 : static constexpr Options::String help = {"Polynomial coefficients a_i"}; 160 : }; 161 : 162 0 : struct TrigScaling { 163 0 : using type = double; 164 0 : static constexpr Options::String help = { 165 : "Fundamental wavenumber of trig " 166 : "functions, k"}; 167 0 : static double lower_bound() { return 0.0; } 168 : }; 169 : 170 0 : struct SinCoefficients { 171 0 : using type = std::vector<double>; 172 0 : static constexpr Options::String help = {"Sine coefficients b_j"}; 173 : }; 174 0 : struct CosCoefficients { 175 0 : using type = std::vector<double>; 176 0 : static constexpr Options::String help = {"Cosine coefficients c_j"}; 177 : }; 178 0 : struct StitchedLowDensityEoS { 179 0 : using type = LowDensityEoS; 180 0 : static std::string name() { 181 : return pretty_type::short_name<LowDensityEoS>(); 182 : } 183 0 : static constexpr Options::String help = { 184 : "Low density EoS stitched at the MinimumDensity"}; 185 : }; 186 : 187 0 : struct TransitionDeltaEpsilon { 188 0 : using type = double; 189 0 : static constexpr Options::String help = { 190 : "the change in internal energy across the low-" 191 : "to-high-density transition, generically 0.0"}; 192 0 : static double lower_bound() { return 0.0; } 193 : }; 194 : 195 0 : static constexpr Options::String help = { 196 : "An EoS with a parametrized value h(log(rho/rho_0)) with h the specific " 197 : "enthalpy and rho the baryon rest mass density. The enthalpy is " 198 : "expanded as a sum of polynomial terms and trigonometric corrections. " 199 : "let x = log(rho/rho_0) in" 200 : "h(x) = \\sum_i a_ix^i + \\sum_j b_jsin(k * j * x) + c_jcos(k * j * x) " 201 : "Note that rho(x)(1+epsilon(x)) = int_0^x e^x' h((x') dx' can be " 202 : "computed " 203 : "analytically, and therefore so can " 204 : "P(x) = rho(x) * (h(x) - (1 + epsilon(x))) "}; 205 : 206 0 : using options = 207 : tmpl::list<ReferenceDensity, MaximumDensity, MinimumDensity, TrigScaling, 208 : PolynomialCoefficients, SinCoefficients, CosCoefficients, 209 : StitchedLowDensityEoS, TransitionDeltaEpsilon>; 210 : 211 0 : Enthalpy() = default; 212 0 : Enthalpy(const Enthalpy&) = default; 213 0 : Enthalpy& operator=(const Enthalpy&) = default; 214 0 : Enthalpy(Enthalpy&&) = default; 215 0 : Enthalpy& operator=(Enthalpy&&) = default; 216 0 : ~Enthalpy() override = default; 217 : 218 0 : Enthalpy(double reference_density, double max_density, double min_density, 219 : double trig_scale, 220 : const std::vector<double>& polynomial_coefficients, 221 : const std::vector<double>& sin_coefficients, 222 : const std::vector<double>& cos_coefficients, 223 : const LowDensityEoS& low_density_eos, 224 : const double transition_delta_epsilon); 225 : 226 0 : std::unique_ptr<EquationOfState<true, 1>> get_clone() const override; 227 : 228 0 : std::unique_ptr<EquationOfState<true, 3>> promote_to_3d_eos() const override; 229 : 230 0 : std::unique_ptr<EquationOfState<true, 2>> promote_to_2d_eos() const override; 231 : 232 0 : bool is_equal(const EquationOfState<true, 1>& rhs) const override; 233 : 234 0 : bool operator==(const Enthalpy<LowDensityEoS>& rhs) const; 235 : 236 0 : bool operator!=(const Enthalpy<LowDensityEoS>& rhs) const; 237 : 238 : EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(Enthalpy, 1) 239 : 240 0 : WRAPPED_PUPable_decl_base_template( // NOLINT 241 : SINGLE_ARG(EquationOfState<true, 1>), Enthalpy); 242 : 243 : /// The lower bound of the rest mass density that is valid for this EOS 244 1 : double rest_mass_density_lower_bound() const override { return 0.0; } 245 : 246 : /// The upper bound of the rest mass density that is valid for this EOS 247 1 : double rest_mass_density_upper_bound() const override { 248 : return std::numeric_limits<double>::max(); 249 : } 250 : 251 : /// The lower bound of the specific enthalpy that is valid for this EOS 252 1 : double specific_enthalpy_lower_bound() const override { return 1.0; } 253 : 254 : /// The lower bound of the specific internal energy that is valid for this EOS 255 1 : double specific_internal_energy_lower_bound() const override { return 0.0; } 256 : 257 : /// The upper bound of the specific internal energy that is valid for this EOS 258 1 : double specific_internal_energy_upper_bound() const override { 259 : return std::numeric_limits<double>::max(); 260 : } 261 : 262 : /// The vacuum baryon mass for this EoS 263 1 : double baryon_mass() const override { return low_density_eos_.baryon_mass(); } 264 : 265 : private: 266 : EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(1) 267 : 268 : SPECTRE_ALWAYS_INLINE 269 0 : bool in_low_density_domain(const double rest_mass_density) const { 270 : return rest_mass_density < minimum_density_; 271 : } 272 : 273 0 : double x_from_density(const double rest_mass_density) const; 274 0 : double density_from_x(const double x) const; 275 0 : double energy_density_from_log_density(const double x, 276 : const double rest_mass_density) const; 277 0 : static double evaluate_coefficients( 278 : const Enthalpy::Coefficients& coefficients, const double x, 279 : const double exponential_prefactor = 280 : std::numeric_limits<double>::signaling_NaN()); 281 0 : static Enthalpy::Coefficients compute_pressure_coefficients( 282 : const typename Enthalpy::Coefficients& enthalpy, 283 : const typename Enthalpy::Coefficients& energy_density); 284 : 285 0 : double chi_from_density(const double rest_mass_density) const; 286 0 : double specific_internal_energy_from_density( 287 : const double rest_mass_density) const; 288 0 : double specific_enthalpy_from_density(const double rest_mass_density) const; 289 0 : double pressure_from_density(const double rest_mass_density) const; 290 0 : double pressure_from_log_density(const double x, 291 : const double rest_mass_density) const; 292 0 : double rest_mass_density_from_enthalpy(const double specific_enthalpy) const; 293 : 294 0 : double reference_density_ = std::numeric_limits<double>::signaling_NaN(); 295 0 : double minimum_density_ = std::numeric_limits<double>::signaling_NaN(); 296 0 : double maximum_density_ = std::numeric_limits<double>::signaling_NaN(); 297 0 : double minimum_enthalpy_ = std::numeric_limits<double>::signaling_NaN(); 298 : 299 0 : LowDensityEoS low_density_eos_; 300 0 : Coefficients coefficients_; 301 0 : Coefficients exponential_integral_coefficients_; 302 0 : Coefficients derivative_coefficients_; 303 0 : Coefficients pressure_coefficients_; 304 : }; 305 : 306 : } // namespace EquationsOfState