SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/LinearOperators - PowerMonitors.hpp Hit Total Coverage
Commit: ecb8a275e1aebab77dcce48a5e098ed4e486ab4b Lines: 12 15 80.0 %
Date: 2026-08-22 01:05:40
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 <array>
       7             : #include <cstddef>
       8             : #include <limits>
       9             : 
      10             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      11             : #include "Utilities/Gsl.hpp"
      12             : #include "Utilities/TMPL.hpp"
      13             : 
      14             : /// \cond
      15             : class DataVector;
      16             : /// \endcond
      17             : 
      18             : /*!
      19             :  * \brief Items for assessing truncation error in spectral methods.
      20             :  */
      21           1 : namespace PowerMonitors {
      22             : 
      23             : /// @{
      24             : /*!
      25             :  * \ingroup SpectralGroup
      26             :  * \brief Returns array of power monitors in each spatial dimension.
      27             :  *
      28             :  * Computed following Sec. 5.1 of Ref. \cite Szilagyi2014fna.
      29             :  * For example, in the x dimension (indexed by \f$ k_0 \f$), we compute
      30             :  *
      31             :  * \f{align*}{
      32             :  *  P_{k_0}[\psi] = \sqrt{ \frac{1}{N_1 N_2}
      33             :  *   \sum_{k_1,k_2} \left| C_{k_0,k_1,k_2} \right|^2} ,
      34             :  * \f}
      35             :  *
      36             :  * where \f$ C_{k_0,k_1,k_2}\f$ are the modal coefficients
      37             :  * of variable \f$ \psi \f$.
      38             :  *
      39             :  * For dimensions using a Fourier basis, the cosine and sine power for each
      40             :  * wavenumber \f$ k \f$ are combined via \f$ P_k = \sqrt{P_{\cos,k}^2 +
      41             :  * P_{\sin,k}^2} \f$, so the output size for a Fourier dimension of \f$ N \f$
      42             :  * points is \f$ N/2 + 1 \f$ (integer division) rather than \f$ N \f$.
      43             :  *
      44             :  */
      45             : template <typename VectorType, size_t Dim>
      46           1 : void power_monitors(gsl::not_null<std::array<DataVector, Dim>*> result,
      47             :                     const VectorType& u, const Mesh<Dim>& mesh);
      48             : 
      49             : template <typename VectorType, size_t Dim>
      50           1 : std::array<DataVector, Dim> power_monitors(const VectorType& u,
      51             :                                            const Mesh<Dim>& mesh);
      52             : /// @}
      53             : 
      54             : /// @{
      55             : /*!
      56             :  * \ingroup SpectralGroup
      57             :  * \brief Compute the relative truncation error.
      58             :  *
      59             :  * The negative logarithm of this quantity is defined by Eqs. (57) and
      60             :  * (58) of Ref. \cite Szilagyi2014fna, i.e.,
      61             :  *
      62             :  * \f{align*}{
      63             :  *  \mathcal{T}\left[P_k\right] = \log_{10} \max \left(P_0, P_1\right)
      64             :  *   - \dfrac{\sum_{j=0}^{j_{\text{max}, k}} \log_{10} \left(P_j\right) w_j}
      65             :  *   {\sum_{j=0}^{j_{\text{max}, k}} w_j} , \f}
      66             :  *
      67             :  * with weights
      68             :  *
      69             :  * \f{align*}{
      70             :  *  w_j = \exp\left[ - \left(j - j_{\text{max}, k}
      71             :  *          + \dfrac{1}{2}\right)^2 \right] .
      72             :  * \f}
      73             :  *
      74             :  * where \f$ j_{\text{max}, k}  = N_k - 1 \f$ and  \f$ N_k \f$ is the number of
      75             :  * modes or gridpoints in dimension k. Here the second term is a weighted
      76             :  * average with larger weights toward the highest modes.
      77             :  *
      78             :  * \note Modes below a cutoff of $100 \epsilon \mathrm{max}_k(P_k)$ are ignored
      79             :  * in the weighted average, where $\epsilon$ is the machine epsilon. This
      80             :  * ensures that we don't underestimate the truncation error if some modes are
      81             :  * zero (e.g. by symmetry). Furthermore, if the last two or more modes are zero,
      82             :  * we assume that the function is represented exactly and return a relative
      83             :  * truncation error of zero.
      84             :  *
      85             :  * \details The number of modes (`num_modes_to_use`) argument needs to be less
      86             :  * or equal than the total number of power monitors (`power_monitor.size()`).
      87             :  * In contrast with Ref. \cite Szilagyi2014fna, here we index the modes starting
      88             :  * from zero.
      89             :  *
      90             :  */
      91           1 : double relative_truncation_error(const DataVector& power_monitor,
      92             :                                  size_t num_modes_to_use);
      93             : /// @}
      94             : 
      95             : /*!
      96             :  * \brief The relative truncation error in each logical direction of the grid
      97             :  *
      98             :  * This overload is intended for visualization purposes only. It takes a tensor
      99             :  * component as input, so it can be used as a kernel to post-process volume data
     100             :  * with Python bindings (see `TransformVolumeData.py`).
     101             :  */
     102             : template <typename VectorType, size_t Dim>
     103           1 : std::array<double, Dim> relative_truncation_error(
     104             :     const VectorType& tensor_component, const Mesh<Dim>& mesh);
     105             : 
     106             : /// @{
     107             : /*!
     108             :  * \ingroup SpectralGroup
     109             :  * \brief Returns an estimate of the absolute truncation error in each
     110             :  * dimension.
     111             :  *
     112             :  * The estimate of the numerical error is given by
     113             :  *
     114             :  * \f{align*}{
     115             :  *  \mathcal{E}\left[P_k\right] = u_\mathrm{max} \times 10^{- \mathcal{T}[P_k]},
     116             :  * \f}
     117             :  *
     118             :  * where \f$ u_\mathrm{max} = \mathrm{max} |u|\f$ in the corresponding element
     119             :  * and \f$ \mathcal{T}[P_k] \f$ is the relative error estimate
     120             :  * computed from the power monitors \f$ P_k \f$.
     121             :  *
     122             :  * \warning This estimate is intended for visualization purposes only.
     123             :  */
     124             : template <typename VectorType, size_t Dim>
     125           1 : std::array<double, Dim> absolute_truncation_error(
     126             :     const VectorType& tensor_component, const Mesh<Dim>& mesh);
     127             : /// @}
     128             : 
     129             : /// Holds convergence rate and pile up modes of a power monitor
     130           1 : struct ConvergenceInfo {
     131           0 :   double convergence_rate{std::numeric_limits<double>::signaling_NaN()};
     132           0 :   double number_of_pile_up_modes{std::numeric_limits<double>::signaling_NaN()};
     133             : };
     134             : 
     135             : /*!
     136             :  * \ingroup SpectralGroup
     137             :  * \brief Returns the convergence rate and the number of pile up modes of a
     138             :  * power monitor as a ConvergenceInfo.
     139             :  *
     140             :  * \details Computes the convergence rate of a power monitor as a weighted
     141             :  * average of slopes measured using different subsets of spectral modes
     142             :  * in the power monitor. Equation (53) of \cite Szilagyi2014fna gives
     143             :  * the convergence rate $\mathcal{C}$ in terms of a power monitor $P_k$ as
     144             :  * \begin{equation}
     145             :  * \mathcal{C}(P_k) = -\frac{\sum_{k_1=0}^2\sum_{k_2=\tilde{k}_1}^{\tilde{N}-1}
     146             :  *   \frac{\mathcal{S}(k_1,k_2)}{\epsilon + \mathcal{E}(k_1,k_2)}}{
     147             :  *   \sum_{k_1=0}^2\sum_{k_2=\tilde{k}_1}^{\tilde{N}-1}
     148             :  *   \frac{1}{\epsilon + \mathcal{E}(k_1,k_2)}}.
     149             :  * \end{equation}
     150             :  * Here, $\mathcal{S}(k_1,k_2)$ is the slope of a linear regression fit of
     151             :  * $\log_{10}(P_k)$ with $k$ satisfying $k_1\leq k \leq k_2$,
     152             :  * $\mathcal{E}(k_1,k_2)$ is the error of the slope in that fit,
     153             :  * $\epsilon=\max\left(10^{-3}\max\left(\mathcal{E}(k_1,k_2)\right),
     154             :  * 10^{-15}\right)$ is a small number to avoid dividing by zero in the event the
     155             :  * fit errors vanish,
     156             :  * $\max\left(\mathcal{E}(k_1,k_2)\right)$ is the maximum fit error of each
     157             :  * fit whose slope is included in the summation,
     158             :  * $\tilde{k}_1 = \min\left(k_1+4,\tilde{N}-1\right)$,
     159             :  * $\tilde{N} = N-N_f$, $N$ is the number of modes in the
     160             :  * power monitor, and the highest $N_f$ modes are filtered. Note that the
     161             :  * way $\epsilon$ is defined is so that it matches SpEC's definition, while
     162             :  * also ensuring that it is nonzero even if the error in the slope fit is
     163             :  * exactly zero.
     164             :  *
     165             :  * Also computes the number of pile up modes in a power monitor. Pile up
     166             :  * modes are modes where the power is no longer converging at the overall
     167             :  * convergence rate. Following Eq. (56) of \cite Szilagyi2014fna, the number of
     168             :  * pile up modes $\mathcal{P}$ is defined as
     169             :  * \begin{equation}
     170             :  * \mathcal{P}(P_k) = \sum_{j=2}^{\tilde{N}-2}
     171             :  * \exp\left[-32\left(\frac{\tilde{\mathcal{C}}_j}
     172             :  * {\mathcal{C}(P_k)}\right)^2\right],
     173             :  * \end{equation}
     174             :  * where $\mathcal{C}(P_k)$ is the convergence rate of the power monitor $P_k$,
     175             :  * the local convergence rate $\tilde{\mathcal{C}}_j$ of mode $j$ is
     176             :  * \begin{equation}
     177             :  * \tilde{\mathcal{C}}_j = -\mathcal{S}(j,\min(\tilde{N}-1,j+4)),
     178             :  * \end{equation}
     179             :  * $\mathcal{S}(k_1,k_2)$ is the slope of a linear regression fit of
     180             :  * $\log_{10}(P_k)$ with $k$ satisfying $k_1\leq k \leq k_2$,
     181             :  * $\tilde{N} = N-N_f$, $N$ is the number of modes in the
     182             :  * power monitor, and the highest $N_f$ modes are filtered.
     183             :  * The motivation of this definition is the following: if the local
     184             :  * convergence rate $\tilde{\mathcal{C}}_j$ is comparable to the overall
     185             :  * convergence rate $\mathcal{C}(P_k)$, then the $j^{\rm th}$ term in the
     186             :  * summation becomes $\approx \exp(-32) \approx 10^{-14}$, while if
     187             :  * $\tilde{\mathcal{C}}_j \ll \mathcal{C}(P_k)$, then the $j^{\rm th}$ term in
     188             :  * the summation is $\approx \exp(0) = 1$. Note that the coefficient value 32
     189             :  * is chosen to agree with SpEC.
     190             :  * \note The summation goes up to $\tilde{N}-2$ so that there is it least one
     191             :  * larger unfiltered mode for use in computing the slope. The highest mode
     192             :  * used when computing the slope is the highest unfiltered mode, $\tilde{N}-1$.
     193             :  * Mode numbers in the power monitor are zero based. These choices are off by
     194             :  * one vs. Eqs. (55) and (56) of \cite Szilagyi2014fna, because those formulas
     195             :  * apparently assume one-based indexing.
     196             :  * \param power_monitor The power monitor.
     197             :  * \param number_of_filtered_modes How many of the highest modes of the
     198             :  * power monitor are filtered (default 0).
     199             :  */
     200           1 : ConvergenceInfo convergence_rate_and_number_of_pile_up_modes(
     201             :     const DataVector& power_monitor, size_t number_of_filtered_modes = 0);
     202             : 
     203             : /// @{
     204             : /*!
     205             :  * \brief Return the radial power monitor for a tensor component on a
     206             :  * spherical shell.
     207             :  *
     208             :  * The mesh dimensions are assumed to be ordered `(radial, theta, phi)`. The
     209             :  * radial grid points are contiguous, so each angular point supplies one
     210             :  * radial slice to the one-dimensional modal transform.
     211             :  */
     212           1 : void spherical_shell_radial_power_monitor(gsl::not_null<DataVector*> result,
     213             :                                           const DataVector& tensor_component,
     214             :                                           const Mesh<3>& mesh);
     215             : 
     216           1 : DataVector spherical_shell_radial_power_monitor(
     217             :     const DataVector& tensor_component, const Mesh<3>& mesh);
     218             : /// @}
     219             : 
     220             : /// @{
     221             : /*!
     222             :  * \brief Return the angular power monitor for one TensorYlm component on a
     223             :  * spherical shell.
     224             :  *
     225             :  * The mesh dimensions are assumed to be ordered `(radial, theta, phi)`, with
     226             :  * `l_max == m_max`. TensorYlm coefficients use the radial dimension as the
     227             :  * fastest-moving extent. As reviewed in Sec. II of \cite Boyle2023,
     228             :  * spin-weighted spherical harmonics with `l < |spin_weight|` vanish, so these
     229             :  * modes are omitted from both the sum and its normalization. Set
     230             :  * `zero_m_is_real` for real scalar coefficients, which have no imaginary
     231             :  * `m=0` coefficients in Spherepack storage.
     232             :  */
     233           1 : void spherical_shell_angular_power_monitor(
     234             :     gsl::not_null<DataVector*> result, const DataVector& tensor_ylm_component,
     235             :     const Mesh<3>& mesh, int spin_weight, bool zero_m_is_real);
     236             : 
     237           1 : DataVector spherical_shell_angular_power_monitor(
     238             :     const DataVector& tensor_ylm_component, const Mesh<3>& mesh,
     239             :     int spin_weight, bool zero_m_is_real);
     240             : /// @}
     241             : }  // namespace PowerMonitors

Generated by: LCOV version 1.14