Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <limits>
7 : #include <ostream>
8 :
9 : #include "NumericalAlgorithms/Interpolation/CubicSpline.hpp"
10 : #include "PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp"
11 :
12 : /// \cond
13 : namespace Options {
14 : struct Option;
15 : template <typename T>
16 : struct create_from_yaml;
17 : } // namespace Options
18 : namespace PUP {
19 : class er;
20 : } // namespace PUP
21 : /// \endcond
22 :
23 : namespace RelativisticEuler::Solutions {
24 :
25 : /// Radial coordinate of a TOV solution.
26 1 : enum class TovCoordinates {
27 : /// Areal radial coordinate, in which the exterior TOV solution coincides with
28 : /// the Schwarzschild metric in standard Schwarzschild coordinates.
29 : Schwarzschild,
30 : /// Isotropic radial coordinate, in which the spatial metric is conformally
31 : /// flat.
32 : Isotropic
33 : };
34 :
35 0 : std::ostream& operator<<(std::ostream& os, TovCoordinates coords);
36 :
37 : } // namespace RelativisticEuler::Solutions
38 :
39 : template <>
40 0 : struct Options::create_from_yaml<RelativisticEuler::Solutions::TovCoordinates> {
41 : template <typename Metavariables>
42 0 : static RelativisticEuler::Solutions::TovCoordinates create(
43 : const Options::Option& options) {
44 : return create<void>(options);
45 : }
46 : };
47 :
48 : template <>
49 0 : RelativisticEuler::Solutions::TovCoordinates
50 : Options::create_from_yaml<RelativisticEuler::Solutions::TovCoordinates>::create<
51 : void>(const Options::Option& options);
52 :
53 : namespace RelativisticEuler::Solutions {
54 :
55 : /*!
56 : * \brief TOV solver based on Lindblom's method
57 : *
58 : * Uses Lindblom's method of integrating the TOV equations from
59 : * \cite Lindblom1998dp .
60 : *
61 : * Instead of integrating the interior mass \f$m(r)\f$ and pressure \f$p(r)\f$,
62 : * Lindblom introduces the variables \f$u=r^2\f$ and \f$v=m/r\f$. Then, the TOV
63 : * equations are integrated with the log of the specific enthalpy as the
64 : * independent variable, \f$\ln(h)\f$, from the center of the star where
65 : * \f$h(r=0) = h_c\f$ to its surface \f$h(r=R) = 1\f$. The ODEs being solved are
66 : * Eq. (A2) and (A3) in \cite Lindblom1998dp :
67 : *
68 : * \f{align}
69 : * \frac{\mathrm{d}u}{\mathrm{d}\ln{h}} &= \frac{-2u (1 - 2v)}{4\pi u p + v} \\
70 : * \frac{\mathrm{d}v}{\mathrm{d}\ln{h}} &=
71 : * -(1 - 2v) \frac{4\pi u \rho - v}{4\pi u p + v}
72 : * \f}
73 : *
74 : * Note that Lindblom's paper labels the independent variable as \f$h\f$.
75 : * However the \f$h\f$ in Lindblom's paper is **not** the specific enthalpy but
76 : * its logarithm, \f$\ln(h)\f$.
77 : *
78 : * The ODEs are solved numerically when this class is constructed, and the
79 : * quantities \f$m(r)/r\f$ and \f$\ln(h)\f$ are interpolated and exposed as
80 : * member functions. With these quantities the metric can be constructed as:
81 : *
82 : * \f{equation}
83 : * \mathrm{d}s^2 = -\alpha^2 \mathrm{d}t^2 + (1 - 2m/r)^{-1} \mathrm{d}r^2
84 : * + r^2 \mathrm{d}\Omega^2
85 : * \f}
86 : *
87 : * where the lapse is
88 : *
89 : * \f{equation}
90 : * \alpha(r < R) = \mathcal{E} / h = \alpha(r=R) / h
91 : * \text{,}
92 : * \f}
93 : *
94 : * with the conserved `injection_energy()` \f$\mathcal{E}\f$, such that the
95 : * lapse matches the exterior Schwarzschild solution:
96 : *
97 : * \f{equation}
98 : * \alpha(r \geq R) = \sqrt{1 - \frac{2M}{r}}
99 : * \f}
100 : *
101 : * \par Isotropic radial coordinate
102 : * This class also supports transforming to an isotropic radial coordinate. When
103 : * you pass `RelativisticEuler::Solutions::TovCoordinates::Isotropic` to the
104 : * constructor, an additional ODE is integrated alongside the TOV equations to
105 : * determine the conformal factor
106 : *
107 : * \f{equation}
108 : * \psi^2 = \frac{r}{\bar{r}}
109 : * \f}
110 : *
111 : * where \f$r\f$ is the areal (Schwarzschild) radius and \f$\bar{r}\f$ is the
112 : * isotropic radius. The additional ODE is:
113 : *
114 : * \f{equation}
115 : * \frac{\mathrm{d}\ln(\psi)}{\mathrm{d}\ln{h}} =
116 : * \frac{\sqrt{1 - 2v}}{1 + \sqrt{1 - 2v}} \frac{v}{4\pi u p + v}
117 : * \f}
118 : *
119 : * In isotropic coordinates, the spatial metric is conformally flat:
120 : *
121 : * \f{equation}
122 : * \mathrm{d}s^2 = -\alpha^2 \mathrm{d}t^2 + \psi^4 (\mathrm{d}\bar{r}^2 +
123 : * \bar{r}^2 \mathrm{d}\Omega^2)
124 : * \f}
125 : *
126 : * When isotropic coordinates are selected, radii returned by member functions
127 : * or taken as arguments are isotropic. An exception is `mass_over_radius()`,
128 : * which always returns the quantity \f$m / r\f$ because that is the quantity
129 : * stored internally and hence most numerically precise. See
130 : * `mass_over_radius()` for details.
131 : */
132 1 : class TovSolution {
133 : public:
134 0 : TovSolution(
135 : const EquationsOfState::EquationOfState<true, 1>& equation_of_state,
136 : double central_mass_density,
137 : const TovCoordinates coordinate_system = TovCoordinates::Schwarzschild,
138 : double log_enthalpy_at_outer_radius = 0.0,
139 : double absolute_tolerance = 1.e-18, double relative_tolerance = 1.0e-14);
140 :
141 0 : TovSolution() = default;
142 0 : TovSolution(const TovSolution& /*rhs*/) = default;
143 0 : TovSolution& operator=(const TovSolution& /*rhs*/) = default;
144 0 : TovSolution(TovSolution&& /*rhs*/) = default;
145 0 : TovSolution& operator=(TovSolution&& /*rhs*/) = default;
146 0 : ~TovSolution() = default;
147 :
148 : /// The type of radial coordinate.
149 : ///
150 : /// \see RelativisticEuler::Solutions::TovCoordinates
151 1 : TovCoordinates coordinate_system() const { return coordinate_system_; }
152 :
153 : /// \brief The outer radius of the solution.
154 : ///
155 : /// This is the outer radius in the specified `coordinate_system()`, i.e.,
156 : /// areal or isotropic.
157 : ///
158 : /// \note This is the radius at which `log_specific_enthalpy` is equal
159 : /// to the value of `log_enthalpy_at_outer_radius` that was given when
160 : /// constructing this TovSolution
161 1 : double outer_radius() const { return outer_radius_; }
162 :
163 : /// The total mass \f$m(R)\f$, where \f$R\f$ is the outer radius.
164 1 : double total_mass() const { return total_mass_; }
165 :
166 : /*!
167 : * \brief The injection energy \f$\mathcal{E}=\alpha(r=R)=\sqrt{1-2M/R}\f$.
168 : *
169 : * The injection energy of the TOV solution is
170 : *
171 : * \f{equation}
172 : * \mathcal{E} = -h k^a u_a = h \alpha
173 : * \text{,}
174 : * \f}
175 : *
176 : * where \f$\boldsymbol{k} = \partial_t\f$ is a Killing vector of the static
177 : * solution, \f$h\f$ is the specific enthalpy, \f$u_a\f$ is the fluid
178 : * four-velocity, and \f$\alpha\f$ is the lapse (see, e.g., Eqs. (2.19) and
179 : * (4.2) in \cite Moldenhauer2014yaa). Since the TOV solution is static, the
180 : * injection energy is conserved not only along stream lines but throughout
181 : * the star,
182 : *
183 : * \f{equation}
184 : * \nabla_a \mathcal{E} = 0
185 : * \text{.}
186 : * \f}
187 : *
188 : * Therefore,
189 : *
190 : * \f{equation}
191 : * \mathcal{E} = \alpha(r=R) = \sqrt{1 - 2M/R}
192 : * \f}
193 : *
194 : * by evaluating the injection energy at the outer (areal) radius \f$R\f$,
195 : * where \f$h=1\f$ and where we match the lapse to the outer Schwarzschild
196 : * solution. The conservation also implies
197 : *
198 : * \f{equation}
199 : * \alpha = \mathcal{E} / h
200 : * \f}
201 : *
202 : * throughout the star.
203 : */
204 1 : double injection_energy() const { return injection_energy_; }
205 :
206 : /// \brief The mass inside the given radius over the areal radius,
207 : /// \f$\frac{m(r)}{r}\f$
208 : ///
209 : /// The argument to this function is the radius in the `coordinate_system()`,
210 : /// i.e., areal (Schwarzschild) or isotropic radius. The denominator \f$r\f$
211 : /// in the return value is always the areal (Schwarzschild) radius. You can
212 : /// use the conformal factor \f$\psi=\sqrt{r / \bar{r}}\f$ returned by the
213 : /// `conformal_factor()` function to obtain the mass over the isotropic
214 : /// radius, or the mass alone. The reason for this choice is that we represent
215 : /// the solution internally as the mass over the areal radius, so this is the
216 : /// most numerically precise quantity from which other quantities can be
217 : /// derived.
218 : ///
219 : /// \note `r` should be non-negative and not greater than `outer_radius()`.
220 : template <typename DataType>
221 1 : DataType mass_over_radius(const DataType& r) const;
222 :
223 : /// \brief The log of the specific enthalpy at the given radius
224 : ///
225 : /// \note `r` should be non-negative and not greater than `outer_radius()`
226 : template <typename DataType>
227 1 : DataType log_specific_enthalpy(const DataType& r) const;
228 :
229 : /// \brief The conformal factor \f$\psi=\sqrt{r / \bar{r}}\f$.
230 : ///
231 : /// The conformal factor is computed only when the `coordinate_system()` is
232 : /// `RelativisticEuler::Solution::TovCoordinates::Isotropic`. Otherwise, it is
233 : /// an error to call this function.
234 : ///
235 : /// \note `r` should be non-negative and not greater than `outer_radius()`
236 : template <typename DataType>
237 1 : DataType conformal_factor(const DataType& r) const;
238 :
239 0 : const intrp::CubicSpline& mass_over_radius_interpolant() const {
240 : return mass_over_radius_interpolant_;
241 : }
242 0 : const intrp::CubicSpline& log_specific_enthalpy_interpolant() const {
243 : return log_enthalpy_interpolant_;
244 : }
245 0 : const intrp::CubicSpline& conformal_factor_interpolant() const {
246 : return conformal_factor_interpolant_;
247 : }
248 :
249 : // NOLINTNEXTLINE(google-runtime-references)
250 0 : void pup(PUP::er& p);
251 :
252 : private:
253 : template <TovCoordinates CoordSystem>
254 0 : void integrate(
255 : const EquationsOfState::EquationOfState<true, 1>& equation_of_state,
256 : const double central_mass_density,
257 : const double log_enthalpy_at_outer_radius,
258 : const double absolute_tolerance, const double relative_tolerance);
259 :
260 0 : TovCoordinates coordinate_system_{};
261 0 : double outer_radius_{std::numeric_limits<double>::signaling_NaN()};
262 0 : double total_mass_{std::numeric_limits<double>::signaling_NaN()};
263 0 : double injection_energy_{std::numeric_limits<double>::signaling_NaN()};
264 0 : intrp::CubicSpline mass_over_radius_interpolant_;
265 0 : intrp::CubicSpline log_enthalpy_interpolant_;
266 0 : intrp::CubicSpline conformal_factor_interpolant_;
267 : };
268 :
269 : } // namespace RelativisticEuler::Solutions
|