SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/SphericalHarmonics - SpherepackIterator.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 13 33 39.4 %
Date: 2025-12-05 05:03:31
Legend: Lines: hit not hit

          Line data    Source code
       1           1 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : ///
       5             : /// \file
       6             : /// Defines class SpherepackIterator.
       7             : 
       8             : #pragma once
       9             : 
      10             : #include <cmath>
      11             : #include <cstddef>
      12             : #include <optional>
      13             : #include <ostream>
      14             : #include <vector>
      15             : 
      16             : #include "Utilities/ErrorHandling/Assert.hpp"
      17             : 
      18             : namespace ylm {
      19             : 
      20             : /*!
      21             :  * \ingroup SpectralGroup
      22             :  * \brief
      23             :  * Iterates over spectral coefficients stored in SPHEREPACK format.
      24             :  *
      25             :  * \details
      26             :  * The internal SPHEREPACK ordering is not intuitive, so
      27             :  * SpherepackIterator exists for the purpose of iterating over an
      28             :  * array containing SPHEREPACK coefficients and determining the
      29             :  * (l,m) of each entry in the array.
      30             :  *
      31             :  * SPHEREPACK expands \f$f(\theta,\phi)\f$ as
      32             :  * \f[
      33             :  * f(\theta,\phi) =
      34             :  * \frac{1}{2} \sum_{l=0}^{l_{max}} \bar{P}_l^0 a(0,l)
      35             :  *   + \sum_{m=1}^{m_{max}} \sum_{l=m}^{l_{max}} \bar{P}_l^m
      36             :  *         \left(  a(m,l) \cos(m \phi) - b(m,l) \sin(m \phi)\right)
      37             :  * \f]
      38             :  * where \f$a(m,l)\f$ and \f$b(m,l)\f$ are the SPHEREPACK
      39             :  * spectral coefficients, and \f$\bar{P}_l^m\f$ are
      40             :  * unit-orthonormal associated Legendre polynomials:
      41             :  * \f[
      42             :  * \bar{P}_l^m =
      43             :  * (-1)^m \sqrt{\frac{(2l+1)(l-m)!}{2(l+m)!}} P_l^m,
      44             :  * \f]
      45             :  * where \f$P_l^m\f$ are the associated Legendre polynomials as defined
      46             :  * for example in Jackson "Classical Electrodynamics".
      47             :  * \example
      48             :  * \snippet Test_SpherepackIterator.cpp spherepack_iterator_example
      49             :  */
      50           1 : class SpherepackIterator {
      51             :  public:
      52             :   /// SPHEREPACK has two coefficient variables, 'a' and 'b', that hold
      53             :   /// the cos(m*phi) and sin(m*phi) parts of the spectral
      54             :   /// coefficients.
      55           0 :   enum class CoefficientArray { a, b };
      56             : 
      57             :   /// Construct a SpherepackIterator.
      58             :   ///
      59             :   /// For all uses of SpherepackIterator by the SPHEREPACK routines,
      60             :   /// zero_m_is_real should be true, indicating that the m=0 values of
      61             :   /// the coefficients are real (or in other words, the 'b' array has
      62             :   /// no m=0 values).
      63             :   ///
      64             :   /// However, we sometimes use the same storage scheme (and hence
      65             :   /// SpherepackIterator) to hold spin-weighted-spherical-harmonic
      66             :   /// coefficients of complex quantities, in which case the m=0 values
      67             :   /// of the coefficients can be complex; in that case, zero_m_is_real
      68             :   /// should be false.
      69           1 :   SpherepackIterator(size_t l_max_input, size_t m_max_input, size_t stride = 1,
      70             :                      bool zero_m_is_real = true);
      71             : 
      72           0 :   size_t l_max() const { return l_max_; }
      73           0 :   size_t m_max() const { return m_max_; }
      74           0 :   size_t n_th() const { return n_th_; }
      75           0 :   size_t n_ph() const { return n_ph_; }
      76           0 :   size_t stride() const { return stride_; }
      77           0 :   bool zero_m_is_real() const { return zero_m_is_real_; }
      78             : 
      79             :   /// Size of a SPHEREPACK coefficient array (a and b combined), not
      80             :   /// counting stride.  For non-unit stride, the size of the array
      81             :   /// should be spherepack_array_size()*stride.
      82           1 :   size_t spherepack_array_size() const {
      83             :     return (l_max_ + 1) * (m_max_ + 1) * 2;
      84             :   }
      85             : 
      86           0 :   SpherepackIterator& operator++() {
      87             :     ASSERT(current_compact_index_ != offset_into_spherepack_array.size(),
      88             :            "Incrementing an invalid iterator: "
      89             :                << current_compact_index_ << " "
      90             :                << offset_into_spherepack_array.size());
      91             :     ++current_compact_index_;
      92             :     return *this;
      93             :   }
      94             : 
      95           0 :   explicit operator bool() const {
      96             :     return current_compact_index_ < offset_into_spherepack_array.size();
      97             :   }
      98             : 
      99             :   /// Current index into a SPHEREPACK coefficient array.
     100           1 :   size_t operator()() const {
     101             :     return offset_into_spherepack_array[current_compact_index_];
     102             :   }
     103             : 
     104             :   /// Given an offset into a SPHEREPACK coefficient array, return the compact
     105             :   /// index corresponding to that offset.
     106             :   ///
     107             :   /// Essentially the inverse of operator(). If the offset points to an element
     108             :   /// that SPHEREPACK doesn't actually use (i.e. no compact index can reach the
     109             :   /// given offset), then a std::nullopt is returned.
     110           1 :   std::optional<size_t> compact_index(const size_t offset) const {
     111             :     return offset_to_compact_index_[offset];
     112             :   }
     113             : 
     114             :   /// Returns the current compact index that SpherepackIterator uses internally.
     115             :   /// This does not index a SPHEREPACK coefficient array. \see operator() for an
     116             :   /// index into a SPHEREPACK coefficient array
     117           1 :   size_t current_compact_index() const { return current_compact_index_; }
     118             : 
     119             :   /// Current values of l and m.
     120           1 :   size_t l() const { return compact_l_[current_compact_index_]; }
     121           0 :   size_t m() const { return compact_m_[current_compact_index_]; }
     122             :   ///  Whether the iterator points to an element of 'a' or 'b',
     123             :   ///  i.e. points to the cos(m*phi) or sin(m*phi) part of a spectral
     124             :   ///  coefficient.
     125           1 :   CoefficientArray coefficient_array() const {
     126             :     return (current_compact_index_ < number_of_valid_entries_in_a_
     127             :                 ? CoefficientArray::a
     128             :                 : CoefficientArray::b);
     129             :   }
     130             : 
     131             :   /// Reset iterator back to beginning value. Returns *this.
     132           1 :   SpherepackIterator& reset();
     133             : 
     134             :   /// Set iterator to specific value of l, m, array.  Returns *this.
     135           1 :   SpherepackIterator& set(size_t l_input, size_t m_input,
     136             :                           CoefficientArray coefficient_array_input);
     137             : 
     138             :   /// Same as 'set' above, but assumes CoefficientArray is 'a' for m>=0 and
     139             :   /// 'b' for m<0.  This is useful when converting between true
     140             :   /// spherical harmonics (which allow negative values of m) and
     141             :   /// SPHEREPACK coefficients (which have only positive values of m,
     142             :   /// but two arrays for sin(m*phi) and cos(m*phi) parts).
     143           1 :   SpherepackIterator& set(size_t l_input, int m_input) {
     144             :     return set(l_input, size_t(abs(m_input)),
     145             :                m_input >= 0 ? CoefficientArray::a : CoefficientArray::b);
     146             :   }
     147             : 
     148             :   /// Set iterator to a specific compact index. Returns *this.
     149           1 :   SpherepackIterator& set(size_t compact_index);
     150             : 
     151             :  private:
     152           0 :   size_t l_max_, m_max_, n_th_, n_ph_, stride_;
     153           0 :   bool zero_m_is_real_;
     154           0 :   size_t number_of_valid_entries_in_a_;
     155           0 :   size_t current_compact_index_;
     156           0 :   std::vector<size_t> offset_into_spherepack_array;
     157           0 :   std::vector<size_t> compact_l_, compact_m_;
     158           0 :   std::vector<std::optional<size_t>> offset_to_compact_index_;
     159             : };
     160             : 
     161           0 : inline bool operator==(const SpherepackIterator& lhs,
     162             :                        const SpherepackIterator& rhs) {
     163             :   return lhs.l_max() == rhs.l_max() and lhs.m_max() == rhs.m_max() and
     164             :          lhs.stride() == rhs.stride() and lhs() == rhs() and
     165             :          lhs.zero_m_is_real() == rhs.zero_m_is_real();
     166             : }
     167             : 
     168           0 : inline bool operator!=(const SpherepackIterator& lhs,
     169             :                        const SpherepackIterator& rhs) {
     170             :   return not(lhs == rhs);
     171             : }
     172             : 
     173           0 : inline std::ostream& operator<<(
     174             :     std::ostream& os,
     175             :     const SpherepackIterator::CoefficientArray& coefficient_array) {
     176             :   return os << (coefficient_array == SpherepackIterator::CoefficientArray::a
     177             :                     ? 'a'
     178             :                     : 'b');
     179             : }
     180             : }  // namespace ylm

Generated by: LCOV version 1.14