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 <cstddef>
14 : #include <limits>
15 : #include <pup.h>
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/Serialization/CharmPupable.hpp"
22 : #include "Utilities/TMPL.hpp"
23 :
24 : /// \cond
25 : class DataVector;
26 : /// \endcond
27 :
28 : namespace EquationsOfState {
29 : /*!
30 : * \ingroup EquationsOfStateGroup
31 : *
32 : * \brief Hybrid equation of state combining a barotropic EOS for cold
33 : * (zero-temperature) part with a simple thermal part
34 : *
35 : * The hybrid equation of state:
36 : *
37 : * \f[
38 : * p = p_{cold}(\rho) + \rho (\Gamma_{th}-1) (\epsilon - \epsilon_{cold}(\rho))
39 : * \f]
40 : *
41 : * where \f$p\f$ is the pressure, \f$\rho\f$ is the rest mass density,
42 : * \f$\epsilon\f$ is the specific internal energy, \f$p_{cold}\f$ and
43 : * \f$\epsilon_{cold}\f$ are the pressure and specific internal energy evaluated
44 : * using the cold EOS, and \f$\Gamma_{th}\f$ is the adiabatic index for the
45 : * thermal part.
46 : *
47 : * The temperature \f$T\f$ is defined as
48 : *
49 : * \f[
50 : * T = (\Gamma_{th} - 1) (\epsilon - \epsilon_{cold})
51 : * \f]
52 : *
53 : * This is the amount of internal energy above that of the cold EOS.
54 : *
55 : * For a hybrid EOS with the cold EOS being a polytrope we have
56 : *
57 : * \f{align}
58 : * p &= \kappa \rho^\Gamma + \rho T
59 : * =\kappa\rho^\Gamma+\rho
60 : * (\Gamma-1)\left(\epsilon-\frac{K\rho^{\Gamma-1}}{\Gamma-1}\right), \\
61 : * T&=(\Gamma-1)\left(\epsilon-\frac{K\rho^{\Gamma-1}}{\Gamma-1}\right), \\
62 : * \epsilon &= \frac{1}{(\Gamma-1)}\frac{p}{\rho}
63 : * \f}
64 : */
65 : template <typename ColdEquationOfState>
66 1 : class HybridEos
67 : : public EquationOfState<ColdEquationOfState::is_relativistic, 2> {
68 : public:
69 0 : static constexpr size_t thermodynamic_dim = 2;
70 0 : static constexpr bool is_relativistic = ColdEquationOfState::is_relativistic;
71 :
72 0 : struct ColdEos {
73 0 : using type = ColdEquationOfState;
74 0 : static constexpr Options::String help = {"Cold equation of state"};
75 0 : static std::string name() {
76 : return pretty_type::short_name<ColdEquationOfState>();
77 : }
78 : };
79 :
80 0 : struct ThermalAdiabaticIndex {
81 0 : using type = double;
82 0 : static constexpr Options::String help = {"Adiabatic index Gamma_th"};
83 : };
84 :
85 0 : struct MinTemperature {
86 0 : using type = double;
87 0 : static type lower_bound() { return 0.0; }
88 0 : static constexpr Options::String help = {
89 : "Minimum temperature. "
90 : "This value must be non-negative."};
91 : };
92 :
93 0 : static constexpr Options::String help = {
94 : "A hybrid equation of state combining a cold EOS with a simple thermal "
95 : "part. The pressure is related to the rest mass density by "
96 : " p = p_cold(rho) + rho * (Gamma_th - 1) * (epsilon - "
97 : "epsilon_cold(rho)), where p is the pressure, rho is the rest mass "
98 : "density, epsilon is the specific internal energy, p_cold and "
99 : "epsilon_cold are the pressure and specific internal energy evaluated "
100 : "using the cold EOS and Gamma_th is the adiabatic index for the thermal "
101 : "part."};
102 :
103 0 : using options = tmpl::list<ColdEos, ThermalAdiabaticIndex, MinTemperature>;
104 :
105 0 : HybridEos() = default;
106 0 : HybridEos(const HybridEos&) = default;
107 0 : HybridEos& operator=(const HybridEos&) = default;
108 0 : HybridEos(HybridEos&&) = default;
109 0 : HybridEos& operator=(HybridEos&&) = default;
110 0 : ~HybridEos() override = default;
111 :
112 0 : HybridEos(ColdEquationOfState cold_eos, double thermal_adiabatic_index,
113 : double min_temperature = 0.0);
114 :
115 : EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(HybridEos, 2)
116 :
117 0 : WRAPPED_PUPable_decl_base_template( // NOLINT
118 : SINGLE_ARG(EquationOfState<is_relativistic, 2>), HybridEos);
119 :
120 0 : std::unique_ptr<EquationOfState<is_relativistic, 2>> get_clone()
121 : const override;
122 :
123 0 : std::unique_ptr<EquationOfState<is_relativistic, 3>> promote_to_3d_eos()
124 : const override;
125 :
126 : /// \brief Returns `true` if the EOS is barotropic
127 1 : bool is_barotropic() const override { return false; }
128 :
129 0 : bool operator==(const HybridEos<ColdEquationOfState>& rhs) const;
130 :
131 0 : bool operator!=(const HybridEos<ColdEquationOfState>& rhs) const;
132 :
133 0 : bool is_equal(const EquationOfState<is_relativistic, 2>& rhs) const override;
134 :
135 0 : static std::string name() {
136 : return "HybridEos(" + pretty_type::name<ColdEquationOfState>() + ")";
137 : }
138 :
139 : /// The lower bound of the rest mass density that is valid for this EOS
140 1 : double rest_mass_density_lower_bound() const override {
141 : return cold_eos_.rest_mass_density_lower_bound();
142 : }
143 :
144 : /// The upper bound of the rest mass density that is valid for this EOS
145 1 : double rest_mass_density_upper_bound() const override {
146 : return cold_eos_.rest_mass_density_upper_bound();
147 : }
148 :
149 : /// The lower bound of the specific internal energy that is valid for this EOS
150 : /// at the given rest mass density \f$\rho\f$
151 1 : double specific_internal_energy_lower_bound(
152 : const double rest_mass_density) const override {
153 : return get(cold_eos_.specific_internal_energy_from_density(
154 : Scalar<double>{rest_mass_density})) +
155 : (min_temperature_) / (thermal_adiabatic_index_ - 1.0);
156 : }
157 :
158 : /// The upper bound of the specific internal energy that is valid for this EOS
159 : /// at the given rest mass density \f$\rho\f$
160 1 : double specific_internal_energy_upper_bound(
161 : const double /*rest_mass_density*/) const override {
162 : return std::numeric_limits<double>::max();
163 : }
164 :
165 : /// The lower bound of the specific enthalpy that is valid for this EOS
166 1 : double specific_enthalpy_lower_bound() const override {
167 : return cold_eos_.specific_enthalpy_lower_bound() +
168 : (thermal_adiabatic_index_ * min_temperature_) /
169 : (thermal_adiabatic_index_ - 1.0);
170 : }
171 :
172 : /// The lower bound of the temperature that is valid for this EOS.
173 : /// Non-zero lower bound could be set to impose floor on the specific
174 : /// internal energy.
175 1 : double temperature_lower_bound() const override { return min_temperature_; }
176 :
177 : /// The vacuum baryon mass for this EoS
178 1 : double baryon_mass() const override { return cold_eos_.baryon_mass(); }
179 :
180 : private:
181 : EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(2)
182 :
183 0 : double specific_entropy_from_density_and_thermal_energy(
184 : double density, double energy) const;
185 :
186 0 : ColdEquationOfState cold_eos_;
187 0 : double thermal_adiabatic_index_ =
188 : std::numeric_limits<double>::signaling_NaN();
189 0 : double min_temperature_ = std::numeric_limits<double>::signaling_NaN();
190 : };
191 :
192 : /// \cond
193 : template <typename ColdEquationOfState>
194 : PUP::able::PUP_ID EquationsOfState::HybridEos<ColdEquationOfState>::my_PUP_ID =
195 : 0;
196 : /// \endcond
197 : } // namespace EquationsOfState
|