SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/Spectral - Projection.hpp Hit Total Coverage
Commit: c3e43f8d41800b0ecefb9d1393f1de1d5a280c8f Lines: 11 15 73.3 %
Date: 2026-07-24 22:09:25
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 <functional>
       9             : 
      10             : #include "DataStructures/DataVector.hpp"
      11             : #include "DataStructures/Matrix.hpp"
      12             : #include "DataStructures/Variables.hpp"
      13             : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      14             : #include "NumericalAlgorithms/Spectral/SegmentSize.hpp"
      15             : #include "Utilities/ConstantExpressions.hpp"
      16             : #include "Utilities/Gsl.hpp"
      17             : 
      18             : namespace Spectral {
      19             : /// Determine whether data needs to be projected between a child mesh and its
      20             : /// parent mesh. If no projection is necessary the data may be used as-is.
      21             : /// Projection is necessary if the child is either p-refined or h-refined
      22             : /// relative to its parent, or both. This operation is symmetric, i.e. it is
      23             : /// irrelevant in which order the child and the parent mesh are passed in.
      24             : template <size_t Dim>
      25           1 : bool needs_projection(const Mesh<Dim>& mesh1, const Mesh<Dim>& mesh2,
      26             :                       const std::array<SegmentSize, Dim>& child_sizes);
      27             : 
      28             : /*!
      29             :  * \brief The projection matrix from a child mesh to its parent.
      30             :  *
      31             :  * The projection matrices returned by this function (and by
      32             :  * projection_matrix_parent_to_child()) define orthogonal projection operators
      33             :  * between the spaces of functions on a parent mesh and its children. These
      34             :  * projections are usually the correct way to transfer data between meshes in
      35             :  * a mesh-refinement hierarchy, as well as between an element face and its
      36             :  * adjacent mortars.
      37             :  *
      38             :  * These functions assume that the `child_mesh` is at least as fine as the
      39             :  * `parent_mesh`, i.e. functions on the `parent_mesh` can be represented exactly
      40             :  * on the `child_mesh`. In practice this means that functions can be projected
      41             :  * to a mortar (the `child_mesh`) from both adjacent element faces (the
      42             :  * `parent_mesh`) without losing accuracy. Similarly, functions in a
      43             :  * mesh-refinement hierarchy don't lose accuracy when an element is split
      44             :  * (h-refined). For this reason, the `projection_matrix_child_to_parent` is
      45             :  * sometimes referred to as a "restriction operator" and the
      46             :  * `projection_matrix_parent_to_child` as a "prolongation operator".
      47             :  *
      48             :  * \par Massive quantities
      49             :  * If the quantity that should be projected is not a function over the
      50             :  * computational grid but a "massive" residual, i.e. a quantity
      51             :  * \f$\int_{\Omega_k} f(x) \psi_p(x) \mathrm{d}V\f$ where \f$\psi_p\f$ are the
      52             :  * basis functions on the mesh, then pass `true` for the parameter
      53             :  * `operand_is_massive` (default is `false`). The restriction operator for this
      54             :  * case is just the transpose of the prolongation operator, i.e. just an
      55             :  * interpolation matrix transpose. Note that the "massive" residual already
      56             :  * takes the difference in element size between parent and children into account
      57             :  * by including a Jacobian in the volume element of the integral.
      58             :  *
      59             :  * \par Implementation details
      60             :  * The half-interval projections are based on an equation derived by
      61             :  * Saul.  This shows that the projection from the spectral basis for
      62             :  * the entire interval to the spectral basis for the upper half
      63             :  * interval is
      64             :  * \f{equation*}
      65             :  * T_{jk} = \frac{2 j + 1}{2} 2^j \sum_{n=0}^{j-k} \binom{j}{k+n}
      66             :  * \binom{(j + k + n - 1)/2}{j} \frac{(k + n)!^2}{(2 k + n + 1)! n!}
      67             :  * \f}
      68             :  *
      69             :  * \note This and the other matrix-returning projection functions in this file
      70             :  * (projection_matrix_parent_to_child(), projection_matrices()) only support
      71             :  * tensor-product bases (Legendre, Fourier, ZernikeB2). They cannot be used for
      72             :  * spherical-shell meshes that use the `SphericalHarmonic` basis, because the
      73             :  * angular projection couples the \f$\theta\f$ and \f$\phi\f$ directions and so
      74             :  * cannot be expressed as the per-dimension matrices these functions return. Use
      75             :  * Spectral::project() instead whenever spherical-shell meshes must be
      76             :  * supported; it dispatches to these matrices for tensor-product meshes and to a
      77             :  * Spherepack-based angular projection for spherical-shell meshes.
      78             :  */
      79           1 : const Matrix& projection_matrix_child_to_parent(
      80             :     const Mesh<1>& child_mesh, const Mesh<1>& parent_mesh, SegmentSize size,
      81             :     bool operand_is_massive = false);
      82             : 
      83             : /// The projection matrix from a child mesh to its parent, in `Dim` dimensions.
      84             : template <size_t Dim>
      85             : std::array<std::reference_wrapper<const Matrix>, Dim>
      86           1 : projection_matrix_child_to_parent(
      87             :     const Mesh<Dim>& child_mesh, const Mesh<Dim>& parent_mesh,
      88             :     const std::array<SegmentSize, Dim>& child_sizes,
      89             :     bool operand_is_massive = false);
      90             : 
      91             : /// The projection matrix from a parent mesh to one of its children.
      92             : ///
      93             : /// \see projection_matrix_child_to_parent()
      94           1 : const Matrix& projection_matrix_parent_to_child(const Mesh<1>& parent_mesh,
      95             :                                                 const Mesh<1>& child_mesh,
      96             :                                                 SegmentSize size);
      97             : 
      98             : /// The projection matrix from a parent mesh to one of its children, in `Dim`
      99             : /// dimensions
     100             : template <size_t Dim>
     101             : std::array<std::reference_wrapper<const Matrix>, Dim>
     102           1 : projection_matrix_parent_to_child(
     103             :     const Mesh<Dim>& parent_mesh, const Mesh<Dim>& child_mesh,
     104             :     const std::array<SegmentSize, Dim>& child_sizes);
     105             : 
     106             : /// The projection matrices from a source mesh to a target mesh
     107             : /// covering given portions of an element
     108             : template <size_t Dim>
     109           1 : std::array<std::reference_wrapper<const Matrix>, Dim> projection_matrices(
     110             :     const Mesh<Dim>& source_mesh, const Mesh<Dim>& target_mesh,
     111             :     const std::array<SegmentSize, Dim>& source_sizes,
     112             :     const std::array<SegmentSize, Dim>& target_sizes,
     113             :     bool operand_is_massive = false);
     114             : 
     115             : /// Change the angular resolution (`l_max`, with `m_max == l_max`) of volume
     116             : /// data on a spherical shell from `source_data` to `result_data`, for
     117             : /// `num_components` components each laid out with the radial dimension varying
     118             : /// fastest. Uses Spherepack prolong/restrict. \see Spectral::project() for the
     119             : /// higher-level interface to call this.
     120             : ///
     121             : /// For non-massive operands this is the L2 (Galerkin) projection,
     122             : /// $P_\mathrm{L2}$. For massive operands (`operand_is_massive == true`) the
     123             : /// restriction is the transpose of the prolongation (interpolation) operator,
     124             : /// $I^T$, which is the L2 projection conjugated by the diagonal matrices $W$ of
     125             : /// angular quadrature weights:
     126             : /// $I^T = W_\mathrm{target} P_\mathrm{L2} W_\mathrm{source}^{-1}$.
     127             : /// This means we divide by the source weights before projecting and multiply by
     128             : /// the target weights afterwards. See projection_matrix_child_to_parent() for
     129             : /// details on massive operands.
     130           1 : void project_spherical_harmonics(gsl::not_null<double*> result_data,
     131             :                                  const double* source_data,
     132             :                                  size_t num_components,
     133             :                                  size_t num_radial_points, size_t l_max_source,
     134             :                                  size_t l_max_target, bool operand_is_massive);
     135             : 
     136             : /// @{
     137             : /*!
     138             :  * \brief Project volume data from `source_mesh` to `target_mesh`, writing the
     139             :  * (resized) result into `*result`.
     140             :  *
     141             :  * This is the unified entry point for projection regardless of basis. It
     142             :  * handles both tensor-product meshes and spherical-shell meshes (Legendre
     143             :  * radial dimension plus `SphericalHarmonic` angular dimensions with
     144             :  * `m_max == l_max`):
     145             :  * - For tensor-product meshes it delegates to the per-dimension projection
     146             :  *   matrices above.
     147             :  * - For spherical-shell meshes it projects the radial dimension with the 1D
     148             :  *   projection matrix and changes the angular `l_max` with a Spherepack
     149             :  *   prolong/restrict. The angular dimensions cannot be h-refined, so their
     150             :  *   segment sizes must be `Full`.
     151             :  *
     152             :  * `source_sizes` and `target_sizes` carry h-refinement information (which
     153             :  * portion of an element the source/target cover); pass `Full` in every
     154             :  * dimension for pure p-refinement.
     155             :  *
     156             :  * \warning `*result` must not point to `source` (the result is resized to the
     157             :  * target number of grid points).
     158             :  */
     159             : template <typename VectorType, size_t Dim>
     160           1 : void project(gsl::not_null<VectorType*> result, const VectorType& source,
     161             :              const Mesh<Dim>& source_mesh, const Mesh<Dim>& target_mesh,
     162             :              const std::array<SegmentSize, Dim>& source_sizes,
     163             :              const std::array<SegmentSize, Dim>& target_sizes,
     164             :              bool operand_is_massive = false);
     165             : 
     166             : template <size_t Dim, typename TagList>
     167           1 : void project(const gsl::not_null<Variables<TagList>*> result,
     168             :              const Variables<TagList>& source, const Mesh<Dim>& source_mesh,
     169             :              const Mesh<Dim>& target_mesh,
     170             :              const std::array<SegmentSize, Dim>& source_sizes,
     171             :              const std::array<SegmentSize, Dim>& target_sizes,
     172             :              const bool operand_is_massive = false) {
     173             :   // Type-erase to the vector implementation with multiple components
     174             :   using VectorType = typename Variables<TagList>::vector_type;
     175             :   using ValueType = typename Variables<TagList>::value_type;
     176             :   result->initialize(target_mesh.number_of_grid_points());
     177             :   VectorType result_view(result->data(), result->size());
     178             :   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
     179             :   const VectorType source_view(const_cast<ValueType*>(source.data()),
     180             :                                source.size());
     181             :   project(make_not_null(&result_view), source_view, source_mesh, target_mesh,
     182             :           source_sizes, target_sizes, operand_is_massive);
     183             : }
     184             : 
     185             : template <size_t Dim, typename T>
     186           1 : T project(const T& source, const Mesh<Dim>& source_mesh,
     187             :           const Mesh<Dim>& target_mesh,
     188             :           const std::array<SegmentSize, Dim>& source_sizes,
     189             :           const std::array<SegmentSize, Dim>& target_sizes,
     190             :           const bool operand_is_massive = false) {
     191             :   T result{};
     192             :   project(make_not_null(&result), source, source_mesh, target_mesh,
     193             :           source_sizes, target_sizes, operand_is_massive);
     194             :   return result;
     195             : }
     196             : /// @}
     197             : 
     198             : /// @{
     199             : /// \brief Performs a perfect hash of the mortars into $2^{d-1}$ slots on the
     200             : /// range $[0, 2^{d-1})$.
     201             : ///
     202             : /// This is particularly useful when hashing into statically-sized maps based
     203             : /// on the number of dimensions.
     204             : template <size_t DimMinusOne>
     205           1 : size_t hash(const std::array<Spectral::SegmentSize, DimMinusOne>& mortar_size);
     206             : 
     207             : template <size_t Dim>
     208           0 : struct MortarSizeHash {
     209             :   template <size_t MaxSize>
     210           0 :   static constexpr bool is_perfect = MaxSize == two_to_the(Dim);
     211             : 
     212           0 :   size_t operator()(
     213             :       const std::array<Spectral::SegmentSize, Dim - 1>& mortar_size);
     214             : };
     215             : /// @}
     216             : }  // namespace Spectral

Generated by: LCOV version 1.14