SpECTRE Documentation Coverage Report
Current view: top level - Evolution/VariableFixing - FixToAtmosphere.hpp Hit Total Coverage
Commit: fbcce2ed065a8e48da2f38009a84bbfbc0c260ee Lines: 10 103 9.7 %
Date: 2025-11-14 20:55:50
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 <iosfwd>
       8             : #include <limits>
       9             : #include <optional>
      10             : 
      11             : #include "DataStructures/Tensor/TypeAliases.hpp"
      12             : #include "Options/Auto.hpp"
      13             : #include "Options/Context.hpp"
      14             : #include "Options/String.hpp"
      15             : #include "PointwiseFunctions/GeneralRelativity/Tags.hpp"
      16             : #include "PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp"
      17             : #include "PointwiseFunctions/Hydro/Tags.hpp"
      18             : #include "PointwiseFunctions/Hydro/TagsDeclarations.hpp"
      19             : #include "Utilities/Gsl.hpp"
      20             : #include "Utilities/TMPL.hpp"
      21             : 
      22             : /// \cond
      23             : class DataVector;
      24             : 
      25             : namespace Options {
      26             : class Option;
      27             : template <typename T>
      28             : struct create_from_yaml;
      29             : }  // namespace Options
      30             : 
      31             : namespace PUP {
      32             : class er;
      33             : }  // namespace PUP
      34             : /// \endcond
      35             : 
      36             : namespace VariableFixing {
      37             : /// \brief How to apply atmosphere to the reconstructed state.
      38           0 : enum class FixReconstructedStateToAtmosphere : uint8_t {
      39             :   Always,
      40             :   AtDgFdInterfaceOnly,
      41             :   OnFdOnly,
      42             :   Never
      43             : };
      44             : 
      45           0 : std::ostream& operator<<(std::ostream& os,
      46             :                          const FixReconstructedStateToAtmosphere& t);
      47             : }  // namespace VariableFixing
      48             : 
      49             : template <>
      50           0 : struct Options::create_from_yaml<
      51             :     VariableFixing::FixReconstructedStateToAtmosphere> {
      52             :   template <typename Metavariables>
      53           0 :   static VariableFixing::FixReconstructedStateToAtmosphere create(
      54             :       const Options::Option& options) {
      55             :     return create<void>(options);
      56             :   }
      57             : };
      58             : 
      59             : template <>
      60           0 : VariableFixing::FixReconstructedStateToAtmosphere
      61             : Options::create_from_yaml<VariableFixing::FixReconstructedStateToAtmosphere>::
      62             :     create<void>(const Options::Option& options);
      63             : 
      64             : namespace VariableFixing {
      65             : /*!
      66             :  * \ingroup VariableFixingGroup
      67             :  * \brief Fix the primitive variables to an atmosphere in low density regions
      68             :  *
      69             :  * If the rest mass density is below \f$\rho_{\textrm{cutoff}}\f$
      70             :  * (DensityCutoff), it is set to \f$\rho_{\textrm{atm}}\f$
      71             :  * (DensityOfAtmosphere), and the pressure, and specific internal energy (for
      72             :  * one-dimensional equations of state) are adjusted to satisfy the equation of
      73             :  * state.  For a two-dimensional equation of state, the specific internal energy
      74             :  * is set to zero.
      75             :  */
      76             : template <size_t Dim>
      77           1 : class FixToAtmosphere {
      78           0 :   struct Disabled {};
      79           0 :   struct FromEos {};
      80             : 
      81             :  public:
      82             :   /// \brief Rest mass density of the atmosphere
      83           1 :   struct DensityOfAtmosphere {
      84           0 :     using type = double;
      85           0 :     static type lower_bound() { return 0.0; }
      86           0 :     static constexpr Options::String help = {"Density of atmosphere"};
      87             :   };
      88             :   /// \brief Rest mass density at which to impose the atmosphere. Should be
      89             :   /// greater than or equal to the density of the atmosphere.
      90           1 :   struct DensityCutoff {
      91           0 :     using type = double;
      92           0 :     static type lower_bound() { return 0.0; }
      93           0 :     static constexpr Options::String help = {
      94             :         "Density to impose atmosphere at. Must be >= rho_atm"};
      95             :   };
      96             : 
      97             :   /*!
      98             :    * \brief Limit the velocity in and near the atmosphere.
      99             :    *
     100             :    * Let $v_{\max}$ be the maximum magnitude of the
     101             :    * velocity near the atmosphere, which we typically set to $10^{-4}$.
     102             :    * We let $v_{\mathrm{atm}}$ be the maximum magnitude of the velocity
     103             :    * in the atmosphere, which we typically set to $0$. We then define
     104             :    * the maximum magnitude of the spatial velocity to be
     105             :    *
     106             :    * \f{align*}{
     107             :    *   \tilde{v}
     108             :    *   &=\begin{cases}
     109             :    *     v_{\mathrm{atm}}, & \mathrm{if}\; \rho < \rho_{v_-} \   \
     110             :    *     v_{\mathrm{atm}} + \left(v_{\max} - v_{\mathrm{atm}}\right)
     111             :    *     \frac{\rho - \rho_{v_-}}{\rho_{v_+} - \rho_{v_-}},
     112             :    *                       & \mathrm{if}\;\rho_{v_-} \le \rho < \rho_{v_+}
     113             :    *   \end{cases}
     114             :    * \f}
     115             :    *
     116             :    * We then rescale the velocity by
     117             :    *
     118             :    * \f{align*}{
     119             :    *   v^i\to v^i\frac{\tilde{v}}{\sqrt{v^i\gamma_{ij}v^j}}.
     120             :    * \f}
     121             :    */
     122           1 :   struct VelocityLimitingOptions {
     123           0 :     struct AtmosphereMaxVelocity {
     124           0 :       using type = double;
     125           0 :       static constexpr Options::String help = {
     126             :           "The maximum velocity magnitude IN the atmosphere. Typically set to "
     127             :           "0."};
     128             :     };
     129             : 
     130           0 :     struct NearAtmosphereMaxVelocity {
     131           0 :       using type = double;
     132           0 :       static constexpr Options::String help = {
     133             :           "The maximum velocity magnitude NEAR the atmosphere. Typically set "
     134             :           "to 1e-4."};
     135             :     };
     136             : 
     137           0 :     struct AtmosphereDensityCutoff {
     138           0 :       using type = double;
     139           0 :       static constexpr Options::String help = {
     140             :           "The rest mass density cutoff below which the velocity magnitude is "
     141             :           "limited to AtmosphereMaxVelocity. Typically set to "
     142             :           "(10 or 20)*DensityOfAtmosphere."};
     143             :     };
     144             : 
     145           0 :     struct TransitionDensityBound {
     146           0 :       using type = double;
     147           0 :       static constexpr Options::String help = {
     148             :           "The rest mass density above which no velocity limiting is done. "
     149             :           "Between "
     150             :           "this value and AtmosphereDensityCutoff a linear transition in the "
     151             :           "maximum magnitude of the velocity between AtmosphereMaxVelocity and "
     152             :           "NearAtmosphereMaxVelocity is done. Typically set to "
     153             :           "10*AtmosphereDensityCutoff."};
     154             :     };
     155           0 :     using options = tmpl::list<AtmosphereMaxVelocity, NearAtmosphereMaxVelocity,
     156             :                                AtmosphereDensityCutoff, TransitionDensityBound>;
     157           0 :     static constexpr Options::String help = {
     158             :         "Limit the velocity in and near the atmosphere."};
     159             : 
     160             :     // NOLINTNEXTLINE(google-runtime-references)
     161           0 :     void pup(PUP::er& p);
     162             : 
     163           0 :     bool operator==(const VelocityLimitingOptions& rhs) const;
     164           0 :     bool operator!=(const VelocityLimitingOptions& rhs) const;
     165             : 
     166           0 :     double atmosphere_max_velocity{
     167             :         std::numeric_limits<double>::signaling_NaN()};
     168           0 :     double near_atmosphere_max_velocity{
     169             :         std::numeric_limits<double>::signaling_NaN()};
     170           0 :     double atmosphere_density_cutoff{
     171             :         std::numeric_limits<double>::signaling_NaN()};
     172           0 :     double transition_density_bound{
     173             :         std::numeric_limits<double>::signaling_NaN()};
     174             :   };
     175           0 :   struct VelocityLimiting {
     176           0 :     using type = Options::Auto<VelocityLimitingOptions, Disabled>;
     177           0 :     static constexpr Options::String help = VelocityLimitingOptions::help;
     178             :   };
     179             : 
     180             :   /*!
     181             :    * \brief Options for limiting the temperature in the atmosphere by
     182             :    * effectively limiting the polytropic constant, with a generalization for
     183             :    * finite temperature equations of state.
     184             :    *
     185             :    * The basic density limit is fine for a cold EOS, but when a finite
     186             :    * temperature EOS is used the temperature needs to be controlled in the
     187             :    * atmosphere. While the basic bound of
     188             :    * $T\in[T_{\mathrm{min}},T_{\mathrm{max}}]$ is a necessary condition, it is
     189             :    * often not sufficient for stability in the atmosphere. We define lower and
     190             :    * upper bounds $\rho_{\tau_-}$ and $\rho_{\tau_+}$ for $\rho$ where we apply
     191             :    * different limiting behavior when $\rho<\rho_{\tau_-}$,
     192             :    * $\rho\in[\rho_{\tau_-},\rho_{\tau_+}]$, and $\rho>\rho_{\tau_+}$.
     193             :    *
     194             :    * When $\rho<\rho_{\tau_-}$ we set $T=T_{\mathrm{min}}$ if
     195             :    * $|T-T_{\mathrm{min}}|>\epsilon_{\kappa_-} |T|$, otherwise we don't change
     196             :    * $T$. SpEC uses $\epsilon_{\kappa_-}=10^{-3}$.
     197             :    *
     198             :    * When $\rho\in[\rho_{\tau_-},\rho_{\tau_+}]$ we apply a more complicated
     199             :    * algorithm. We define
     200             :    *
     201             :    * \f{align*}{
     202             :    *   \kappa_{\mathrm{max}} =
     203             :    *   1 +
     204             :    *   \epsilon_{\kappa,\mathrm{max}}
     205             :    *   \min\left(\frac{\rho-\rho_{\tau_-}}{\rho_{\tau_+}-\rho_{\tau_-}},1\right)
     206             :    * \f}
     207             :    *
     208             :    * where $\epsilon_{\kappa,\mathrm{max}}\ge0$ (set to 0.01 in SpEC). With
     209             :    * $\kappa_{\mathrm{max}}$ computed we now limit the temperature. We define
     210             :    * $p_{\mathrm{min}}=p(\rho,T_{\mathrm{min}},Y_e)$, $p=p(\rho,T,Y_e)$, and
     211             :    * $p_{\kappa_{\mathrm{max}}}=p_{\mathrm{min}}\kappa_{\mathrm{max}}$. We then
     212             :    * need to find $T$ such that $p(\rho, T, Y_e) = p_{\kappa_{\mathrm{max}}}$.
     213             :    * We do this by finding the root of
     214             :    *
     215             :    * \f{align*}{
     216             :    *   f(T')=p(\rho, T', Y_e) - p_{\kappa_{\mathrm{max}}}.
     217             :    * \f}
     218             :    *
     219             :    * We bracket the temperature $T'\in[T_{\min}, T]$, where $T$ is the
     220             :    * unmodified temperature. We solve the equation using the TOMS748 algorithm
     221             :    * to avoid the need for derivatives of the pressure with respect to the
     222             :    * temperature. If $T-T_{\min}<10^{-13}$ we set $T'=\frac{1}{2}(T+T_{\min})$
     223             :    * to avoid any root find.
     224             :    *
     225             :    * When $\rho>\rho_{\tau_+}$ we optionally can apply the same procedure as in
     226             :    * the previous case, but with
     227             :    * $\kappa_\mathrm{max}=1+\epsilon_{\kappa,\mathrm{max}}$. This is not
     228             :    * typically done.
     229             :    */
     230           1 :   struct KappaLimitingOptions {
     231           0 :     struct DensityLowerBound {
     232           0 :       using type = double;
     233           0 :       static type lower_bound() { return 0.0; }
     234           0 :       static constexpr Options::String help = {
     235             :           "Below this value we set T=T_min if |T-T_min|<EplisonKappaMinus "
     236             :           "|T|. Typically set to about a factor of 20 larger than the "
     237             :           "atmosphere density."};
     238             :     };
     239           0 :     struct EplisonKappaMinus {
     240           0 :       using type = double;
     241           0 :       static constexpr Options::String help = {
     242             :           "Used to limit the temperature in conjunction with "
     243             :           "DensityLowerBound. See that text for the formula. Typically set "
     244             :           "to 1.0e-3."};
     245             :     };
     246           0 :     struct DensityUpperBound {
     247           0 :       using type = double;
     248           0 :       static type lower_bound() { return 0.0; }
     249           0 :       static constexpr Options::String help = {
     250             :           "The density below which we limit the temperature by limiting the "
     251             :           "pressure (at fixed rho, Y_e) to 'p(T)<p_min kappa_max'. Typically "
     252             :           "set a factor of 10 larger than DensityLowerBound."};
     253             :     };
     254           0 :     struct EpsilonKappaMax {
     255           0 :       using type = double;
     256           0 :       static constexpr Options::String help = {
     257             :           "The epsilon factor in the KappaMax computation multiplying the "
     258             :           "transition factor. Typically set to 0.01."};
     259             :     };
     260           0 :     struct MinTemperature {
     261           0 :       using type = Options::Auto<double, FromEos>;
     262           0 :       static constexpr Options::String help = {"The minimum temperature."};
     263             :     };
     264           0 :     struct LimitAboveDensityUpperBound {
     265           0 :       using type = bool;
     266           0 :       static constexpr Options::String help = {
     267             :           "If true then we limit the temperature using the KappaMax procedure "
     268             :           "at all densities above DensityUpperBound, but with "
     269             :           "`KappaMax=1+EplisonKappaMax`. Typically set to False."};
     270             :     };
     271           0 :     using options = tmpl::list<DensityLowerBound, EplisonKappaMinus,
     272             :                                DensityUpperBound, EpsilonKappaMax,
     273             :                                MinTemperature, LimitAboveDensityUpperBound>;
     274           0 :     static constexpr Options::String help = {
     275             :         "If set then we apply a limiting precodure on the temperature near the "
     276             :         "atmosphere based on essentially limiting the polytropic constant in a "
     277             :         "Gamma-law equation of state."};
     278             : 
     279             :     // NOLINTNEXTLINE(google-runtime-references)
     280           0 :     void pup(PUP::er& p);
     281             : 
     282           0 :     bool operator==(const KappaLimitingOptions& rhs) const;
     283           0 :     bool operator!=(const KappaLimitingOptions& rhs) const;
     284             : 
     285           0 :     double density_lower_bound{std::numeric_limits<double>::signaling_NaN()};
     286           0 :     double eplison_kappa_minus{std::numeric_limits<double>::signaling_NaN()};
     287           0 :     double density_upper_bound{std::numeric_limits<double>::signaling_NaN()};
     288           0 :     double epsilon_kappa_max{std::numeric_limits<double>::signaling_NaN()};
     289           0 :     std::optional<double> min_temperature{std::nullopt};
     290           0 :     bool limit_above_density_upper_bound{false};
     291             :   };
     292             :   /// \brief If set then we apply a limiting precodure on the temperature near
     293             :   /// the atmosphere based on essentially limiting the polytropic constant in a
     294             :   /// Gamma-law equation of state.
     295           1 :   struct KappaLimiting {
     296           0 :     using type = Options::Auto<KappaLimitingOptions, Disabled>;
     297           0 :     static constexpr Options::String help = KappaLimitingOptions::help;
     298             :   };
     299             : 
     300           0 :   using options = tmpl::list<DensityOfAtmosphere, DensityCutoff,
     301             :                              VelocityLimiting, KappaLimiting>;
     302           0 :   static constexpr Options::String help = {
     303             :       "If the rest mass density is below DensityCutoff, it is set\n"
     304             :       "to DensityOfAtmosphere, and the pressure, and specific internal energy\n"
     305             :       "(for one-dimensional equations of state) are\n"
     306             :       "adjusted to satisfy the equation of state. For a two-dimensional\n"
     307             :       "equation of state, the specific internal energy is set to zero.\n"
     308             :       "In addition, the spatial velocity is set to zero, and the Lorentz\n"
     309             :       "factor is set to one.\n"};
     310             : 
     311           0 :   FixToAtmosphere(double density_of_atmosphere, double density_cutoff,
     312             :                   std::optional<VelocityLimitingOptions> velocity_limiting,
     313             :                   std::optional<KappaLimitingOptions> kappa_limiting,
     314             :                   const Options::Context& context = {});
     315             : 
     316           0 :   FixToAtmosphere() = default;
     317           0 :   FixToAtmosphere(const FixToAtmosphere& /*rhs*/) = default;
     318           0 :   FixToAtmosphere& operator=(const FixToAtmosphere& /*rhs*/) = default;
     319           0 :   FixToAtmosphere(FixToAtmosphere&& /*rhs*/) = default;
     320           0 :   FixToAtmosphere& operator=(FixToAtmosphere&& /*rhs*/) = default;
     321           0 :   ~FixToAtmosphere() = default;
     322             : 
     323             :   // NOLINTNEXTLINE(google-runtime-references)
     324           0 :   void pup(PUP::er& p);
     325             : 
     326           0 :   using return_tags =
     327             :       tmpl::list<hydro::Tags::RestMassDensity<DataVector>,
     328             :                  hydro::Tags::SpecificInternalEnergy<DataVector>,
     329             :                  hydro::Tags::SpatialVelocity<DataVector, Dim>,
     330             :                  hydro::Tags::LorentzFactor<DataVector>,
     331             :                  hydro::Tags::Pressure<DataVector>,
     332             :                  hydro::Tags::Temperature<DataVector>>;
     333           0 :   using argument_tags = tmpl::list<hydro::Tags::ElectronFraction<DataVector>,
     334             :                                    gr::Tags::SpatialMetric<DataVector, Dim>,
     335             :                                    hydro::Tags::GrmhdEquationOfState>;
     336             : 
     337             :   // for use in `db::mutate_apply`
     338             :   template <size_t ThermodynamicDim>
     339           0 :   void operator()(
     340             :       gsl::not_null<Scalar<DataVector>*> rest_mass_density,
     341             :       gsl::not_null<Scalar<DataVector>*> specific_internal_energy,
     342             :       gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>
     343             :           spatial_velocity,
     344             :       gsl::not_null<Scalar<DataVector>*> lorentz_factor,
     345             :       gsl::not_null<Scalar<DataVector>*> pressure,
     346             :       gsl::not_null<Scalar<DataVector>*> temperature,
     347             :       const Scalar<DataVector>& electron_fraction,
     348             :       const tnsr::ii<DataVector, Dim, Frame::Inertial>& spatial_metric,
     349             :       const EquationsOfState::EquationOfState<true, ThermodynamicDim>&
     350             :           equation_of_state) const;
     351             : 
     352             :   /// @{
     353             :   /// \brief Other algorithmic decisions may depend on the atmosphere treatment
     354             :   /// so provide access to the values.
     355           1 :   double density_of_atmosphere() const { return density_of_atmosphere_; }
     356           1 :   double density_cutoff() const { return density_cutoff_; }
     357           1 :   const std::optional<VelocityLimitingOptions>& velocity_limiting() const {
     358             :     return velocity_limiting_;
     359             :   }
     360           1 :   const std::optional<KappaLimitingOptions>& kappa_limiting() const {
     361             :     return kappa_limiting_;
     362             :   }
     363             :   /// @}
     364             : 
     365             :  private:
     366             :   template <size_t ThermodynamicDim>
     367           0 :   void set_density_to_atmosphere(
     368             :       gsl::not_null<Scalar<DataVector>*> rest_mass_density,
     369             :       gsl::not_null<Scalar<DataVector>*> specific_internal_energy,
     370             :       gsl::not_null<Scalar<DataVector>*> temperature,
     371             :       gsl::not_null<Scalar<DataVector>*> pressure,
     372             :       const Scalar<DataVector>& electron_fraction,
     373             :       const EquationsOfState::EquationOfState<true, ThermodynamicDim>&
     374             :           equation_of_state,
     375             :       size_t grid_index) const;
     376             : 
     377           0 :   void apply_velocity_limit(
     378             :       gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>
     379             :           spatial_velocity,
     380             :       gsl::not_null<Scalar<DataVector>*> lorentz_factor,
     381             :       const Scalar<DataVector>& rest_mass_density,
     382             :       const tnsr::ii<DataVector, Dim, Frame::Inertial>& spatial_metric,
     383             :       size_t grid_index) const;
     384             : 
     385             :   template <size_t ThermodynamicDim>
     386           0 :   bool apply_kappa_limit(
     387             :       gsl::not_null<Scalar<DataVector>*> temperature,
     388             :       const Scalar<DataVector>& rest_mass_density,
     389             :       const Scalar<DataVector>& electron_fraction,
     390             :       const EquationsOfState::EquationOfState<true, ThermodynamicDim>&
     391             :           equation_of_state,
     392             :       size_t grid_index) const;
     393             : 
     394             :   template <size_t SpatialDim>
     395             :   // NOLINTNEXTLINE(readability-redundant-declaration)
     396           0 :   friend bool operator==(const FixToAtmosphere<SpatialDim>& lhs,
     397             :                          const FixToAtmosphere<SpatialDim>& rhs);
     398             : 
     399           0 :   double density_of_atmosphere_{std::numeric_limits<double>::signaling_NaN()};
     400           0 :   double density_cutoff_{std::numeric_limits<double>::signaling_NaN()};
     401           0 :   std::optional<VelocityLimitingOptions> velocity_limiting_{std::nullopt};
     402           0 :   std::optional<KappaLimitingOptions> kappa_limiting_{std::nullopt};
     403             : };
     404             : 
     405             : template <size_t Dim>
     406           0 : bool operator!=(const FixToAtmosphere<Dim>& lhs,
     407             :                 const FixToAtmosphere<Dim>& rhs);
     408             : 
     409             : }  // namespace VariableFixing

Generated by: LCOV version 1.14