SpECTRE Documentation Coverage Report
Current view: top level - PointwiseFunctions/AnalyticSolutions/RelativisticEuler - FishboneMoncriefDisk.hpp Hit Total Coverage
Commit: 5b6dac11263b5fb9107cb6ea064c64c61b65a417 Lines: 10 76 13.2 %
Date: 2024-04-19 22:56:45
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <cstddef>
       7             : #include <limits>
       8             : 
       9             : #include "DataStructures/Tensor/TypeAliases.hpp"
      10             : #include "Options/String.hpp"
      11             : #include "PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp"
      12             : #include "PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp"
      13             : #include "PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Solutions.hpp"
      14             : #include "PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp"  // IWYU pragma: keep
      15             : #include "PointwiseFunctions/Hydro/Tags.hpp"
      16             : #include "PointwiseFunctions/Hydro/TagsDeclarations.hpp"
      17             : #include "PointwiseFunctions/Hydro/Temperature.hpp"
      18             : #include "PointwiseFunctions/InitialDataUtilities/InitialData.hpp"
      19             : #include "Utilities/ForceInline.hpp"
      20             : #include "Utilities/Requires.hpp"
      21             : #include "Utilities/Serialization/CharmPupable.hpp"
      22             : #include "Utilities/TMPL.hpp"
      23             : #include "Utilities/TaggedTuple.hpp"
      24             : 
      25             : /// \cond
      26             : namespace PUP {
      27             : class er;  // IWYU pragma: keep
      28             : }  // namespace PUP
      29             : namespace grmhd::AnalyticData {
      30             : class MagnetizedFmDisk;
      31             : }  // namespace grmhd::AnalyticData
      32             : /// \endcond
      33             : 
      34             : // IWYU pragma: no_include <pup.h>
      35             : 
      36             : namespace RelativisticEuler {
      37           1 : namespace Solutions {
      38             : 
      39             : /*!
      40             :  * \brief Fluid disk orbiting a Kerr black hole
      41             :  *
      42             :  * The Fishbone-Moncrief solution to the 3D relativistic Euler system
      43             :  * \cite Fishbone1976apj, representing the isentropic flow of a thick fluid disk
      44             :  * orbiting a Kerr black hole. In Boyer-Lindquist coordinates
      45             :  * \f$(t, r, \theta, \phi)\f$, the flow is assumed to be purely toroidal,
      46             :  *
      47             :  * \f{align*}
      48             :  * u^\mu = (u^t, 0, 0, u^\phi),
      49             :  * \f}
      50             :  *
      51             :  * where \f$u^\mu\f$ is the 4-velocity. Then, all the fluid quantities are
      52             :  * assumed to share the same symmetries as those of the background spacetime,
      53             :  * namely they are stationary (independent of \f$t\f$), and axially symmetric
      54             :  * (independent of \f$\phi\f$).
      55             :  *
      56             :  * Self-gravity is neglected, so that the fluid
      57             :  * variables are determined as functions of the metric. Following the treatment
      58             :  * by Kozlowski et al. \cite Kozlowski1978aa (but using signature +2)
      59             :  * the solution is expressed in terms of the quantities
      60             :  *
      61             :  * \f{align*}
      62             :  * \Omega &= \dfrac{u^\phi}{u^t},\\
      63             :  * W &= W_\text{in} - \int_{p_\text{in}}^p\frac{dp}{e + p},
      64             :  * \f}
      65             :  *
      66             :  * where \f$\Omega\f$ is the angular velocity, \f$p\f$ is the fluid pressure,
      67             :  * \f$e\f$ is the energy density, and \f$W\f$ is an auxiliary quantity
      68             :  * interpreted in the Newtonian limit as the total (gravitational + centrifugal)
      69             :  * potential. \f$W_\text{in}\f$ and \f$p_\text{in}\f$ are the potential and
      70             :  * the pressure at the radius of the inner edge, i.e. the closest edge to the
      71             :  * black hole. Here we assume \f$p_\text{in} = 0.\f$ The solution to the Euler
      72             :  * equation is then
      73             :  *
      74             :  * \f{align*}
      75             :  * (u^t)^2 &= \frac{A}{2\Delta\Sigma}\left(1 +
      76             :  * \sqrt{1 + \frac{4l^2\Delta \Sigma^2}{A^2\sin^2\theta}}\right)\\
      77             :  * \Omega &= \frac{\Sigma}{A (u^t)^2}\frac{l}{\sin^2\theta} + \frac{2Mra}{A}\\
      78             :  * u^\phi &= \Omega u^t\\
      79             :  * W &= l\Omega - \ln u^t,
      80             :  * \f}
      81             :  *
      82             :  * where
      83             :  *
      84             :  * \f{align*}
      85             :  * \Sigma = r^2 + a^2\cos^2\theta\qquad
      86             :  * \Delta = r^2 - 2Mr + a^2\qquad
      87             :  * A = (r^2 + a^2)^2 - \Delta a^2 \sin^2\theta
      88             :  * \f}
      89             :  *
      90             :  * and \f$l = u_\phi u^t\f$ is the so-called angular momentum per unit
      91             :  * intertial mass, which is a parameter defining an
      92             :  * individual disk. In deriving the solution, an integration constant has been
      93             :  * chosen so that \f$ W\longrightarrow 0\f$ as \f$r\longrightarrow \infty\f$,
      94             :  * in accordance with the Newtonian limit. Note that, from its definition,
      95             :  * equipotential contours coincide with isobaric contours. Physically, the
      96             :  * matter can fill each of the closed surfaces \f$W = \text{const}\f$, giving
      97             :  * rise to an orbiting thick disk. For \f$W > 0\f$, all equipotentials are open,
      98             :  * whereas for \f$W < 0\f$, some of them will be closed. Should a disk exist,
      99             :  * the pressure reaches a maximum value on the equator at a coordinate radius
     100             :  * \f$r_\text{max}\f$ that is related to the angular momentum per unit inertial
     101             :  * mass via
     102             :  *
     103             :  * \f{align*}
     104             :  * l = \dfrac{M^{1/2}(r_\text{max}^{3/2} + aM^{1/2})(a^2 - 2aM^{1/2}
     105             :  * r_\text{max}^{1/2} + r_\text{max}^2)}{2aM^{1/2}r_\text{max}^{3/2} +
     106             :  * (r_\text{max} - 3M)r_\text{max}^2}.
     107             :  * \f}
     108             :  *
     109             :  * Once \f$W\f$ is determined, an equation of state is required in order to
     110             :  * obtain the thermodynamic variables. If the flow is isentropic, the specific
     111             :  * enthalpy can readily be obtained from the first and second laws of
     112             :  * thermodynamics: one has
     113             :  *
     114             :  * \f{align*}
     115             :  * \frac{dp}{e + p} = \frac{dh}{h}
     116             :  * \f}
     117             :  *
     118             :  * so that
     119             :  *
     120             :  * \f{align*}
     121             :  * h = h_\text{in}\exp(W_\text{in} - W),
     122             :  * \f}
     123             :  *
     124             :  * and the pressure can be obtained from a thermodynamic relation of the form
     125             :  * \f$h = h(p)\f$. Here we assume a polytropic relation
     126             :  *
     127             :  * \f{align*}
     128             :  * p = K\rho^\gamma.
     129             :  * \f}
     130             :  *
     131             :  * Once all the variables are known in Boyer-Lindquist (or Kerr) coordinates, it
     132             :  * is straightforward to write them in Cartesian Kerr-Schild coordinates. The
     133             :  * coordinate transformation in gr::KerrSchildCoords helps read the Jacobian
     134             :  * matrix, which, applied to the azimuthal flow of the disk, gives
     135             :  *
     136             :  * \f{align*}
     137             :  * u_\text{KS}^\mu = u^t(1, -y\Omega, x\Omega, 0),
     138             :  * \f}
     139             :  *
     140             :  * where \f$u^t\f$ and \f$\Omega\f$ are now understood as functions of the
     141             :  * Kerr-Schild coordinates. Finally, the spatial velocity can be readily
     142             :  * obtained from its definition,
     143             :  *
     144             :  * \f{align*}
     145             :  * \alpha v^i = \frac{u^i}{u^t} + \beta^i,
     146             :  * \f}
     147             :  *
     148             :  * where \f$\alpha\f$ and \f$\beta^i\f$ are the lapse and the shift,
     149             :  * respectively.
     150             :  *
     151             :  * \note Kozlowski et al. \cite Kozlowski1978aa denote
     152             :  * \f$l_* = u_\phi u^t\f$ in order to
     153             :  * distinguish this quantity from their own definition \f$l = - u_\phi/u_t\f$.
     154             :  *
     155             :  * \note When using Kerr-Schild coordinates, the horizon that is at
     156             :  * constant \f$r\f$ is not spherical, but instead spheroidal. This could make
     157             :  * application of boundary condition and computing various fluxes
     158             :  * across the horizon more complicated than they need to be.
     159             :  * Thus, we use Spherical Kerr-Schild coordinates,
     160             :  * see gr::Solutions::SphericalKerrSchild, in which constant \f$r\f$
     161             :  * is spherical. Because we compute variables in Kerr-Schild coordinates,
     162             :  * there is a necessary extra step of transforming them back to
     163             :  * Spherical Kerr-Schild coordinates.
     164             :  *
     165             :  */
     166           1 : class FishboneMoncriefDisk
     167             :     : public virtual evolution::initial_data::InitialData,
     168             :       public MarkAsAnalyticSolution,
     169             :       public AnalyticSolution<3>,
     170             :       public hydro::TemperatureInitialization<FishboneMoncriefDisk> {
     171             :  protected:
     172             :   template <typename DataType>
     173             :   struct IntermediateVariables;
     174             : 
     175             :  public:
     176           0 :   using equation_of_state_type = EquationsOfState::PolytropicFluid<true>;
     177             : 
     178             :   /// The mass of the black hole, \f$M\f$.
     179           1 :   struct BhMass {
     180           0 :     using type = double;
     181           0 :     static constexpr Options::String help = {"The mass of the black hole."};
     182           0 :     static type lower_bound() { return 0.0; }
     183             :   };
     184             :   /// The dimensionless black hole spin, \f$\chi = a/M\f$.
     185           1 :   struct BhDimlessSpin {
     186           0 :     using type = double;
     187           0 :     static constexpr Options::String help = {
     188             :         "The dimensionless black hole spin."};
     189           0 :     static type lower_bound() { return 0.0; }
     190           0 :     static type upper_bound() { return 1.0; }
     191             :   };
     192             :   /// The radial coordinate of the inner edge of the disk, in units of \f$M\f$.
     193           1 :   struct InnerEdgeRadius {
     194           0 :     using type = double;
     195           0 :     static constexpr Options::String help = {
     196             :         "The radial coordinate of the inner edge of the disk."};
     197             :   };
     198             :   /// The radial coordinate of the maximum pressure, in units of \f$M\f$.
     199           1 :   struct MaxPressureRadius {
     200           0 :     using type = double;
     201           0 :     static constexpr Options::String help = {
     202             :         "The radial coordinate of the maximum pressure."};
     203             :   };
     204             :   /// The polytropic constant of the fluid.
     205           1 :   struct PolytropicConstant {
     206           0 :     using type = double;
     207           0 :     static constexpr Options::String help = {
     208             :         "The polytropic constant of the fluid."};
     209           0 :     static type lower_bound() { return 0.; }
     210             :   };
     211             :   /// The polytropic exponent of the fluid.
     212           1 :   struct PolytropicExponent {
     213           0 :     using type = double;
     214           0 :     static constexpr Options::String help = {
     215             :         "The polytropic exponent of the fluid."};
     216           0 :     static type lower_bound() { return 1.; }
     217             :   };
     218             : 
     219           0 :   using options =
     220             :       tmpl::list<BhMass, BhDimlessSpin, InnerEdgeRadius, MaxPressureRadius,
     221             :                  PolytropicConstant, PolytropicExponent>;
     222           0 :   static constexpr Options::String help = {
     223             :       "Fluid disk orbiting a Kerr black hole."};
     224             : 
     225           0 :   FishboneMoncriefDisk() = default;
     226           0 :   FishboneMoncriefDisk(const FishboneMoncriefDisk& /*rhs*/) = default;
     227           0 :   FishboneMoncriefDisk& operator=(const FishboneMoncriefDisk& /*rhs*/) =
     228             :       default;
     229           0 :   FishboneMoncriefDisk(FishboneMoncriefDisk&& /*rhs*/) = default;
     230           0 :   FishboneMoncriefDisk& operator=(FishboneMoncriefDisk&& /*rhs*/) = default;
     231           0 :   ~FishboneMoncriefDisk() override = default;
     232             : 
     233           0 :   FishboneMoncriefDisk(double bh_mass, double bh_dimless_spin,
     234             :                        double inner_edge_radius, double max_pressure_radius,
     235             :                        double polytropic_constant, double polytropic_exponent);
     236             : 
     237           0 :   auto get_clone() const
     238             :       -> std::unique_ptr<evolution::initial_data::InitialData> override;
     239             : 
     240             :   /// \cond
     241             :   explicit FishboneMoncriefDisk(CkMigrateMessage* msg);
     242             :   using PUP::able::register_constructor;
     243             :   WRAPPED_PUPable_decl_template(FishboneMoncriefDisk);
     244             :   /// \endcond
     245             : 
     246             :   // Eventually, if we implement a gr::Solutions::BoyerLindquist
     247             :   // black hole, the following two functions aren't needed, and the algebra
     248             :   // of the three functions after can be simplified by using the corresponding
     249             :   // lapse, shift, and spatial metric.
     250             :   template <typename DataType>
     251           0 :   DataType sigma(const DataType& r_sqrd, const DataType& sin_theta_sqrd) const;
     252             : 
     253             :   template <typename DataType>
     254           0 :   DataType inv_ucase_a(const DataType& r_sqrd, const DataType& sin_theta_sqrd,
     255             :                        const DataType& delta) const;
     256             : 
     257             :   template <typename DataType>
     258           0 :   DataType four_velocity_t_sqrd(const DataType& r_sqrd,
     259             :                                 const DataType& sin_theta_sqrd) const;
     260             : 
     261             :   template <typename DataType>
     262           0 :   DataType angular_velocity(const DataType& r_sqrd,
     263             :                             const DataType& sin_theta_sqrd) const;
     264             : 
     265             :   template <typename DataType>
     266           0 :   DataType potential(const DataType& r_sqrd,
     267             :                      const DataType& sin_theta_sqrd) const;
     268             : 
     269             :   template <typename DataType>
     270           0 :   using tags =
     271             :       tmpl::append<hydro::grmhd_tags<DataType>,
     272             :                    typename gr::Solutions::SphericalKerrSchild::tags<DataType>>;
     273             : 
     274             :   /// @{
     275             :   /// The variables in Cartesian Spherical-Kerr-Schild coordinates at `(x, t)`
     276             :   template <typename DataType, typename... Tags>
     277           1 :   tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,
     278             :                                          const double /*t*/,
     279             :                                          tmpl::list<Tags...> /*meta*/) const {
     280             :     // Can't store IntermediateVariables as member variable because we need to
     281             :     // be threadsafe.
     282             :     IntermediateVariables<DataType> vars(x);
     283             :     return {std::move(
     284             :         get<Tags>(variables(x, tmpl::list<Tags>{}, make_not_null(&vars))))...};
     285             :   }
     286             : 
     287             :   template <typename DataType, typename Tag>
     288           1 :   tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,
     289             :                                      const double /*t*/,
     290             :                                      tmpl::list<Tag> /*meta*/) const {
     291             :     // Can't store IntermediateVariables as member variable because we need to
     292             :     // be threadsafe.
     293             :     IntermediateVariables<DataType> vars(x);
     294             :     return variables(x, tmpl::list<Tag>{}, make_not_null(&vars));
     295             :   }
     296             :   /// @}
     297             : 
     298             :   // NOLINTNEXTLINE(google-runtime-references)
     299           0 :   void pup(PUP::er& p) override;
     300             : 
     301           0 :   const EquationsOfState::PolytropicFluid<true>& equation_of_state() const {
     302             :     return equation_of_state_;
     303             :   }
     304             : 
     305             :  protected:
     306             :   template <typename DataType>
     307           0 :   auto variables(const tnsr::I<DataType, 3>& x,
     308             :                  tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,
     309             :                  gsl::not_null<IntermediateVariables<DataType>*> vars) const
     310             :       -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;
     311             : 
     312             :   template <typename DataType>
     313           0 :   auto variables(const tnsr::I<DataType, 3>& x,
     314             :                  tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/,
     315             :                  gsl::not_null<IntermediateVariables<DataType>*> vars) const
     316             :       -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;
     317             : 
     318             :   template <typename DataType>
     319           0 :   auto variables(const tnsr::I<DataType, 3>& x,
     320             :                  tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/,
     321             :                  gsl::not_null<IntermediateVariables<DataType>*> vars) const
     322             :       -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;
     323             : 
     324             :   template <typename DataType>
     325           0 :   auto variables(const tnsr::I<DataType, 3>& x,
     326             :                  tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,
     327             :                  gsl::not_null<IntermediateVariables<DataType>*> vars) const
     328             :       -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;
     329             : 
     330             :   template <typename DataType>
     331           0 :   auto variables(const tnsr::I<DataType, 3>& x,
     332             :                  tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/,
     333             :                  gsl::not_null<IntermediateVariables<DataType>*> vars) const
     334             :       -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>>;
     335             : 
     336             :   template <typename DataType>
     337           0 :   auto variables(
     338             :       const tnsr::I<DataType, 3>& x,
     339             :       tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,
     340             :       gsl::not_null<IntermediateVariables<DataType>*> vars) const
     341             :       -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;
     342             : 
     343             :   template <typename DataType>
     344           0 :   auto variables(const tnsr::I<DataType, 3>& x,
     345             :                  tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/,
     346             :                  gsl::not_null<IntermediateVariables<DataType>*> vars) const
     347             :       -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;
     348             : 
     349             :   template <typename DataType>
     350           0 :   auto variables(const tnsr::I<DataType, 3>& x,
     351             :                  tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/,
     352             :                  gsl::not_null<IntermediateVariables<DataType>*> vars) const
     353             :       -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;
     354             : 
     355             :   template <typename DataType>
     356           0 :   auto variables(const tnsr::I<DataType, 3>& x,
     357             :                  tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/,
     358             :                  gsl::not_null<IntermediateVariables<DataType>*> vars) const
     359             :       -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;
     360             : 
     361             :   template <typename DataType>
     362           0 :   auto variables(
     363             :       const tnsr::I<DataType, 3>& x,
     364             :       tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/,
     365             :       gsl::not_null<IntermediateVariables<DataType>*> vars) const
     366             :       -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;
     367             : 
     368             :   // Grab the metric variables
     369             :   template <typename DataType, typename Tag,
     370             :             Requires<not tmpl::list_contains_v<
     371             :                 tmpl::push_back<hydro::grmhd_tags<DataType>,
     372             :                                 hydro::Tags::SpecificEnthalpy<DataType>,
     373             :                                 hydro::Tags::SpatialVelocity<DataType, 3>,
     374             :                                 hydro::Tags::LorentzFactor<DataType>>,
     375             :                 Tag>> = nullptr>
     376           0 :   tuples::TaggedTuple<Tag> variables(
     377             :       const tnsr::I<DataType, 3>& x, tmpl::list<Tag> /*meta*/,
     378             :       gsl::not_null<IntermediateVariables<DataType>*> vars) const {
     379             :     return {get<Tag>(background_spacetime_.variables(
     380             :         x, 0.0, tmpl::list<Tag>{},
     381             :         make_not_null(&vars->sph_kerr_schild_cache)))};
     382             :   }
     383             : 
     384             :   template <typename DataType, typename Func>
     385           0 :   void variables_impl(gsl::not_null<IntermediateVariables<DataType>*> vars,
     386             :                       Func f) const;
     387             : 
     388             :   // Intermediate variables needed to set several of the Fishbone-Moncrief
     389             :   // solution's variables.
     390             : 
     391             :   template <typename DataType>
     392           0 :   struct IntermediateVariables {
     393           0 :     explicit IntermediateVariables(const tnsr::I<DataType, 3>& x);
     394             : 
     395           0 :     DataType r_squared{};
     396           0 :     DataType sin_theta_squared{};
     397             :     gr::Solutions::SphericalKerrSchild::IntermediateVars<DataType,
     398             :                                                          Frame::Inertial>
     399           0 :         sph_kerr_schild_cache =
     400             :             gr::Solutions::SphericalKerrSchild::IntermediateVars<
     401             :                 DataType, Frame::Inertial>(0);
     402             :   };
     403             : 
     404           0 :   friend bool operator==(const FishboneMoncriefDisk& lhs,
     405             :                          const FishboneMoncriefDisk& rhs);
     406           0 :   friend class grmhd::AnalyticData::MagnetizedFmDisk;
     407             : 
     408           0 :   double bh_mass_ = std::numeric_limits<double>::signaling_NaN();
     409           0 :   double bh_spin_a_ = std::numeric_limits<double>::signaling_NaN();
     410           0 :   double inner_edge_radius_ = std::numeric_limits<double>::signaling_NaN();
     411           0 :   double max_pressure_radius_ = std::numeric_limits<double>::signaling_NaN();
     412           0 :   double polytropic_constant_ = std::numeric_limits<double>::signaling_NaN();
     413           0 :   double polytropic_exponent_ = std::numeric_limits<double>::signaling_NaN();
     414           0 :   double angular_momentum_ = std::numeric_limits<double>::signaling_NaN();
     415           0 :   EquationsOfState::PolytropicFluid<true> equation_of_state_{};
     416           0 :   gr::Solutions::SphericalKerrSchild background_spacetime_{};
     417             : };
     418             : 
     419           0 : bool operator!=(const FishboneMoncriefDisk& lhs,
     420             :                 const FishboneMoncriefDisk& rhs);
     421             : }  // namespace Solutions
     422             : }  // namespace RelativisticEuler

Generated by: LCOV version 1.14