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
|