SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/SphericalHarmonics - Spherepack.hpp Hit Total Coverage
Commit: 8f6d7ed2ad592dd78354983fd8e5ec2be7abb468 Lines: 45 72 62.5 %
Date: 2024-05-02 15:57:06
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 <cmath>
       8             : #include <cstddef>
       9             : #include <utility>
      10             : #include <vector>
      11             : 
      12             : #include "DataStructures/DataVector.hpp"
      13             : #include "DataStructures/DynamicBuffer.hpp"
      14             : #include "DataStructures/Tensor/TypeAliases.hpp"
      15             : #include "NumericalAlgorithms/SphericalHarmonics/SpherepackHelper.hpp"
      16             : #include "Utilities/Blas.hpp"
      17             : #include "Utilities/ForceInline.hpp"
      18             : #include "Utilities/Gsl.hpp"
      19             : 
      20             : /// Items related to spherical harmonics
      21             : namespace ylm {
      22             : 
      23             : /*!
      24             :  * \ingroup SpectralGroup
      25             :  *
      26             :  * \brief Defines the C++ interface to SPHEREPACK.
      27             :  *
      28             :  * \details The class `Spherepack` defines the C++ interface to the fortran
      29             :  * library SPHEREPACK used for computations on the surface of a sphere.
      30             :  *
      31             :  * Given a real-valued, scalar function \f$g(\theta, \phi)\f$, SPHEREPACK
      32             :  * expands it as:
      33             :  *
      34             :  * \f{align}
      35             :  * g(\theta, \phi)
      36             :  * &=\frac{1}{2}\sum_{l=0}^{l_{\max}}\bar P_l^0(\cos\theta) a_{l0}
      37             :  * +\sum_{l=1}^{l_{\max}}\sum_{m=1}^{\min(l, m_{\max})}\bar P_l^m(\cos\theta)\{
      38             :  *   a_{lm}\cos m\phi -b_{lm}\sin m\phi\}\label{eq:spherepack_expansion}
      39             :  * \f}
      40             :  *
      41             :  * where \f$a_{lm}\f$ and \f$b_{lm}\f$ are real-valued
      42             :  * spectral coefficient arrays used by
      43             :  * SPHEREPACK, \f$P_l^m(x)\f$ are defined as
      44             :  *
      45             :  * \f{align}
      46             :  * \bar P_l^m(x)&=\sqrt{\frac{(2l+1)(l-m)!}{2(l+m)!}}\;P_{lm}(x)
      47             :  * \f}
      48             :  *
      49             :  * and \f$P_{nm}(x)\f$ are the associated Legendre polynomials as defined,
      50             :  * for example, in Jackson's "Classical Electrodynamics".
      51             :  *
      52             :  * #### Relationship to standard spherical harmonics
      53             :  *
      54             :  * The standard expansion of \f$g(\theta, \phi)\f$ in terms of scalar
      55             :  * spherical harmonics is
      56             :  * \f{align}
      57             :  * g(\theta, \phi)
      58             :  * &=
      59             :  * \sum_{l=0}^{l_{\max}}\sum_{m=-\min(l, m_{\max})}^{\min(l, m_{\max})}
      60             :  * A_{lm} Y_{lm}(\theta,\phi),
      61             :  * \f}
      62             :  * where \f$Y_{lm}(\theta,\phi)\f$ are the usual complex-valued scalar
      63             :  * spherical harmonics (as defined, for example, in
      64             :  * Jackson's "Classical Electrodynamics")
      65             :  * and \f$A_{lm}\f$ are complex coefficients.
      66             :  *
      67             :  * The relationship between the complex coefficients \f$A_{lm}\f$ and
      68             :  * SPHEREPACK's real-valued \f$a_{lm}\f$ and \f$b_{lm}\f$ is
      69             :  * \f{align}
      70             :  * a_{l0} & = \sqrt{\frac{2}{\pi}}A_{l0}&\qquad l\geq 0,\\
      71             :  * a_{lm} & = (-1)^m\sqrt{\frac{2}{\pi}} \mathrm{Re}(A_{lm})
      72             :  * &\qquad l\geq 1, m\geq 1, \\
      73             :  * b_{lm} & = (-1)^m\sqrt{\frac{2}{\pi}} \mathrm{Im}(A_{lm})
      74             :  * &\qquad l\geq 1, m\geq 1.
      75             :  * \f}
      76             :  *
      77             :  * \note If \f$g\f$ is real,
      78             :  * \f$A_{lm} = (-1)^m A^\star_{l -m}\f$ (where \f${}^\star\f$ means
      79             :  * a complex conjugate); this is why we don't need to consider \f$m<0\f$
      80             :  * in the previous formulas or in SPHEREPACK's expansion.
      81             :  *
      82             :  * #### Relationship to real-valued spherical harmonics
      83             :  *
      84             :  * Sometimes it is useful to expand a real-valued function in the form
      85             :  * \f{align}
      86             :  * g(\theta, \phi)
      87             :  * &= \sum_{l=0}^\infty\sum_{m=0}^l
      88             :  * \left[
      89             :  * c_{lm}\mathrm{Re}(Y_{lm}(\theta, \phi))+
      90             :  * d_{nm}\mathrm{Im}(Y_{lm}(\theta, \phi))
      91             :  * \right].
      92             :  * \f}
      93             :  * The coefficients here are therefore
      94             :  * \f{align}
      95             :  * c_{l0} &= A_{l0},\\
      96             :  * c_{lm} &= 2\mathrm{Re}(A_{lm}) \qquad m\geq 1,\\
      97             :  * d_{lm} &=-2\mathrm{Im}(A_{lm}).
      98             :  * \f}
      99             :  *
     100             :  * #### Modal and nodal representations
     101             :  *
     102             :  * Internally, SPHEREPACK can represent its expansion in two ways which we
     103             :  * will refer to as modal and nodal representations:
     104             :  *
     105             :  * -# modal: The spectral coefficient arrays \f$a_{lm}\f$ and \f$b_{lm}\f$,
     106             :  * referred to as `spectral_coefs` in the methods below. For this C++ interface,
     107             :  * they are saved in a single `DataVector`. To help you index the coefficients
     108             :  * as expected by this interface, use the class `SpherepackIterator`.
     109             :  *
     110             :  * -# nodal: The values at certain collocation points, referred to as
     111             :  * `collocation_values` in the methods below. This is an array of the expanded
     112             :  * function \f$g(\theta,\phi)\f$ evaluated at collocation values
     113             :  * \f$(\theta_i,\phi_j)\f$, where \f$\theta_i\f$ are Gauss-Legendre quadrature
     114             :  * nodes in the interval \f$(0, \pi)\f$ with \f$i = 0, ..., l_{\max}\f$, and
     115             :  * \f$\phi_j\f$ is distributed uniformly in \f$(0, 2\pi)\f$ with \f$i = 0, ...,
     116             :  * 2m_{\max}\f$. The angles of the collocation points can be computed with the
     117             :  * method `theta_phi_points`.
     118             :  *
     119             :  * To convert between the two representations the methods `spec_to_phys` and
     120             :  * `phys_to_spec` can be used. For internal calculations SPHEREPACK will usually
     121             :  * convert to spectral coefficients first, so it is in general more efficient to
     122             :  * use these directly.
     123             :  *
     124             :  * Most methods of SPHEREPACK will compute the requested values of e.g.
     125             :  * `gradient` or `scalar_laplacian` at the collocation points, effectively
     126             :  * returning an expansion in nodal form as defined above. To evaluate the
     127             :  * function at arbitrary angles \f$\theta\f$, \f$\phi\f$, these values have to
     128             :  * be "interpolated" (i.e. the new expansion evaluated) using `interpolate`.
     129             :  *
     130             :  * Spherepack stores two types of quantities:
     131             :  *   1. storage_, which is filled in the constructor and is always const.
     132             :  *   2. memory_pool_, which is dynamic and thread_local, and is overwritten
     133             :  *      by various member functions that need temporary storage.
     134             :  */
     135           1 : class Spherepack {
     136             :  public:
     137             :   /// Type returned by gradient function.
     138           1 :   using FirstDeriv = tnsr::i<DataVector, 2, Frame::ElementLogical>;
     139             :   /// Type returned by second derivative function.
     140           1 :   using SecondDeriv = tnsr::ij<DataVector, 2, Frame::ElementLogical>;
     141             : 
     142             :   /// Struct to hold cached information at a set of target interpolation
     143             :   /// points.
     144             :   template <typename T>
     145           1 :   struct InterpolationInfo {
     146           0 :     InterpolationInfo(size_t l_max, size_t m_max, const gsl::span<double> pmm,
     147             :                       const std::array<T, 2>& target_points);
     148           0 :     T cos_theta;
     149             :     // cos(m*phi)
     150           0 :     DynamicBuffer<T> cos_m_phi;
     151             :     // sin(m*phi)
     152           0 :     DynamicBuffer<T> sin_m_phi;
     153             :     // pbar_factor[m] = Pbar(m,m)*sin(theta)^m
     154           0 :     DynamicBuffer<T> pbar_factor;
     155             : 
     156           0 :     size_t size() const { return num_points_; }
     157           0 :     size_t m_max() const { return m_max_; }
     158           0 :     size_t l_max() const { return l_max_; }
     159             : 
     160             :    private:
     161           0 :     size_t l_max_;
     162           0 :     size_t m_max_;
     163           0 :     size_t num_points_;
     164             :   };
     165             : 
     166             :   /// Here l_max and m_max are the largest fully-represented l and m in
     167             :   /// the Ylm expansion.
     168           1 :   Spherepack(size_t l_max, size_t m_max);
     169             : 
     170             :   /// @{
     171             :   /// Static functions to return the correct sizes of vectors of
     172             :   /// collocation points and spectral coefficients for a given l_max
     173             :   /// and m_max.  Useful for allocating space without having to create
     174             :   /// a Spherepack.
     175           1 :   SPECTRE_ALWAYS_INLINE static constexpr size_t physical_size(
     176             :       const size_t l_max, const size_t m_max) {
     177             :     return (l_max + 1) * (2 * m_max + 1);
     178             :   }
     179             :   /// \note `spectral_size` is the size of the buffer that holds the
     180             :   /// coefficients; it is not the number of coefficients (which is
     181             :   /// \f$m_{\rm max}^2+(l_{\rm max}-m_{\rm max})(2m_{\rm max}+1)\f$).
     182             :   /// To simplify its internal indexing, SPHEREPACK uses a buffer with
     183             :   /// more space than necessary. See SpherepackIterator for
     184             :   /// how to index the coefficients in the buffer.
     185           1 :   SPECTRE_ALWAYS_INLINE static constexpr size_t spectral_size(
     186             :       const size_t l_max, const size_t m_max) {
     187             :     return 2 * (l_max + 1) * (m_max + 1);
     188             :   }
     189             :   /// @}
     190             : 
     191             :   /// @{
     192             :   /// Sizes in physical and spectral space for this instance.
     193           1 :   size_t l_max() const { return l_max_; }
     194           1 :   size_t m_max() const { return m_max_; }
     195           1 :   size_t physical_size() const { return n_theta_ * n_phi_; }
     196           1 :   size_t spectral_size() const { return spectral_size_; }
     197             :   /// @}
     198             : 
     199           0 :   std::array<size_t, 2> physical_extents() const {
     200             :     return {{n_theta_, n_phi_}};
     201             :   }
     202             : 
     203             :   /// @{
     204             :   /// Collocation points theta and phi.
     205             :   ///
     206             :   /// The phi points are uniform in phi, with the first point
     207             :   /// at phi=0.
     208             :   ///
     209             :   /// The theta points are Gauss-Legendre in \f$\cos(\theta)\f$,
     210             :   /// so there are no points at the poles.
     211           1 :   SPECTRE_ALWAYS_INLINE const std::vector<double>& theta_points() const {
     212             :     return storage_.theta;
     213             :   }
     214           1 :   SPECTRE_ALWAYS_INLINE const std::vector<double>& phi_points() const {
     215             :     return storage_.phi;
     216             :   }
     217           1 :   std::array<DataVector, 2> theta_phi_points() const;
     218             :   /// @}
     219             : 
     220             :   /// @{
     221             :   /// Spectral transformations.
     222             :   /// To act on a slice of the input and output arrays, specify strides
     223             :   /// and offsets.
     224           1 :   void phys_to_spec(gsl::not_null<double*> spectral_coefs,
     225             :                     gsl::not_null<const double*> collocation_values,
     226             :                     size_t physical_stride = 1, size_t physical_offset = 0,
     227             :                     size_t spectral_stride = 1,
     228             :                     size_t spectral_offset = 0) const {
     229             :     phys_to_spec_impl(spectral_coefs, collocation_values, physical_stride,
     230             :                       physical_offset, spectral_stride, spectral_offset, false);
     231             :   }
     232           1 :   void spec_to_phys(gsl::not_null<double*> collocation_values,
     233             :                     gsl::not_null<const double*> spectral_coefs,
     234             :                     size_t spectral_stride = 1, size_t spectral_offset = 0,
     235             :                     size_t physical_stride = 1,
     236             :                     size_t physical_offset = 0) const {
     237             :     spec_to_phys_impl(collocation_values, spectral_coefs, spectral_stride,
     238             :                       spectral_offset, physical_stride, physical_offset, false);
     239             :   };
     240             :   /// @}
     241             : 
     242             :   /// @{
     243             :   /// Spectral transformations where `collocation_values` and
     244             :   /// `spectral_coefs` are assumed to point to 3-dimensional arrays
     245             :   /// (I1 x S2 topology), and the transformations are done for all
     246             :   /// 'radial' points at once by internally looping over all values of
     247             :   /// the offset from zero to `stride`-1 (the physical and spectral
     248             :   /// strides are equal and are called `stride`).
     249           1 :   void phys_to_spec_all_offsets(gsl::not_null<double*> spectral_coefs,
     250             :                                 gsl::not_null<const double*> collocation_values,
     251             :                                 size_t stride) const {
     252             :     phys_to_spec_impl(spectral_coefs, collocation_values, stride, 0, stride, 0,
     253             :                       true);
     254             :   }
     255           1 :   void spec_to_phys_all_offsets(gsl::not_null<double*> collocation_values,
     256             :                                 gsl::not_null<const double*> spectral_coefs,
     257             :                                 size_t stride) const {
     258             :     spec_to_phys_impl(collocation_values, spectral_coefs, stride, 0, stride, 0,
     259             :                       true);
     260             :   };
     261             :   /// @}
     262             : 
     263             :   /// @{
     264             :   /// Simpler, less general interfaces to `phys_to_spec` and `spec_to_phys`.
     265             :   /// Acts on a slice of the input and returns a unit-stride result.
     266           1 :   DataVector phys_to_spec(const DataVector& collocation_values,
     267             :                           size_t physical_stride = 1,
     268             :                           size_t physical_offset = 0) const;
     269           1 :   DataVector spec_to_phys(const DataVector& spectral_coefs,
     270             :                           size_t spectral_stride = 1,
     271             :                           size_t spectral_offset = 0) const;
     272             :   /// @}
     273             : 
     274             :   /// @{
     275             :   /// Simpler, less general interfaces to `phys_to_spec_all_offsets`
     276             :   /// and `spec_to_phys_all_offsets`.  Result has the same stride as
     277             :   /// the input.
     278           1 :   DataVector phys_to_spec_all_offsets(const DataVector& collocation_values,
     279             :                                       size_t stride) const;
     280           1 :   DataVector spec_to_phys_all_offsets(const DataVector& spectral_coefs,
     281             :                                       size_t stride) const;
     282             :   /// @}
     283             : 
     284             :   /// Computes Pfaffian derivative (df/dtheta, csc(theta) df/dphi) at
     285             :   /// the collocation values.
     286             :   /// To act on a slice of the input and output arrays, specify stride
     287             :   /// and offset (assumed to be the same for input and output).
     288           1 :   void gradient(const std::array<double*, 2>& df,
     289             :                 gsl::not_null<const double*> collocation_values,
     290             :                 size_t physical_stride = 1, size_t physical_offset = 0) const;
     291             : 
     292             :   /// Same as `gradient`, but takes the spectral coefficients (rather
     293             :   /// than collocation values) of the function.  This is more
     294             :   /// efficient if one happens to already have the spectral
     295             :   /// coefficients.
     296             :   /// To act on a slice of the input and output arrays, specify strides
     297             :   /// and offsets.
     298           1 :   void gradient_from_coefs(const std::array<double*, 2>& df,
     299             :                            gsl::not_null<const double*> spectral_coefs,
     300             :                            size_t spectral_stride = 1,
     301             :                            size_t spectral_offset = 0,
     302             :                            size_t physical_stride = 1,
     303             :                            size_t physical_offset = 0) const {
     304             :     gradient_from_coefs_impl(df, spectral_coefs, spectral_stride,
     305             :                              spectral_offset, physical_stride, physical_offset,
     306             :                              false);
     307             :   }
     308             : 
     309             :   /// @{
     310             :   /// Same as `gradient` but pointers are assumed to point to
     311             :   /// 3-dimensional arrays (I1 x S2 topology), and the gradient is
     312             :   /// done for all 'radial' points at once by internally looping
     313             :   /// over all values of the offset from zero to `stride`-1.
     314           1 :   void gradient_all_offsets(const std::array<double*, 2>& df,
     315             :                             gsl::not_null<const double*> collocation_values,
     316             :                             size_t stride = 1) const;
     317             : 
     318           1 :   SPECTRE_ALWAYS_INLINE void gradient_from_coefs_all_offsets(
     319             :       const std::array<double*, 2>& df,
     320             :       gsl::not_null<const double*> spectral_coefs, size_t stride = 1) const {
     321             :     gradient_from_coefs_impl(df, spectral_coefs, stride, 0, stride, 0, true);
     322             :   }
     323             :   /// @}
     324             : 
     325             :   /// @{
     326             :   /// Simpler, less general interfaces to `gradient`.
     327             :   /// Acts on a slice of the input and returns a unit-stride result.
     328           1 :   FirstDeriv gradient(const DataVector& collocation_values,
     329             :                       size_t physical_stride = 1,
     330             :                       size_t physical_offset = 0) const;
     331           1 :   FirstDeriv gradient_from_coefs(const DataVector& spectral_coefs,
     332             :                                  size_t spectral_stride = 1,
     333             :                                  size_t spectral_offset = 0) const;
     334             :   /// @}
     335             : 
     336             :   /// @{
     337             :   /// Simpler, less general interfaces to `gradient_all_offsets`.
     338             :   /// Result has the same stride as the input.
     339           1 :   FirstDeriv gradient_all_offsets(const DataVector& collocation_values,
     340             :                                   size_t stride = 1) const;
     341           1 :   FirstDeriv gradient_from_coefs_all_offsets(const DataVector& spectral_coefs,
     342             :                                              size_t stride = 1) const;
     343             :   /// @}
     344             : 
     345             :   /// Computes Laplacian in physical space.
     346             :   /// To act on a slice of the input and output arrays, specify stride
     347             :   /// and offset (assumed to be the same for input and output).
     348           1 :   void scalar_laplacian(gsl::not_null<double*> scalar_laplacian,
     349             :                         gsl::not_null<const double*> collocation_values,
     350             :                         size_t physical_stride = 1,
     351             :                         size_t physical_offset = 0) const;
     352             : 
     353             :   /// Same as `scalar_laplacian` above, but the input is the spectral
     354             :   /// coefficients (rather than collocation values) of the function.
     355             :   /// This is more efficient if one happens to already have the
     356             :   /// spectral coefficients.
     357             :   /// To act on a slice of the input and output arrays, specify strides
     358             :   /// and offsets.
     359           1 :   void scalar_laplacian_from_coefs(gsl::not_null<double*> scalar_laplacian,
     360             :                                    gsl::not_null<const double*> spectral_coefs,
     361             :                                    size_t spectral_stride = 1,
     362             :                                    size_t spectral_offset = 0,
     363             :                                    size_t physical_stride = 1,
     364             :                                    size_t physical_offset = 0) const;
     365             : 
     366             :   /// @{
     367             :   /// Simpler, less general interfaces to `scalar_laplacian`.
     368             :   /// Acts on a slice of the input and returns a unit-stride result.
     369           1 :   DataVector scalar_laplacian(const DataVector& collocation_values,
     370             :                               size_t physical_stride = 1,
     371             :                               size_t physical_offset = 0) const;
     372           1 :   DataVector scalar_laplacian_from_coefs(const DataVector& spectral_coefs,
     373             :                                          size_t spectral_stride = 1,
     374             :                                          size_t spectral_offset = 0) const;
     375             :   /// @}
     376             : 
     377             :   /// Computes Pfaffian first and second derivative in physical space.
     378             :   /// The first derivative is \f$df(i) = d_i f\f$, and the
     379             :   /// second derivative is \f$ddf(i,j) = d_i (d_j f)\f$,
     380             :   /// where \f$d_0 = d/d\theta\f$ and \f$d_1 = csc(\theta) d/d\phi\f$.
     381             :   /// ddf is not symmetric.
     382             :   /// To act on a slice of the input and output arrays, specify stride
     383             :   /// and offset (assumed to be the same for input and output).
     384           1 :   void second_derivative(const std::array<double*, 2>& df,
     385             :                          gsl::not_null<SecondDeriv*> ddf,
     386             :                          gsl::not_null<const double*> collocation_values,
     387             :                          size_t physical_stride = 1,
     388             :                          size_t physical_offset = 0) const;
     389             : 
     390             :   /// Simpler, less general interface to second_derivative
     391           1 :   std::pair<FirstDeriv, SecondDeriv> first_and_second_derivative(
     392             :       const DataVector& collocation_values) const;
     393             : 
     394             :   /// Computes the integral over the sphere.
     395           1 :   SPECTRE_ALWAYS_INLINE double definite_integral(
     396             :       gsl::not_null<const double*> collocation_values,
     397             :       size_t physical_stride = 1, size_t physical_offset = 0) const {
     398             :     // clang-tidy: 'do not use pointer arithmetic'
     399             :     return ddot_(n_theta_ * n_phi_, storage_.quadrature_weights.data(), 1,
     400             :                  collocation_values.get() + physical_offset,  // NOLINT
     401             :                  physical_stride);
     402             :   }
     403             : 
     404             :   /// Returns weights \f$w_i\f$ such that \f$sum_i (c_i w_i)\f$
     405             :   /// is the definite integral, where \f$c_i\f$ are collocation values
     406             :   /// at point i.
     407           1 :   SPECTRE_ALWAYS_INLINE const std::vector<double>& integration_weights() const {
     408             :     return storage_.quadrature_weights;
     409             :   }
     410             : 
     411             :   /// Adds a constant (i.e. \f$f(\theta,\phi)\f$ += \f$c\f$) to the function
     412             :   /// given by the spectral coefficients, by modifying the coefficients.
     413           1 :   SPECTRE_ALWAYS_INLINE static void add_constant(
     414             :       const gsl::not_null<DataVector*> spectral_coefs, const double c) {
     415             :     // The factor of sqrt(8) is because of the normalization of
     416             :     // SPHEREPACK's coefficients.
     417             :     (*spectral_coefs)[0] += sqrt(8.0) * c;
     418             :   }
     419             : 
     420             :   /// Returns the average of \f$f(\theta,\phi)\f$ over \f$(\theta,\phi)\f$.
     421           1 :   SPECTRE_ALWAYS_INLINE static double average(
     422             :       const DataVector& spectral_coefs) {
     423             :     // The factor of sqrt(8) is because of the normalization of
     424             :     // SPHEREPACK's coefficients.  All other coefficients average to zero.
     425             :     return spectral_coefs[0] / sqrt(8.0);
     426             :   }
     427             : 
     428             :   /// Sets up the `InterpolationInfo` structure for interpolating onto
     429             :   /// a set of target \f$(\theta,\phi)\f$ points.  Does not depend on
     430             :   /// the function being interpolated.
     431             :   template <typename T>
     432           1 :   InterpolationInfo<T> set_up_interpolation_info(
     433             :       const std::array<T, 2>& target_points) const;
     434             : 
     435             :   /// Interpolates from `collocation_values` onto the points that have
     436             :   /// been passed into the `set_up_interpolation_info` function.
     437             :   /// To interpolate a different function on the same spectral grid, there
     438             :   /// is no need to recompute `interpolation_info`.
     439             :   /// If you specify stride and offset, acts on a slice of the input values.
     440             :   /// The output has unit stride.
     441             :   template <typename T>
     442           1 :   void interpolate(gsl::not_null<T*> result,
     443             :                    gsl::not_null<const double*> collocation_values,
     444             :                    const InterpolationInfo<T>& interpolation_info,
     445             :                    size_t physical_stride = 1,
     446             :                    size_t physical_offset = 0) const;
     447             : 
     448             :   /// Same as `interpolate`, but assumes you have spectral coefficients.
     449             :   /// This is more efficient if you already have the spectral coefficients
     450             :   /// available.
     451             :   /// If you specify stride and offset, acts on a slice of the input coefs.
     452             :   /// The output has unit stride.
     453             :   template <typename T, typename R>
     454           1 :   void interpolate_from_coefs(gsl::not_null<T*> result, const R& spectral_coefs,
     455             :                               const InterpolationInfo<T>& interpolation_info,
     456             :                               size_t spectral_stride = 1,
     457             :                               size_t spectral_offset = 0) const;
     458             : 
     459             :   /// Simpler interface to `interpolate`.  If you need to call this
     460             :   /// repeatedly on different `spectral_coefs` or `collocation_values`
     461             :   /// for the same target points, this is inefficient; instead use
     462             :   /// `set_up_interpolation_info` and the functions that use
     463             :   /// `InterpolationInfo`.
     464             :   template <typename T>
     465           1 :   T interpolate(const DataVector& collocation_values,
     466             :                 const std::array<T, 2>& target_points) const;
     467             :   template <typename T>
     468           0 :   T interpolate_from_coefs(const DataVector& spectral_coefs,
     469             :                            const std::array<T, 2>& target_points) const;
     470             : 
     471             :   /// Takes spectral coefficients compatible with `*this`, and either
     472             :   /// prolongs them or restricts them to be compatible with `target`.
     473             :   /// This is done by truncation (restriction) or padding with zeros
     474             :   /// (prolongation).
     475           1 :   DataVector prolong_or_restrict(const DataVector& spectral_coefs,
     476             :                                  const Spherepack& target) const;
     477             : 
     478             :  private:
     479             :   // Spectral transformations and gradient.
     480             :   // If `loop_over_offset` is true, then `collocation_values` and
     481             :   // `spectral_coefs` are assumed to point to 3-dimensional
     482             :   // arrays (I1 x S2 topology), and the transformations are done for
     483             :   // all 'radial' points at once by looping over all values of the
     484             :   // offset from zero to stride-1.  If `loop_over_offset` is true,
     485             :   // `physical_stride` must equal `spectral_stride`.
     486           0 :   void phys_to_spec_impl(gsl::not_null<double*> spectral_coefs,
     487             :                          gsl::not_null<const double*> collocation_values,
     488             :                          size_t physical_stride = 1, size_t physical_offset = 0,
     489             :                          size_t spectral_stride = 1, size_t spectral_offset = 0,
     490             :                          bool loop_over_offset = false) const;
     491           0 :   void spec_to_phys_impl(gsl::not_null<double*> collocation_values,
     492             :                          gsl::not_null<const double*> spectral_coefs,
     493             :                          size_t spectral_stride = 1, size_t spectral_offset = 0,
     494             :                          size_t physical_stride = 1, size_t physical_offset = 0,
     495             :                          bool loop_over_offset = false) const;
     496           0 :   void gradient_from_coefs_impl(const std::array<double*, 2>& df,
     497             :                                 gsl::not_null<const double*> spectral_coefs,
     498             :                                 size_t spectral_stride = 1,
     499             :                                 size_t spectral_offset = 0,
     500             :                                 size_t physical_stride = 1,
     501             :                                 size_t physical_offset = 0,
     502             :                                 bool loop_over_offset = false) const;
     503           0 :   void calculate_collocation_points();
     504           0 :   void calculate_interpolation_data();
     505           0 :   void fill_scalar_work_arrays();
     506           0 :   void fill_vector_work_arrays();
     507           0 :   size_t l_max_, m_max_, n_theta_, n_phi_;
     508           0 :   size_t spectral_size_;
     509             :   // memory_pool_ will be shared by multiple instances of
     510             :   // Spherepack on the same thread.  Because these instances are on
     511             :   // the same thread, member functions of two or more of these
     512             :   // instances cannot be called simultaneously.  Note that member
     513             :   // functions do not make any assumptions about the contents of
     514             :   // memory_pool_ on entry, so between calls to member functions it is
     515             :   // safe to resize objects in memory_pool_ or to overwrite them with
     516             :   // arbitrary data.
     517           0 :   static thread_local Spherepack_detail::MemoryPool memory_pool_;
     518           0 :   Spherepack_detail::ConstStorage storage_;
     519             : };  // class Spherepack
     520             : 
     521           0 : bool operator==(const Spherepack& lhs, const Spherepack& rhs);
     522           0 : bool operator!=(const Spherepack& lhs, const Spherepack& rhs);
     523             : 
     524             : }  // namespace ylm

Generated by: LCOV version 1.14