SpECTRE Documentation Coverage Report
Current view: top level - Evolution/VariableFixing - FixToAtmosphere.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 9.7 % 103 10
Test Date: 2026-10-22 17:29:42
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 2.0-1