SpECTRE Documentation Coverage Report
Current view: top level - PointwiseFunctions/AnalyticSolutions/GeneralRelativity - TrumpetSchwarzschild.hpp Hit Total Coverage
Commit: 923cd4a8ea30f5a5589baa60b0a93e358ca9f8e8 Lines: 1 58 1.7 %
Date: 2025-11-07 19:37:56
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 <limits>
       7             : #include <ostream>
       8             : 
       9             : #include "NumericalAlgorithms/Interpolation/CubicSpline.hpp"
      10             : #include "Options/Context.hpp"
      11             : #include "Options/String.hpp"
      12             : #include "PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp"
      13             : #include "PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp"
      14             : #include "PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp"
      15             : #include "PointwiseFunctions/InitialDataUtilities/InitialData.hpp"
      16             : #include "Utilities/TaggedTuple.hpp"
      17             : 
      18             : /// \cond
      19             : namespace PUP {
      20             : class er;
      21             : }  // namespace PUP
      22             : namespace Tags {
      23             : template <typename Tag>
      24             : struct dt;
      25             : }  // namespace Tags
      26             : /// \endcond
      27             : 
      28             : namespace gr::Solutions {
      29             : 
      30             : /*!
      31             :  * \brief Trumpet Schwarzschild solution in isotropic coordinates
      32             :  *
      33             :  * \details
      34             :  * This solution is a trumpet Schwarzschild black hole in the isotropic
      35             :  * coordinates. It is a time-independent puncture solution in 1+log slicing.
      36             :  * The solution cannot be written down analytically in Schwarzschild
      37             :  * coordinates or isotropic coordinates. Refer to \cite Hannam2008
      38             :  * for equations and details.
      39             :  *
      40             :  * We first set up a source grid in isotropic radial coordinate r, on which
      41             :  * we compute the lapse and Schwarzschild radial coordinate R. The lapse
      42             :  * is computed by solving eq. (54)-(56) using the toms748 algorithm, where
      43             :  * the integrals in eq. (54)-(56) are evaluated by a tanh_sinh integrator.
      44             :  *
      45             :  * Eq. (54) is for \f$\alpha < \alpha_s=0.1\f$:
      46             :  * \f{equation*}{
      47             :  * r(\alpha) = R(\alpha_s)^{(1/\alpha_s)} \exp \left[ -
      48             :  * \int_{\alpha}^{\alpha_s} \frac{1}{\bar{\alpha} R(\bar{\alpha})}
      49             :  * \frac{dR}{d\alpha}(\bar{\alpha}) \: d\bar{\alpha} - C_0 \right],
      50             :  * \f}
      51             :  * where eq. (55) gives
      52             :  * \f{equation*}{
      53             :  * C_0 = \int_{\alpha_s}^{1} \frac{\ln R(\alpha)}{\alpha^2} d\alpha.
      54             :  * \f}
      55             :  * Eq. (56) is for \f$\alpha > \alpha_s=0.1\f$:
      56             :  * \f{equation*}{
      57             :  * r(\alpha) = R(\alpha)^{(1/\alpha)} \exp \left[
      58             :  * \int_{\alpha_s}^{\alpha} \frac{\ln R(\bar{\alpha})}{\bar{\alpha}^2}
      59             :  * d\bar{\alpha} - C_0 \right].
      60             :  * \f}
      61             :  *
      62             :  * A standard Gauss quadratures will fail due to singular
      63             :  * behaviors of the integrands near the integration limits. The Schwarzschild R
      64             :  * is then computed by solving eq. (39) using again the toms748 algorithm.
      65             :  * \f{equation*}{
      66             :  * \alpha^2 = 1 - \frac{2M}{R} + \frac{C(n)^2 e^{2 \alpha / n}
      67             :  * }{R^4}.
      68             :  * \f}
      69             :  * Note that for a value of n near 2 (corresponding to standard 1+log slicing),
      70             :  * eq. (39) has two positive roots for a fixed lapse in [0., 1.).
      71             :  * See the following plot by Mathematica. We choose
      72             :  * \image html trumpet_two_branches_illustration.png
      73             :  * the physical root, i.e. with the correct asymptotic behaviors: R diverges as
      74             :  * \f$\alpha\f$ tends to 1, and R tends to the smaller solution as \f$\alpha\f$
      75             :  * tends to 0.
      76             : 
      77             :  * The physical root is numerically selected by finding the
      78             :  * critical lapse and critical Schwarzschild R, below which we tell the toms748
      79             :  * solver to find a root \f$R\in\f$ [min_schwarzschild_r, crit_schwarzschild_r]
      80             :  * or else we find a root
      81             :  * \f$R\in\f$ [crit_schwarzschild_r, max_schwarzschild_r]. min_schwarzschild_r
      82             :  * is currently selected to be 0., and max_schwarzschild_r is at least as large
      83             :  * as max_isotropic_r (currently 5000M), the maximum coordinate radius we
      84             :  * support for this initial data. In case the solver is asked to find a
      85             :  * Schwarzschild R greater than the max_isotropic_r, we use double the
      86             :  * asymptotic solution from eq. (39), i.e. \f$4/(1-\alpha^2)\f$, as solver
      87             :  * upper bound. This latter upper bound is necessary since the integrator
      88             :  * in eq. (55) needs lapse very close to 1 to converge.
      89             :  *
      90             :  * After acquiring the lapse and Schwarzschild R, we can assemble all the 3+1
      91             :  * quantities in the isotropic coordinates on the source grid.
      92             :  *
      93             :  * Since the user supplies grid points in the Cartesian version of the
      94             :  * isotropic coordinates, we compute a user grid in the isotropic radial
      95             :  * coordinate based on the Cartesian grid, compute the lapse and Schwarzschild
      96             :  * R on the source grid, interpolate to the user grid, and then assemble all
      97             :  * 3+1 quantities on the user grid and transform them back to the Cartesian
      98             :  * grid.
      99             :  *
     100             :  * To insulate our implementation from different mass parameters, we
     101             :  * nondimensionalize the above process using the black hole mass until
     102             :  * the final step of computing 3+1 quantities on the Cartesian grid,
     103             :  * where we restore the correct unit.
     104             :  *
     105             :  * The following are input file options that can be specified:
     106             :  *  - Mass
     107             :  *  - N (the parameter n in the slicing condition eq. (36))
     108             :  *
     109             :  * \note
     110             :  * N=2. is strongly suggested, as this gives a stationary solution
     111             :  * in the standard 1+log slicing. The other values of N near 2.
     112             :  * should work but have not been tested thoroughly. N outside of
     113             :  * [2., 3.] has not been tested at all.
     114             :  *
     115             :  * Some quantities very close to the puncture (<1.e-4 in isotropic radius)
     116             :  * may have larger truncation errors. This is expected since some quantities
     117             :  * such as the determiant of the spatial metric diverges at the puncture.
     118             :  */
     119           1 : class TrumpetSchwarzschild : public MarkAsAnalyticSolution,
     120             :                              public AnalyticSolution<3_st> {
     121             :  private:
     122             :   template <typename DataType>
     123             :   struct IntermediateVars;
     124             : 
     125             :  public:
     126           0 :   static constexpr size_t volume_dim = 3;
     127             : 
     128           0 :   struct Mass {
     129           0 :     using type = double;
     130           0 :     static constexpr Options::String help = {
     131             :         "Mass of the Schwarzschild black hole"};
     132           0 :     static type lower_bound() { return 0.1; };
     133             :   };
     134             : 
     135             :   // currently we have only tested around N=2; for other N eq. (39) may need to
     136             :   // be solved differently
     137           0 :   struct N {
     138           0 :     using type = double;
     139           0 :     static constexpr Options::String help = {
     140             :         "Parameter of the trumpet solution family. N=2. gives"
     141             :         "a stationary solution in standard 1+log slicing."
     142             :         "An N value outside [2., 3.] has not been tested."};
     143           0 :     static type lower_bound() { return 0.; };
     144             :   };
     145             : 
     146           0 :   using options = tmpl::list<Mass, N>;
     147           0 :   static constexpr Options::String help{
     148             :       "Schwarzschild solution in trumpet isotropic coordinates"};
     149             : 
     150           0 :   TrumpetSchwarzschild(double mass, double n,
     151             :                        const Options::Context& context = {});
     152             : 
     153           0 :   TrumpetSchwarzschild() = default;
     154           0 :   TrumpetSchwarzschild(const TrumpetSchwarzschild& /*rhs*/) = default;
     155           0 :   TrumpetSchwarzschild& operator=(const TrumpetSchwarzschild& /*rhs*/) =
     156             :       default;
     157           0 :   TrumpetSchwarzschild(TrumpetSchwarzschild&& /*rhs*/) = default;
     158           0 :   TrumpetSchwarzschild& operator=(TrumpetSchwarzschild&& /*rhs*/) = default;
     159           0 :   ~TrumpetSchwarzschild() = default;
     160             : 
     161           0 :   explicit TrumpetSchwarzschild(CkMigrateMessage* /*msg*/);
     162             : 
     163             :   template <typename DataType>
     164           0 :   using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataType>,
     165             :                                    tmpl::size_t<volume_dim>, Frame::Inertial>;
     166             :   template <typename DataType>
     167           0 :   using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, volume_dim>,
     168             :                                    tmpl::size_t<volume_dim>, Frame::Inertial>;
     169             :   template <typename DataType>
     170           0 :   using DerivSpatialMetric =
     171             :       ::Tags::deriv<gr::Tags::SpatialMetric<DataType, volume_dim>,
     172             :                     tmpl::size_t<volume_dim>, Frame::Inertial>;
     173             : 
     174             :   template <typename DataType>
     175           0 :   using tags = tmpl::list<
     176             :       gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,
     177             :       DerivLapse<DataType>, gr::Tags::Shift<DataType, volume_dim>,
     178             :       ::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>, DerivShift<DataType>,
     179             :       gr::Tags::SpatialMetric<DataType, volume_dim>,
     180             :       ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>,
     181             :       DerivSpatialMetric<DataType>, gr::Tags::SqrtDetSpatialMetric<DataType>,
     182             :       gr::Tags::ExtrinsicCurvature<DataType, volume_dim>,
     183             :       gr::Tags::InverseSpatialMetric<DataType, volume_dim>>;
     184             : 
     185             :   // The user input Cartesian "x" is in isotropic coordinates
     186             :   template <typename DataType, typename... Tags>
     187           0 :   tuples::TaggedTuple<Tags...> variables(
     188             :       const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,
     189             :       tmpl::list<Tags...> /*meta*/) const {
     190             :     const auto& vars =
     191             :         IntermediateVars<DataType>{mass_, n_, x, t, data_on_source_grid_};
     192             :     return {get<Tags>(variables(x, t, vars, tmpl::list<Tags>{}))...};
     193             :   }
     194             : 
     195             :   template <typename DataType, typename... Tags>
     196           0 :   tuples::TaggedTuple<Tags...> variables(
     197             :       const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,
     198             :       const IntermediateVars<DataType>& vars,
     199             :       tmpl::list<Tags...> /*meta*/) const {
     200             :     static_assert(sizeof...(Tags) > 1,
     201             :                   "Unrecognized tag requested.  See the function parameters "
     202             :                   "for the tag.");
     203             :     return {get<Tags>(variables(x, t, vars, tmpl::list<Tags>{}))...};
     204             :   }
     205             : 
     206             :   // NOLINTNEXTLINE(google-runtime-references)
     207           0 :   void pup(PUP::er& p);
     208             : 
     209           0 :   SPECTRE_ALWAYS_INLINE double mass() const { return mass_; }
     210           0 :   SPECTRE_ALWAYS_INLINE double n() const { return n_; }
     211             : 
     212             :  private:
     213             :   template <typename DataType>
     214           0 :   auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,
     215             :                  double t, const IntermediateVars<DataType>& vars,
     216             :                  tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const
     217             :       -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>>;
     218             : 
     219             :   template <typename DataType>
     220           0 :   auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,
     221             :                  double t, const IntermediateVars<DataType>& vars,
     222             :                  tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/)
     223             :       const -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>>;
     224             : 
     225             :   template <typename DataType>
     226           0 :   auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,
     227             :                  double t, const IntermediateVars<DataType>& vars,
     228             :                  tmpl::list<DerivLapse<DataType>> /*meta*/) const
     229             :       -> tuples::TaggedTuple<DerivLapse<DataType>>;
     230             : 
     231             :   template <typename DataType>
     232           0 :   auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,
     233             :                  double t, const IntermediateVars<DataType>& vars,
     234             :                  tmpl::list<gr::Tags::Shift<DataType, volume_dim>> /*meta*/)
     235             :       const -> tuples::TaggedTuple<gr::Tags::Shift<DataType, volume_dim>>;
     236             : 
     237             :   template <typename DataType>
     238           0 :   auto variables(
     239             :       const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,
     240             :       const IntermediateVars<DataType>& vars,
     241             :       tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> /*meta*/)
     242             :       const
     243             :       -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>>;
     244             : 
     245             :   template <typename DataType>
     246           0 :   auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,
     247             :                  double t, const IntermediateVars<DataType>& vars,
     248             :                  tmpl::list<DerivShift<DataType>> /*meta*/) const
     249             :       -> tuples::TaggedTuple<DerivShift<DataType>>;
     250             : 
     251             :   template <typename DataType>
     252           0 :   auto variables(
     253             :       const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,
     254             :       const IntermediateVars<DataType>& vars,
     255             :       tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>> /*meta*/) const
     256             :       -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, volume_dim>>;
     257             : 
     258             :   template <typename DataType>
     259           0 :   auto variables(
     260             :       const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,
     261             :       const IntermediateVars<DataType>& vars,
     262             :       tmpl::list<
     263             :           ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> /*meta*/)
     264             :       const -> tuples::TaggedTuple<
     265             :           ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>>;
     266             : 
     267             :   template <typename DataType>
     268           0 :   auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,
     269             :                  double /*t*/, const IntermediateVars<DataType>& vars,
     270             :                  tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const
     271             :       -> tuples::TaggedTuple<DerivSpatialMetric<DataType>>;
     272             : 
     273             :   template <typename DataType>
     274           0 :   auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,
     275             :                  double /*t*/, const IntermediateVars<DataType>& vars,
     276             :                  tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/)
     277             :       const -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>;
     278             : 
     279             :   template <typename DataType>
     280           0 :   auto variables(
     281             :       const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,
     282             :       const IntermediateVars<DataType>& vars,
     283             :       tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> /*meta*/)
     284             :       const -> tuples::TaggedTuple<
     285             :           gr::Tags::ExtrinsicCurvature<DataType, volume_dim>>;
     286             : 
     287             :   template <typename DataType>
     288           0 :   auto variables(
     289             :       const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,
     290             :       const IntermediateVars<DataType>& vars,
     291             :       tmpl::list<gr::Tags::InverseSpatialMetric<DataType, volume_dim>> /*meta*/)
     292             :       const -> tuples::TaggedTuple<
     293             :           gr::Tags::InverseSpatialMetric<DataType, volume_dim>>;
     294             : 
     295             :   template <typename DataType>
     296           0 :   struct IntermediateVars {
     297           0 :     IntermediateVars(double mass, double n,
     298             :                      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,
     299             :                      double /*t*/,
     300             :                      const std::array<DataVector, 2>& data_on_source_grid);
     301             :     // the lapse, Schwarzschild R, and isotropic r on isotropic grid points
     302           0 :     DataType lapse_on_user_grid{};
     303           0 :     DataType schwarzschild_r_on_user_grid{};
     304           0 :     DataType d_lapse_d_schwarzschild_r_on_user_grid{};
     305           0 :     DataType isotropic_r_on_user_grid{};
     306             : 
     307             :     // cached value to avoid division
     308           0 :     DataType one_over_schwarzschild_r_on_user_grid{};
     309           0 :     DataType one_over_isotropic_r_on_user_grid{};
     310           0 :     double one_over_mass;
     311           0 :     double one_over_n;
     312             :   };
     313             : 
     314           0 :   double mass_{std::numeric_limits<double>::signaling_NaN()};
     315           0 :   double n_{std::numeric_limits<double>::signaling_NaN()};
     316             : 
     317           0 :   const static tnsr::I<DataVector, 1, Frame::Inertial> source_grid_;
     318           0 :   std::array<DataVector, 2> data_on_source_grid_;
     319             : };
     320             : 
     321           0 : bool operator==(const TrumpetSchwarzschild& lhs,
     322             :                 const TrumpetSchwarzschild& rhs);
     323           0 : bool operator!=(const TrumpetSchwarzschild& lhs,
     324             :                 const TrumpetSchwarzschild& rhs);
     325             : }  // namespace gr::Solutions

Generated by: LCOV version 1.14