SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/TensorYlm - Filter.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 30.8 % 13 4
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 <optional>
       8              : 
       9              : #include "DataStructures/SimpleSparseMatrix.hpp"
      10              : #include "DataStructures/Tensor/TypeAliases.hpp"
      11              : #include "NumericalAlgorithms/TensorYlm/TensorYlm.hpp"
      12              : #include "Utilities/Gsl.hpp"
      13              : 
      14              : /// \cond
      15              : class DataVector;
      16              : template <typename TagsList>
      17              : class Variables;
      18              : /// \endcond
      19              : 
      20              : namespace ylm::TensorYlm {
      21              : 
      22              : /*!
      23              :  * \brief Fills a sparse matrix that does a TensorYlm filter operation.
      24              :  *
      25              :  * If the CoefficientNormalization parameter is set to Standard, then
      26              :  * assumes that $T^{\tilde A}_{\ell' m'}$ is stored in a
      27              :  * Tensor<DataVector>.  Multiplying (one plus) the resulting
      28              :  * sparse matrix by the Tensor<DataVector> is equivalent to
      29              :  * evaluating the right-hand side of Eq. $(\ref{eq:Filter})$.
      30              :  * If the CoefficientNormalization parameter is set to Spherepack, then
      31              :  * assumes the matrix will multiply a Tensor<DataVector> containing
      32              :  * ${\breve T}^{\tilde A}_{\ell' m'}$ and the filter operation is equivalent to
      33              :  * Eq. $(\ref{eq:FilterSpherepack})$.
      34              :  *
      35              :  * We assume that the independent components of the Tensor<DataVector>
      36              :  * are stored contiguously in memory, in order of the storage_index of
      37              :  * the Tensor.  However, we do allow for a stride.  This means that we
      38              :  * can point to memory starting with the first element of
      39              :  * $T^{\tilde A}_{\ell' m'}$ and multiply that by the sparse matrix we compute
      40              :  * here, and get the filtered result.
      41              :  *
      42              :  * The memory layout here is different than in SpEC.  In SpEC, each
      43              :  * tensor component is stored in separately-allocated memory, so the
      44              :  * SpEC equivalent of the fill_filter function fills $N^2$ sparse
      45              :  * matrices, where $N$ is the number of independent components of the
      46              :  * Tensor. The advantage of the SpEC method is that each sparse matrix
      47              :  * is smaller, so sorting elements into the correct order while
      48              :  * constructing each sparse matrix is faster (sorting is > linear in
      49              :  * the number of matrix elements).  The disadvantage of the SpEC
      50              :  * method is that evaluating the filter for a single Tensor involves
      51              :  * $N^2$ matrix-vector multiplications, whereas here evaluating the
      52              :  * filter involves only one matrix-vector multiplication, which should
      53              :  * have more efficient memory access.  It is not clear which method is
      54              :  * faster overall without more profiling.
      55              :  *
      56              :  * ## Explicit formulas
      57              :  *
      58              :  * The following formulas come from Klinger and Scheel, in prep.
      59              :  *
      60              :  * For rank-0 tensors, the expression is simple because there
      61              :  * is no change of basis, only a filter based on $\ell$.
      62              :  * \begin{align}
      63              :  *  F_{l m \tilde{D}}^{\ell'' m''\tilde{A}} &=
      64              :  *  \delta(\tilde{D},\tilde{A})\delta_{\ell \ell''}\delta_{m m''}
      65              :  *  \left[1-
      66              :  *  \delta(\ell_{\mathrm{cut}}^-\leq \ell \leq \ell_{\mathrm{max}}) g(\ell)
      67              :  *  \right].
      68              :  * \end{align}
      69              :  *
      70              :  * For rank-1 tensors, the expression for
      71              :  * $F_{l m \tilde{D}}^{\ell'' m''\tilde{A}}$ is
      72              :  * \begin{align}
      73              :  *  F_{l m \tilde{D}}^{\ell'' m''\tilde{A}} &=
      74              :  *  \delta(\tilde{D},\tilde{A})\delta_{\ell \ell''}\delta_{m m''}
      75              :  *  \nonumber \\
      76              :  *  &- (-1)^{m+m''} (-1)^{\delta(\tilde{D},\mathbf{e}_y)}
      77              :  *  \delta(\ell,\ell'')
      78              :  *  \sum_{\ell'=\ell_{\mathrm{cut}}^-}^{\ell_{\mathrm{max}}+1}
      79              :  *  \frac{2\ell'+1}{2} g(\ell') \nonumber \\
      80              :  *  &\times \sum_{j,p,m'} k_j(\tilde{D})k_p(\tilde{A})
      81              :  *    \left(\begin{array}{rrr}
      82              :  *     \ell&\ell'&1\cr
      83              :  *      -m''&m'&m_j(\tilde{D})
      84              :  *    \end{array}\right)
      85              :  *    \left(\begin{array}{rrr}
      86              :  *     \ell&\ell'&1\cr
      87              :  *      -m&m'&m_p(\tilde{A})
      88              :  *    \end{array}\right),
      89              :  * \end{align}
      90              :  * where the 6-element "matrices"
      91              :  * in parentheses are Wigner 3-J symbols, and where
      92              :  * \begin{align}
      93              :  *  g(\ell') &=
      94              :  *  \left\{\begin{array}{lr}
      95              :  *      1-f(\ell') & \ell' \leq \ell_{\mathrm{cut}}^+,\\
      96              :  *      1 & \ell' > \ell_{\mathrm{cut}}^+.
      97              :  *  \end{array}\right.
      98              :  * \end{align}
      99              :  *
     100              :  * For second-rank tensors, the expression for
     101              :  * $F_{l m \tilde{D}}^{\ell'' m''\tilde{A}}$ is
     102              :  * \begin{align}
     103              :  *  F_{l m \tilde{D}}^{\ell'' m''\tilde{A}}
     104              :  *  &=
     105              :  *  \delta(\tilde{D},\tilde{A})\delta_{\ell \ell''}\delta_{m m''}
     106              :  * \nonumber \\
     107              :  * &-
     108              :  *  \frac{1}{4}
     109              :  * (-1)^{\delta(\tilde{D}_1,\mathbf{e}_y)}
     110              :  * (-1)^{\delta(\tilde{D}_2,\mathbf{e}_y)}
     111              :  *  \delta_{\ell \ell''}
     112              :  * \nonumber \\
     113              :  * &\times
     114              :  * \sum_{\ell'=\ell_{\mathrm{cut}}^-}^{\ell_{\mathrm{max}}+2}
     115              :  * (2\ell'+1) g(\ell')
     116              :  * \sum_{u,v,p,q,\bar{\ell},\tilde{m},\bar{m},m'}
     117              :  * (2 \bar{\ell}+1) S(\bar{\ell},\tilde{D})
     118              :  * k_u(\tilde{D}_1) k_v(\tilde{D}_2)
     119              :  * k_p(\tilde{A}_1) k_q(\tilde{A}_2)
     120              :  * \nonumber \\
     121              :  * &\times
     122              :  *    \left(\begin{array}{rrr}
     123              :  *     \ell&\ell'&\bar{\ell}\cr
     124              :  *      -m&m'&\bar{m}
     125              :  *    \end{array}\right)
     126              :  *    \left(\begin{array}{rrr}
     127              :  *     1&1&\bar{\ell}\cr
     128              :  *      m_p(\tilde{A}_1)&m_q(\tilde{A}_2)&-\bar{m}
     129              :  *    \end{array}\right)
     130              :  * \nonumber \\
     131              :  * &\times
     132              :  *    \left(\begin{array}{rrr}
     133              :  *     \ell'&\ell&\bar{\ell}\cr
     134              :  *      -m'&m''&\tilde{m}
     135              :  *    \end{array}\right)
     136              :  *    \left(\begin{array}{rrr}
     137              :  *     1&1&\bar{\ell}\cr
     138              :  *      m_u(\tilde{D}_1)&m_v(\tilde{D}_2)&\tilde{m}
     139              :  *    \end{array}\right),
     140              :  *  \label{eq:RankTwoTransformWithCut}
     141              :  * \end{align}
     142              :  * where the factor $S(\bar{\ell},\tilde{D})$ is a symmetry factor.
     143              :  * For a 2nd-rank tensor with no symmetries, $S(\bar{\ell},\tilde{D})$ is
     144              :  * unity, but for a tensor symmetric in $(\tilde{D_1},\tilde{D_2})$
     145              :  * the matrix elements $F_{l m \tilde{D}}^{\ell'' m''\tilde{A}}$ are
     146              :  * multiplied only by tensor components with $\tilde{D_1}\geq\tilde{D_2}$
     147              :  * and the symmetry is accounted for by setting
     148              :  * \begin{align}
     149              :  *   S(\bar{\ell},\tilde{D}) &=
     150              :  *   (1+(-1)^{\bar{\ell}})\frac{2-\delta(\tilde{D_1},\tilde{D_2})}{2}.
     151              :  * \end{align}
     152              :  *
     153              :  * For third-rank tensors, the expression for
     154              :  * $F_{l m \tilde{D}}^{\ell'' m''\tilde{A}}$ is
     155              :  * \begin{align}
     156              :  *  F_{l m \tilde{D}}^{\ell'' m''\tilde{A}}
     157              :  *  &=
     158              :  *  \delta(\tilde{D},\tilde{A})\delta_{\ell \ell''}\delta_{m m''}
     159              :  *  \nonumber \\
     160              :  * &-
     161              :  *  \frac{1}{8}
     162              :  *  (-1)^{\delta(\tilde{D}_1,\mathbf{e}_y)}
     163              :  *  (-1)^{\delta(\tilde{D}_2,\mathbf{e}_y)}
     164              :  *  (-1)^{\delta(\tilde{D}_3,\mathbf{e}_y)}
     165              :  *  \delta_{\ell \ell''}
     166              :  *  \nonumber \\
     167              :  *  &\times
     168              :  *  \sum_{\ell'=\ell_{\mathrm{cut}}^-}^{\ell_{\mathrm{max}}+3}
     169              :  *  (2\ell'+1) g(\ell')
     170              :  *  \sum_{u,v,w,p,q,r,\tilde{m},\bar{\ell},\bar{m},
     171              :  *  \hat{\ell},\hat{m},\check{m},m'}
     172              :  *  (2 \bar{\ell}+1) S(\bar{\ell},\tilde{D})
     173              :  *  (2 \hat{\ell}+1)
     174              :  *  \nonumber \\
     175              :  *  &\qquad\times
     176              :  *  k_u(\tilde{D}_2) k_v(\tilde{D}_3) k_w(\tilde{D}_1)
     177              :  *  k_p(\tilde{A}_2) k_q(\tilde{A}_3) k_r(\tilde{A}_1)
     178              :  *  (-1)^{\tilde{m}-\bar{m}}
     179              :  *  \nonumber \\
     180              :  *  &\qquad\times
     181              :  *    \left(\begin{array}{rrr}
     182              :  *     1&1&\bar{\ell}\cr
     183              :  *     m_p(\tilde{A}_2)&m_q(\tilde{A}_3)&-\bar{m}
     184              :  *    \end{array}\right)
     185              :  *    \left(\begin{array}{rrr}
     186              :  *     1&1&\bar{\ell}\cr
     187              :  *     m_u(\tilde{D}_2)&m_v(\tilde{D}_3)&\tilde{m}
     188              :  *    \end{array}\right)
     189              :  *  \nonumber \\
     190              :  *  &\qquad\times
     191              :  *    \left(\begin{array}{rrr}
     192              :  *     \ell&\ell'&\hat{\ell}\cr
     193              :  *     -m&m'&\check{m}
     194              :  *    \end{array}\right)
     195              :  *    \left(\begin{array}{rrr}
     196              :  *     \ell'&\ell&\hat{\ell}\cr
     197              :  *     -m'&m''&\hat{m}
     198              :  *    \end{array}\right)
     199              :  *  \nonumber \\
     200              :  *  &\qquad\times
     201              :  *    \left(\begin{array}{rrr}
     202              :  *     1&\bar{\ell}&\hat{\ell}\cr
     203              :  *     m_r(\tilde{A}_1)&\bar{m}&-\check{m}
     204              :  *    \end{array}\right)
     205              :  *    \left(\begin{array}{rrr}
     206              :  *     1&\bar{\ell}&\hat{\ell}\cr
     207              :  *     m_w(\tilde{D}_1)&-\tilde{m}&\hat{m}
     208              :  *    \end{array}\right).
     209              :  *    \label{eq:RankThreeTransformWithCut}
     210              :  * \end{align}
     211              :  * As in the rank-2 case,
     212              :  * $S(\bar{\ell},\tilde{D})$ is a symmetry factor that is unity
     213              :  * for a tensor with no symmetries and is
     214              :  * \begin{align}
     215              :  *   S(\bar{\ell},\tilde{D}) &=
     216              :  *   (1+(-1)^{\bar{\ell}})\frac{2-\delta(\tilde{D_2},\tilde{D_3})}{2}
     217              :  * \end{align}
     218              :  * for a tensor symmetric on its last two indices. We don't consider
     219              :  * other symmetries because we don't find them in the cases we care
     220              :  * about.
     221              :  *
     222              :  * \tparam TensorStructure A Tensor_detail::Structure
     223              :  * \tparam SparseMatrixType A sparse matrix fillable by SparseMatrixFiller
     224              :  *
     225              :  * \param matrix The sparse matrix to fill
     226              :  * \param ell_max The maximum ylm ell value.
     227              :  * \param number_of_ell_modes_to_kill How many top ell modes to set to zero.
     228              :  * \param half_power The half power $\sigma$ for more complicated filtering.
     229              :  * \param coefficient_normalization Describes the normalization of coefficients.
     230              :  *
     231              :  *  If half_power is std::nullopt, implements a Heaviside filter.
     232              :  *  Otherwise, the filter is the more complicated one described in the
     233              :  *  TensorYlm namespace documentation, with $\sigma$ equal to
     234              :  *  half_power and $\ell_{\mathrm{cut}}^+$ equal to $\ell_{\rm max}$
     235              :  *  minus number_of_ell_modes_to_kill.
     236              :  *
     237              :  */
     238              : template <typename TensorStructure, typename SparseMatrixType>
     239            1 : void fill_filter(gsl::not_null<SparseMatrixType*> matrix, size_t ell_max,
     240              :                  size_t number_of_ell_modes_to_kill,
     241              :                  std::optional<size_t> half_power,
     242              :                  CoefficientNormalization coefficient_normalization);
     243              : 
     244              : /*!
     245              :  * \brief Holds the filtering matrices for different matrix ranks.
     246              :  *
     247              :  * This is used for caching the matrices that are needed for a particular
     248              :  * system.
     249              :  *
     250              :  * Since the data being cached depends on parameters, those parameters are also
     251              :  * cached.
     252              :  */
     253            1 : struct FilterMatrixHolder {
     254            0 :   size_t number_of_ell_modes_to_kill{};
     255            0 :   CoefficientNormalization coefficient_normalization{};
     256            0 :   std::optional<size_t> half_power;
     257              : 
     258            0 :   std::optional<SimpleSparseMatrix> scalar;
     259            0 :   std::optional<SimpleSparseMatrix> i;
     260            0 :   std::optional<SimpleSparseMatrix> ii;
     261            0 :   std::optional<SimpleSparseMatrix> ij;
     262            0 :   std::optional<SimpleSparseMatrix> kii;
     263              : };
     264              : 
     265              : /*!
     266              :  * \brief Fill the FilterMatrixHolder for the specific TagList (system).
     267              :  */
     268              : template <typename TagList>
     269            1 : void fill_tensor_ylm_filters(
     270              :     gsl::not_null<FilterMatrixHolder*> matrix, size_t ell_max,
     271              :     size_t number_of_ell_modes_to_kill, std::optional<size_t> half_power,
     272              :     CoefficientNormalization coefficient_normalization);
     273              : 
     274              : /*!
     275              :  * \brief Applies TensorYlm filter in place to variables.
     276              :  *
     277              :  * When \p radial_extents is 1, `vars` and `temp_storage` are assumed to
     278              :  * be defined on a spherical slice, with number of grid points
     279              :  * corresponding to a spherical-harmonic grid of `ell_max`, and the
     280              :  * filter happens only on that slice.
     281              :  *
     282              :  * When \p radial_extents is > 1, `vars` and `temp_storage` are assumed to
     283              :  * be defined on a spherical shell of topology I1 x S2. The filter
     284              :  * happens in the entire volume, internally iterating over each
     285              :  * spherical slice at a time.
     286              :  *
     287              :  * For performance reasons, `apply_tensor_ylm_filter` does not allocate
     288              :  * or deallocate memory, but takes a `temp_storage` buffer.  The size of
     289              :  * `temp_storage` should at least `radial_extents*spectral_size*num_components`,
     290              :  * where `num_components` is the total number of independent components in the
     291              :  * variable list (e.g. 5 for curved scalar wave and 50 for generalized
     292              :  * harmonic), and `spectral_size` is the size of the S2 Spherepack spectral
     293              :  * coefficient array for `ell_max`, as obtained from the member function
     294              :  * ylm::Spherepack::spectral_size().  Note that for S2 on Spherepack, the number
     295              :  * of collocation points is different than the number of spectral coefficients,
     296              :  * and both are different than the size of the Spherepack storage array.
     297              :  *
     298              :  * \param vars Variables at collocation points to filter.
     299              :  * \param temp_storage Temporary storage for the variables,
     300              :  *   allocated outside apply_tensor_ylm_filter. See above for size requirements.
     301              :  * \param jac_inertial_to_grid Jacobian taking V_x from inertial to grid.
     302              :  * \param jac_grid_to_inertial Jacobian taking V_x from grid to inertial.
     303              :  * \param filter_matrices The bundle of filter matrices computed by
     304              :  *   fill_filter. The members used depend on the ranks present in `TagList`.
     305              :  * \param ell_max The maximum ylm ell.
     306              :  * \param radial_extents The number of radial grid points, can be 1 for slices.
     307              :  */
     308              : template <typename TagList>
     309            1 : void apply_tensor_ylm_filter(
     310              :     gsl::not_null<Variables<TagList>*> vars,
     311              :     gsl::not_null<Variables<TagList>*> temp_storage,
     312              :     const InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid>&
     313              :         jac_inertial_to_grid,
     314              :     const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>&
     315              :         jac_grid_to_inertial,
     316              :     const FilterMatrixHolder& filter_matrices, size_t ell_max,
     317              :     size_t radial_extents);
     318              : }  // namespace ylm::TensorYlm
        

Generated by: LCOV version 2.0-1