SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/Spectral - Projection.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 73.3 % 15 11
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 <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 2.0-1