SpECTRE Documentation Coverage Report
Current view: top level - Elliptic/Systems/Xcts/BoundaryConditions - ApparentHorizon.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 1 44 2.3 %
Date: 2025-12-05 05:03:31
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 <memory>
       8             : #include <optional>
       9             : #include <pup.h>
      10             : #include <string>
      11             : #include <vector>
      12             : 
      13             : #include "DataStructures/Tensor/EagerMath/Magnitude.hpp"
      14             : #include "DataStructures/Tensor/TypeAliases.hpp"
      15             : #include "Domain/Tags.hpp"
      16             : #include "Domain/Tags/FaceNormal.hpp"
      17             : #include "Elliptic/BoundaryConditions/BoundaryCondition.hpp"
      18             : #include "Elliptic/BoundaryConditions/BoundaryConditionType.hpp"
      19             : #include "Elliptic/Systems/Xcts/Geometry.hpp"
      20             : #include "Elliptic/Systems/Xcts/Tags.hpp"
      21             : #include "Options/Auto.hpp"
      22             : #include "Options/Context.hpp"
      23             : #include "Options/String.hpp"
      24             : #include "PointwiseFunctions/GeneralRelativity/Tags.hpp"
      25             : #include "PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp"
      26             : #include "Utilities/Gsl.hpp"
      27             : #include "Utilities/MakeArray.hpp"
      28             : #include "Utilities/Serialization/CharmPupable.hpp"
      29             : #include "Utilities/Serialization/Serialize.hpp"
      30             : #include "Utilities/TMPL.hpp"
      31             : 
      32             : /// \cond
      33             : class DataVector;
      34             : /// \endcond
      35             : 
      36           0 : namespace Xcts::BoundaryConditions {
      37             : 
      38             : /*!
      39             :  * \brief Impose the surface is a quasi-equilibrium apparent horizon.
      40             :  *
      41             :  * These boundary conditions on the conformal factor \f$\psi\f$, the lapse
      42             :  * \f$\alpha\f$ and the shift \f$\beta^i\f$ impose the surface is an apparent
      43             :  * horizon, i.e. that the expansion on the surface vanishes: \f$\Theta=0\f$.
      44             :  * Specifically, we impose:
      45             :  *
      46             :  * \f{align}
      47             :  * \label{eq:ah_psi}
      48             :  * \bar{s}^k\bar{D}_k\psi &= -\frac{\psi^3}{8\alpha}\bar{s}_i\bar{s}_j\left(
      49             :  * (\bar{L}\beta)^{ij} - \bar{u}^{ij}\right)
      50             :  * - \frac{\psi}{4}\bar{m}^{ij}\bar{\nabla}_i\bar{s}_j + \frac{1}{6}K\psi^3
      51             :  * \\
      52             :  * \label{eq:ah_beta}
      53             :  * \beta_\mathrm{excess}^i &= \frac{\alpha}{\psi^2}\bar{s}^i
      54             :  * + \epsilon_{ijk}\Omega^j x^k - \beta_\mathrm{background}^i
      55             :  * \f}
      56             :  *
      57             :  * following section 7.2 of \cite Pfeiffer2005zm, section 12.3.2 of
      58             :  * \cite BaumgarteShapiro or section II.B.1 of \cite Varma2018sqd. In these
      59             :  * equations \f$\bar{s}_i\f$ is the conformal surface normal to the apparent
      60             :  * horizon, \f$\bar{m}^{ij}=\bar{\gamma}^{ij}-\bar{s}^i\bar{s}^j\f$ is the
      61             :  * induced conformal surface metric (denoted \f$\tilde{h}^{ij}\f$ in
      62             :  * \cite Pfeiffer2005zm and \cite Varma2018sqd) and \f$\bar{D}\f$ is the
      63             :  * covariant derivative w.r.t. to the conformal metric \f$\bar{\gamma}_{ij}\f$.
      64             :  * Note that e.g. in \cite Varma2018sqd Eq. (16) appears the surface-normal
      65             :  * \f$s^i\f$, not the _conformal_ surface normal \f$\bar{s}^i = \psi^2 s^i\f$.
      66             :  * To incur a spin on the apparent horizon we can freely choose the rotational
      67             :  * parameters \f$\boldsymbol{\Omega}\f$. Note that for a Kerr solution with
      68             :  * dimensionless spin \f$\boldsymbol{\chi}\f$ the rotational parameters at the
      69             :  * outer horizon are \f$\boldsymbol{\Omega} =
      70             :  * -\frac{\boldsymbol{\chi}}{2r_+}\f$, where \f$r_+ / M = 1 + \sqrt{1 -
      71             :  * \chi^2}\f$ (see e.g. Eq. (8) in \cite Ossokine2015yla). \f$\epsilon_{ijk}\f$
      72             :  * is the flat-space Levi-Civita symbol.
      73             :  *
      74             :  * Note that the quasi-equilibrium conditions don't restrict the boundary
      75             :  * condition for the lapse. The choice for the lapse boundary condition is made
      76             :  * by the `Lapse` input-file options. Currently implemented choices are:
      77             :  * - A zero von-Neumann boundary condition:
      78             :  *   \f$\bar{s}^k\bar{D}_k(\alpha\psi) = 0\f$
      79             :  * - A Dirichlet boundary condition imposing the lapse of an analytic solution.
      80             :  *
      81             :  * \par Negative-expansion boundary conditions:
      82             :  * This class also supports negative-expansion boundary conditions following
      83             :  * section II.B.2 of \cite Varma2018sqd by taking a Kerr solution as additional
      84             :  * parameter and computing its expansion at the excision surface. Choosing an
      85             :  * excision surface _within_ the apparent horizon of the Kerr solution will
      86             :  * result in a negative expansion that is added to the boundary condition for
      87             :  * the conformal factor. Therefore, the excision surface will lie within an
      88             :  * apparent horizon. Specifically, we add the quantity
      89             :  * \f$\frac{\psi^3}{4}\Theta_\mathrm{Kerr}\f$ to (\f$\ref{eq:ah_psi}\f$), where
      90             :  * \f$\Theta_\mathrm{Kerr}\f$ is the expansion of the specified Kerr solution at
      91             :  * the excision surface, and we add the quantity \f$\epsilon = s_i
      92             :  * \beta_\mathrm{Kerr}^i - \alpha_\mathrm{Kerr}\f$ to the orthogonal part
      93             :  * \f$s_i\beta_\mathrm{excess}^i\f$ of (\f$\ref{eq:ah_beta}\f$).
      94             :  *
      95             :  * \note When negative-expansion boundary conditions are selected, the Kerr
      96             :  * solution gets evaluated at every boundary-condition application. This may
      97             :  * turn out to incur a significant computational cost, in which case a future
      98             :  * optimization might be to pre-compute the negative-expansion quantities at
      99             :  * initialization time and store them in the DataBox.
     100             :  */
     101             : template <Xcts::Geometry ConformalGeometry>
     102           1 : class ApparentHorizon
     103             :     : public elliptic::BoundaryConditions::BoundaryCondition<3> {
     104             :  private:
     105           0 :   using Base = elliptic::BoundaryConditions::BoundaryCondition<3>;
     106             : 
     107             :  public:
     108           0 :   static constexpr Options::String help =
     109             :       "Impose the boundary is a quasi-equilibrium apparent horizon.";
     110             : 
     111           0 :   struct Center {
     112           0 :     using type = std::array<double, 3>;
     113           0 :     static constexpr Options::String help =
     114             :         "The center of the excision surface representing the apparent-horizon "
     115             :         "surface";
     116             :   };
     117           0 :   struct Rotation {
     118           0 :     using type = std::array<double, 3>;
     119           0 :     static constexpr Options::String help =
     120             :         "The rotational parameters 'Omega' on the surface, which parametrize "
     121             :         "the spin of the black hole. The rotational parameters enter the "
     122             :         "Dirichlet boundary conditions for the shift in a term "
     123             :         "'Omega x (r - Center)', where 'r' are the coordinates on the surface.";
     124             :   };
     125           0 :   struct Lapse {
     126           0 :     using type =
     127             :         Options::Auto<std::unique_ptr<elliptic::analytic_data::InitialGuess>>;
     128           0 :     static constexpr Options::String help =
     129             :         "Specify an analytic solution or a superposed binary to impose a "
     130             :         "Dirichlet condition on the lapse. The analytic solution will be "
     131             :         "evaluated at coordinates centered at the apparent horizon. "
     132             :         "Alternatively, set this option to 'None' "
     133             :         "to impose a zero von-Neumann boundary condition on the lapse. Note "
     134             :         "that the latter will not result in the standard Kerr-Schild slicing "
     135             :         "for a single black hole.";
     136             :   };
     137           0 :   struct NegativeExpansion {
     138           0 :     using type =
     139             :         Options::Auto<std::unique_ptr<elliptic::analytic_data::InitialGuess>,
     140             :                       Options::AutoLabel::None>;
     141           0 :     static constexpr Options::String help =
     142             :         "Specify an analytic solution to impose its expansion at the excision "
     143             :         "surface. The analytic solution will be evaluated at coordinates "
     144             :         "centered at the apparent horizon. "
     145             :         "If the excision surface lies within the solution's "
     146             :         "apparent horizon, the imposed expansion will be negative and thus the "
     147             :         "excision surface will lie within an apparent horizon. Alternatively, "
     148             :         "set this option to 'None' to impose the expansion is zero at the "
     149             :         "excision surface, meaning the excision surface _is_ an apparent "
     150             :         "horizon.";
     151             :   };
     152             : 
     153           0 :   using options = tmpl::list<Center, Rotation, Lapse, NegativeExpansion>;
     154             : 
     155           0 :   ApparentHorizon() = default;
     156           0 :   ApparentHorizon(const ApparentHorizon&) = delete;
     157           0 :   ApparentHorizon& operator=(const ApparentHorizon&) = delete;
     158           0 :   ApparentHorizon(ApparentHorizon&&) = default;
     159           0 :   ApparentHorizon& operator=(ApparentHorizon&&) = default;
     160           0 :   ~ApparentHorizon() = default;
     161             : 
     162             :   /// \cond
     163             :   explicit ApparentHorizon(CkMigrateMessage* m) : Base(m) {}
     164             :   using PUP::able::register_constructor;
     165             :   WRAPPED_PUPable_decl_template(ApparentHorizon);
     166             :   /// \endcond
     167             : 
     168           0 :   std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()
     169             :       const override {
     170             :     return std::make_unique<ApparentHorizon>(
     171             :         center_, rotation_,
     172             :         solution_for_lapse_.has_value()
     173             :             ? std::make_optional(
     174             :                   deserialize<
     175             :                       std::unique_ptr<elliptic::analytic_data::InitialGuess>>(
     176             :                       serialize(solution_for_lapse_.value()).data()))
     177             :             : std::nullopt,
     178             :         solution_for_negative_expansion_.has_value()
     179             :             ? std::make_optional(deserialize<std::unique_ptr<
     180             :                                      elliptic::analytic_data::InitialGuess>>(
     181             :                   serialize(solution_for_negative_expansion_.value()).data()))
     182             :             : std::nullopt);
     183             :   }
     184             : 
     185           0 :   ApparentHorizon(
     186             :       std::array<double, 3> center, std::array<double, 3> rotation,
     187             :       std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>
     188             :           solution_for_lapse,
     189             :       std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>
     190             :           solution_for_negative_expansion,
     191             :       const Options::Context& context = {});
     192             : 
     193           0 :   const std::array<double, 3>& center() const { return center_; }
     194           0 :   const std::array<double, 3>& rotation() const { return rotation_; }
     195             :   const std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>&
     196           0 :   solution_for_lapse() const {
     197             :     return solution_for_lapse_;
     198             :   }
     199             :   const std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>&
     200           0 :   solution_for_negative_expansion() const {
     201             :     return solution_for_negative_expansion_;
     202             :   }
     203             : 
     204           0 :   std::vector<elliptic::BoundaryConditionType> boundary_condition_types()
     205             :       const override {
     206             :     return {// Conformal factor
     207             :             elliptic::BoundaryConditionType::Neumann,
     208             :             // Lapse times conformal factor
     209             :             this->solution_for_lapse_.has_value()
     210             :                 ? elliptic::BoundaryConditionType::Dirichlet
     211             :                 : elliptic::BoundaryConditionType::Neumann,
     212             :             // Shift
     213             :             elliptic::BoundaryConditionType::Dirichlet,
     214             :             elliptic::BoundaryConditionType::Dirichlet,
     215             :             elliptic::BoundaryConditionType::Dirichlet};
     216             :   }
     217             : 
     218           0 :   using argument_tags = tmpl::flatten<tmpl::list<
     219             :       domain::Tags::FaceNormal<3>,
     220             :       ::Tags::deriv<domain::Tags::UnnormalizedFaceNormal<3>, tmpl::size_t<3>,
     221             :                     Frame::Inertial>,
     222             :       domain::Tags::UnnormalizedFaceNormalMagnitude<3>,
     223             :       domain::Tags::Coordinates<3, Frame::Inertial>,
     224             :       gr::Tags::TraceExtrinsicCurvature<DataVector>,
     225             :       Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,
     226             :       Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,
     227             :                                                               Frame::Inertial>,
     228             :       tmpl::conditional_t<ConformalGeometry == Xcts::Geometry::Curved,
     229             :                           tmpl::list<Tags::InverseConformalMetric<
     230             :                                          DataVector, 3, Frame::Inertial>,
     231             :                                      Tags::ConformalChristoffelSecondKind<
     232             :                                          DataVector, 3, Frame::Inertial>>,
     233             :                           tmpl::list<>>>>;
     234           0 :   using volume_tags = tmpl::list<>;
     235             : 
     236           0 :   void apply(
     237             :       gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,
     238             :       gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor_minus_one,
     239             :       gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,
     240             :       gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,
     241             :       gsl::not_null<Scalar<DataVector>*>
     242             :           n_dot_lapse_times_conformal_factor_gradient,
     243             :       gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_longitudinal_shift_excess,
     244             :       const tnsr::i<DataVector, 3>& deriv_conformal_factor,
     245             :       const tnsr::i<DataVector, 3>& deriv_lapse_times_conformal_factor,
     246             :       const tnsr::iJ<DataVector, 3>& deriv_shift_excess,
     247             :       const tnsr::i<DataVector, 3>& face_normal,
     248             :       const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,
     249             :       const Scalar<DataVector>& face_normal_magnitude,
     250             :       const tnsr::I<DataVector, 3>& x,
     251             :       const Scalar<DataVector>& extrinsic_curvature_trace,
     252             :       const tnsr::I<DataVector, 3>& shift_background,
     253             :       const tnsr::II<DataVector, 3>& longitudinal_shift_background) const;
     254             : 
     255           0 :   void apply(
     256             :       gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,
     257             :       gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor_minus_one,
     258             :       gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,
     259             :       gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,
     260             :       gsl::not_null<Scalar<DataVector>*>
     261             :           n_dot_lapse_times_conformal_factor_gradient,
     262             :       gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_longitudinal_shift_excess,
     263             :       const tnsr::i<DataVector, 3>& deriv_conformal_factor,
     264             :       const tnsr::i<DataVector, 3>& deriv_lapse_times_conformal_factor,
     265             :       const tnsr::iJ<DataVector, 3>& deriv_shift_excess,
     266             :       const tnsr::i<DataVector, 3>& face_normal,
     267             :       const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,
     268             :       const Scalar<DataVector>& face_normal_magnitude,
     269             :       const tnsr::I<DataVector, 3>& x,
     270             :       const Scalar<DataVector>& extrinsic_curvature_trace,
     271             :       const tnsr::I<DataVector, 3>& shift_background,
     272             :       const tnsr::II<DataVector, 3>& longitudinal_shift_background,
     273             :       const tnsr::II<DataVector, 3>& inv_conformal_metric,
     274             :       const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) const;
     275             : 
     276           0 :   using argument_tags_linearized = tmpl::flatten<tmpl::list<
     277             :       ::Tags::Normalized<
     278             :           domain::Tags::UnnormalizedFaceNormal<3, Frame::Inertial>>,
     279             :       ::Tags::deriv<domain::Tags::UnnormalizedFaceNormal<3, Frame::Inertial>,
     280             :                     tmpl::size_t<3>, Frame::Inertial>,
     281             :       ::Tags::Magnitude<
     282             :           domain::Tags::UnnormalizedFaceNormal<3, Frame::Inertial>>,
     283             :       domain::Tags::Coordinates<3, Frame::Inertial>,
     284             :       gr::Tags::TraceExtrinsicCurvature<DataVector>,
     285             :       Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,
     286             :                                                               Frame::Inertial>,
     287             :       Tags::ConformalFactorMinusOne<DataVector>,
     288             :       Tags::LapseTimesConformalFactorMinusOne<DataVector>,
     289             :       ::Tags::NormalDotFlux<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>,
     290             :       tmpl::conditional_t<ConformalGeometry == Xcts::Geometry::Curved,
     291             :                           tmpl::list<Tags::InverseConformalMetric<
     292             :                                          DataVector, 3, Frame::Inertial>,
     293             :                                      Tags::ConformalChristoffelSecondKind<
     294             :                                          DataVector, 3, Frame::Inertial>>,
     295             :                           tmpl::list<>>>>;
     296           0 :   using volume_tags_linearized = tmpl::list<>;
     297             : 
     298           0 :   void apply_linearized(
     299             :       gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,
     300             :       gsl::not_null<Scalar<DataVector>*>
     301             :           lapse_times_conformal_factor_correction,
     302             :       gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,
     303             :       gsl::not_null<Scalar<DataVector>*>
     304             :           n_dot_conformal_factor_gradient_correction,
     305             :       gsl::not_null<Scalar<DataVector>*>
     306             :           n_dot_lapse_times_conformal_factor_gradient_correction,
     307             :       gsl::not_null<tnsr::I<DataVector, 3>*>
     308             :           n_dot_longitudinal_shift_excess_correction,
     309             :       const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,
     310             :       const tnsr::i<DataVector, 3>&
     311             :           deriv_lapse_times_conformal_factor_correction,
     312             :       const tnsr::iJ<DataVector, 3>& deriv_shift_excess_correction,
     313             :       const tnsr::i<DataVector, 3>& face_normal,
     314             :       const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,
     315             :       const Scalar<DataVector>& face_normal_magnitude,
     316             :       const tnsr::I<DataVector, 3>& x,
     317             :       const Scalar<DataVector>& extrinsic_curvature_trace,
     318             :       const tnsr::II<DataVector, 3>& longitudinal_shift_background,
     319             :       const Scalar<DataVector>& conformal_factor_minus_one,
     320             :       const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,
     321             :       const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess) const;
     322             : 
     323           0 :   void apply_linearized(
     324             :       gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,
     325             :       gsl::not_null<Scalar<DataVector>*>
     326             :           lapse_times_conformal_factor_correction,
     327             :       gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,
     328             :       gsl::not_null<Scalar<DataVector>*>
     329             :           n_dot_conformal_factor_gradient_correction,
     330             :       gsl::not_null<Scalar<DataVector>*>
     331             :           n_dot_lapse_times_conformal_factor_gradient_correction,
     332             :       gsl::not_null<tnsr::I<DataVector, 3>*>
     333             :           n_dot_longitudinal_shift_excess_correction,
     334             :       const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,
     335             :       const tnsr::i<DataVector, 3>&
     336             :           deriv_lapse_times_conformal_factor_correction,
     337             :       const tnsr::iJ<DataVector, 3>& deriv_shift_excess_correction,
     338             :       const tnsr::i<DataVector, 3>& face_normal,
     339             :       const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,
     340             :       const Scalar<DataVector>& face_normal_magnitude,
     341             :       const tnsr::I<DataVector, 3>& x,
     342             :       const Scalar<DataVector>& extrinsic_curvature_trace,
     343             :       const tnsr::II<DataVector, 3>& longitudinal_shift_background,
     344             :       const Scalar<DataVector>& conformal_factor_minus_one,
     345             :       const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,
     346             :       const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess,
     347             :       const tnsr::II<DataVector, 3>& inv_conformal_metric,
     348             :       const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) const;
     349             : 
     350             :   // NOLINTNEXTLINE(google-runtime-references)
     351           0 :   void pup(PUP::er& p) override;
     352             : 
     353             :  private:
     354           0 :   std::array<double, 3> center_ =
     355             :       make_array<3>(std::numeric_limits<double>::signaling_NaN());
     356           0 :   std::array<double, 3> rotation_ =
     357             :       make_array<3>(std::numeric_limits<double>::signaling_NaN());
     358             :   std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>
     359           0 :       solution_for_lapse_{};
     360             :   std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>
     361           0 :       solution_for_negative_expansion_{};
     362             : };
     363             : 
     364             : }  // namespace Xcts::BoundaryConditions

Generated by: LCOV version 1.14