SpECTRE Documentation Coverage Report
Current view: top level - PointwiseFunctions/InitialDataUtilities - WithNoise.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 20.8 % 48 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 <memory>
       9              : #include <optional>
      10              : #include <string>
      11              : #include <vector>
      12              : 
      13              : #include "DataStructures/DataVector.hpp"
      14              : #include "DataStructures/Tensor/TypeAliases.hpp"
      15              : #include "Options/Auto.hpp"
      16              : #include "Options/String.hpp"
      17              : #include "PointwiseFunctions/InitialDataUtilities/InitialData.hpp"
      18              : #include "Utilities/Gsl.hpp"
      19              : #include "Utilities/Serialization/CharmPupable.hpp"
      20              : 
      21              : /// \cond
      22              : namespace Options {
      23              : class Option;
      24              : template <typename T>
      25              : struct create_from_yaml;
      26              : }  // namespace Options
      27              : namespace PUP {
      28              : class er;
      29              : }  // namespace PUP
      30              : /// \endcond
      31              : 
      32              : namespace evolution::initial_data {
      33              : 
      34              : /// How the noise amplitude passed to `add_noise_to_tensor` is interpreted.
      35            1 : enum class NoiseAmplitudeType : uint8_t {
      36              :   /// The noise is drawn from \f$[-A,\,A]\f$.
      37              :   Absolute,
      38              :   /// The noise is drawn from \f$[-A M,\,A M]\f$, where \f$M\f$ is the
      39              :   /// maximum absolute value over all stored components and grid points of the
      40              :   /// tensor being perturbed.
      41              :   Relative
      42              : };
      43              : 
      44            0 : std::ostream& operator<<(std::ostream& os, NoiseAmplitudeType value);
      45              : 
      46              : /*!
      47              :  * \brief Add uniform random noise to all stored components of \p tensor.
      48              :  *
      49              :  * Each independent stored component receives noise drawn independently from
      50              :  * \f$\text{Uniform}[-A,\,A]\f$. For `NoiseAmplitudeType::Absolute`,
      51              :  * \f$A\f$ is \p amplitude. For `NoiseAmplitudeType::Relative`, \f$A\f$ is
      52              :  * \p amplitude times the maximum absolute value over all stored components and
      53              :  * grid points of \p tensor, so a tensor that is identically zero receives no
      54              :  * noise. The RNG seed
      55              :  * for component \f$i\f$ is `hash(element_seed, component_offset + i)`, so
      56              :  * calling this function with different \p component_offset values keeps
      57              :  * separate tensor fields independent even when \p element_seed is the same.
      58              :  *
      59              :  * \param tensor Tensor to perturb in place.
      60              :  * \param amplitude Sets \f$\pm A\f$ of the noise interval (absolute or
      61              :  * relative, depending on \p amplitude_type).
      62              :  * \param amplitude_type Whether \p amplitude is absolute or relative to the
      63              :  *   maximum absolute value of \p tensor.
      64              :  * \param element_seed Per-element seed, typically constructed by hashing a
      65              :  *   base seed with the inertial coordinates of the element's first grid point.
      66              :  * \param component_offset Offset added to the component index before hashing,
      67              :  *   used to keep different tensor fields independent.
      68              :  */
      69              : template <typename TensorType>
      70            1 : void add_noise_to_tensor(gsl::not_null<TensorType*> tensor, double amplitude,
      71              :                          NoiseAmplitudeType amplitude_type, size_t element_seed,
      72              :                          size_t component_offset);
      73              : 
      74              : /*!
      75              :  * \brief Combine \p base_seed with the first grid point of \p inertial_coords
      76              :  * to produce a per-element RNG seed.
      77              :  *
      78              :  * Because most domains have elements have distinct first grid points, this
      79              :  * gives independent noise on almost every element even when `base_seed` is the
      80              :  * same.
      81              :  */
      82              : template <size_t Dim>
      83            1 : size_t make_element_seed(
      84              :     size_t base_seed,
      85              :     const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);
      86              : 
      87              : /*!
      88              :  * \brief Wraps any analytic `InitialData` and adds uniform random noise to
      89              :  * selected evolution variables after the inner solution has initialized them.
      90              :  *
      91              :  * \details The inner solution first sets all evolution variables in the normal
      92              :  * way. Then each variable whose `db::tag_name` appears in `Variables` (or
      93              :  * every variable if `All` is listed) has independent noise from
      94              :  * \f$\text{Uniform}[-A,\,A]\f$ added to every grid point and every independent
      95              :  * tensor component. If `AmplitudeType` is `Absolute`, \f$A\f$ is `Amplitude`.
      96              :  * If `AmplitudeType` is `Relative`, \f$A\f$ is `Amplitude` times the maximum
      97              :  * absolute value of the variable on the element, taken over all of its
      98              :  * independent components and grid points. Relative noise is therefore suited
      99              :  * to systems whose variables differ greatly in magnitude. Because the maximum
     100              :  * is computed element by element, \f$A\f$ can differ slightly between
     101              :  * elements, and a variable that is identically zero on an element (e.g.
     102              :  * `Pi` for Minkowski) receives no noise there; use `Absolute` to perturb such
     103              :  * variables.
     104              :  *
     105              :  * Valid variable names are the `db::tag_name`s of the system's evolution
     106              :  * variables. Examples:
     107              :  * - **ScalarWave**: `Psi`, `Pi`, `Phi`
     108              :  * - **GeneralizedHarmonic**: `SpacetimeMetric`, `Pi`, `Phi`
     109              :  *
     110              :  * Different elements receive independent noise because the RNG seed is mixed
     111              :  * with the inertial coordinates of each element's first grid point. Set
     112              :  * `Seed` to a fixed integer for reproducible results.
     113              :  *
     114              :  * \note Only analytic initial data (analytic solutions or analytic data) are
     115              :  * supported as the inner `Solution`.
     116              :  *
     117              :  * \note Nesting `WithNoise` inside `WithNoise` is not supported.
     118              :  *
     119              :  * \note Systems with primitive and conservative variables (e.g.
     120              :  * ValenciaDivClean, NewtonianEuler) are not yet supported and trigger a
     121              :  * `static_assert` in `evolution::Initialization::Actions::SetVariables`.
     122              :  *
     123              :  * \note **GeneralizedHarmonic systems**: the initialization phase
     124              :  * `InitializeInitialDataDependentQuantities` runs
     125              :  * `gh::gauges::SetPiAndPhiFromConstraints` *after* `WithNoise` applies noise.
     126              :  * That action unconditionally overwrites `Phi` with the numerical spatial
     127              :  * derivative of `SpacetimeMetric` and recomputes `Pi` from the gauge source
     128              :  * function and the current geometry. As a result, noise added to `Pi` or `Phi`
     129              :  * is effectively erased. Only noise added to `SpacetimeMetric` persists and
     130              :  * propagates into the other variables: it appears in `Phi` amplified by
     131              :  * roughly one over the grid spacing (due to differentiation), and in `Pi`
     132              :  * through the geometric quantities (lapse, shift) extracted from the metric.
     133              :  *
     134              :  * Example input-file snippet for ScalarWave:
     135              :  * \code{.yaml}
     136              :  * InitialData:
     137              :  *   WithNoise:
     138              :  *     Amplitude: 1.0e-6
     139              :  *     AmplitudeType: Absolute
     140              :  *     Seed: 42
     141              :  *     Variables: [Psi, Pi]
     142              :  *     Solution:
     143              :  *       PlaneWave:
     144              :  *         ...
     145              :  * \endcode
     146              :  */
     147            1 : class WithNoise : public evolution::initial_data::InitialData {
     148              :  public:
     149              :   /// The analytic initial data to wrap.
     150            1 :   struct Solution {
     151            0 :     using type = std::unique_ptr<evolution::initial_data::InitialData>;
     152            0 :     static constexpr Options::String help =
     153              :         "The analytic initial data to wrap. Must be an analytic solution or "
     154              :         "analytic data (not numeric initial data).";
     155              :   };
     156              : 
     157              :   /// Amplitude \f$A\f$ of the noise interval \f$[-A,\,A]\f$.
     158            1 :   struct Amplitude {
     159            0 :     using type = double;
     160            0 :     static constexpr Options::String help =
     161              :         "Amplitude A of the noise interval [-A, A] applied "
     162              :         "independently to each grid point and tensor component.";
     163              :   };
     164              : 
     165              :   /// Whether `Amplitude` is absolute or relative to the maximum absolute
     166              :   /// value of each variable on each element.
     167            1 :   struct AmplitudeType {
     168            0 :     using type = NoiseAmplitudeType;
     169            0 :     static constexpr Options::String help =
     170              :         "'Absolute': noise is drawn from [-A, A] with A = Amplitude. "
     171              :         "'Relative': A = Amplitude * M, where M is the maximum absolute value "
     172              :         "of the variable over all of its components and grid points on the "
     173              :         "element.";
     174              :   };
     175              : 
     176              :   /// Seed for the random number generator.
     177            1 :   struct Seed {
     178            0 :     using type = Options::Auto<size_t, Options::AutoLabel::None>;
     179            0 :     static constexpr Options::String help =
     180              :         "Seed for the noise RNG. Set to 'None' for a random seed chosen at "
     181              :         "runtime (runs will not be reproducible).";
     182              :   };
     183              : 
     184              :   /// Names of evolution variables to perturb, or `[All]` for all.
     185            1 :   struct Variables {
     186            0 :     using type = std::vector<std::string>;
     187            0 :     static constexpr Options::String help =
     188              :         "List of variable names to add noise to, or [All] to noise every "
     189              :         "evolution variable. Valid names depend on the system; see class "
     190              :         "documentation.";
     191              :   };
     192              : 
     193            0 :   using options =
     194              :       tmpl::list<Solution, Amplitude, AmplitudeType, Seed, Variables>;
     195            0 :   static constexpr Options::String help =
     196              :       "Wraps any analytic initial data and adds uniform random noise to "
     197              :       "selected evolution variables after the inner solution sets them.";
     198              : 
     199            0 :   WithNoise() = default;
     200            0 :   WithNoise(const WithNoise& rhs);
     201            0 :   WithNoise& operator=(const WithNoise& rhs);
     202            0 :   WithNoise(WithNoise&&) = default;
     203            0 :   WithNoise& operator=(WithNoise&&) = default;
     204            0 :   ~WithNoise() override = default;
     205              : 
     206            0 :   WithNoise(std::unique_ptr<evolution::initial_data::InitialData> solution,
     207              :             double amplitude, NoiseAmplitudeType amplitude_type,
     208              :             std::optional<size_t> seed, std::vector<std::string> variables);
     209              : 
     210              :   /// \cond
     211              :   explicit WithNoise(CkMigrateMessage* msg);
     212              :   using PUP::able::register_constructor;
     213              :   WRAPPED_PUPable_decl_template(WithNoise);  // NOLINT
     214              :   /// \endcond
     215              : 
     216            0 :   std::unique_ptr<evolution::initial_data::InitialData> get_clone()
     217              :       const override;
     218              : 
     219              :   // NOLINTNEXTLINE(google-runtime-references)
     220            0 :   void pup(PUP::er& p) override;
     221              : 
     222            0 :   const evolution::initial_data::InitialData& solution() const {
     223              :     return *solution_;
     224              :   }
     225              :   /// Unwrap to the inner analytic solution, recursively resolving nested
     226              :   /// `WithNoise` wrappers.
     227            1 :   const evolution::initial_data::InitialData& unwrap() const override {
     228              :     return solution_->unwrap();
     229              :   }
     230            0 :   double amplitude() const { return amplitude_; }
     231            0 :   NoiseAmplitudeType amplitude_type() const { return amplitude_type_; }
     232            0 :   size_t seed() const { return seed_; }
     233            0 :   const std::vector<std::string>& variables() const { return variables_; }
     234              : 
     235            0 :   friend bool operator==(const WithNoise& lhs, const WithNoise& rhs);
     236              : 
     237            0 :   friend bool operator!=(const WithNoise& lhs, const WithNoise& rhs);
     238              : 
     239              :  private:
     240            0 :   std::unique_ptr<evolution::initial_data::InitialData> solution_;
     241            0 :   double amplitude_{0.};
     242            0 :   NoiseAmplitudeType amplitude_type_{NoiseAmplitudeType::Absolute};
     243            0 :   size_t seed_{0};
     244            0 :   std::vector<std::string> variables_;
     245              : };
     246              : 
     247              : }  // namespace evolution::initial_data
     248              : 
     249              : template <>
     250            0 : struct Options::create_from_yaml<evolution::initial_data::NoiseAmplitudeType> {
     251              :   template <typename Metavariables>
     252            0 :   static evolution::initial_data::NoiseAmplitudeType create(
     253              :       const Options::Option& options) {
     254              :     return create<void>(options);
     255              :   }
     256              : };
     257              : 
     258              : template <>
     259            0 : evolution::initial_data::NoiseAmplitudeType
     260              : Options::create_from_yaml<evolution::initial_data::NoiseAmplitudeType>::create<
     261              :     void>(const Options::Option& options);
        

Generated by: LCOV version 2.0-1