GeographicLib 2.1.2
SphericalHarmonic.hpp
Go to the documentation of this file.
1/**
2 * \file SphericalHarmonic.hpp
3 * \brief Header for GeographicLib::SphericalHarmonic class
4 *
5 * Copyright (c) Charles Karney (2011-2019) <charles@karney.com> and licensed
6 * under the MIT/X11 License. For more information, see
7 * https://geographiclib.sourceforge.io/
8 **********************************************************************/
9
10#if !defined(GEOGRAPHICLIB_SPHERICALHARMONIC_HPP)
11#define GEOGRAPHICLIB_SPHERICALHARMONIC_HPP 1
12
13#include <vector>
17
18namespace GeographicLib {
19
20 /**
21 * \brief Spherical harmonic series
22 *
23 * This class evaluates the spherical harmonic sum \verbatim
24 V(x, y, z) = sum(n = 0..N)[ q^(n+1) * sum(m = 0..n)[
25 (C[n,m] * cos(m*lambda) + S[n,m] * sin(m*lambda)) *
26 P[n,m](cos(theta)) ] ]
27 \endverbatim
28 * where
29 * - <i>p</i><sup>2</sup> = <i>x</i><sup>2</sup> + <i>y</i><sup>2</sup>,
30 * - <i>r</i><sup>2</sup> = <i>p</i><sup>2</sup> + <i>z</i><sup>2</sup>,
31 * - \e q = <i>a</i>/<i>r</i>,
32 * - &theta; = atan2(\e p, \e z) = the spherical \e colatitude,
33 * - &lambda; = atan2(\e y, \e x) = the longitude.
34 * - P<sub><i>nm</i></sub>(\e t) is the associated Legendre polynomial of
35 * degree \e n and order \e m.
36 *
37 * Two normalizations are supported for P<sub><i>nm</i></sub>
38 * - fully normalized denoted by SphericalHarmonic::FULL.
39 * - Schmidt semi-normalized denoted by SphericalHarmonic::SCHMIDT.
40 *
41 * Clenshaw summation is used for the sums over both \e n and \e m. This
42 * allows the computation to be carried out without the need for any
43 * temporary arrays. See SphericalEngine.cpp for more information on the
44 * implementation.
45 *
46 * References:
47 * - C. W. Clenshaw,
48 * <a href="https://doi.org/10.1090/S0025-5718-1955-0071856-0">
49 * A note on the summation of Chebyshev series</a>,
50 * %Math. Tables Aids Comput. 9(51), 118--120 (1955).
51 * - R. E. Deakin, Derivatives of the earth's potentials, Geomatics
52 * Research Australasia 68, 31--60, (June 1998).
53 * - W. A. Heiskanen and H. Moritz, Physical Geodesy, (Freeman, San
54 * Francisco, 1967). (See Sec. 1-14, for a definition of Pbar.)
55 * - S. A. Holmes and W. E. Featherstone,
56 * <a href="https://doi.org/10.1007/s00190-002-0216-2">
57 * A unified approach to the Clenshaw summation and the recursive
58 * computation of very high degree and order normalised associated Legendre
59 * functions</a>, J. Geodesy 76(5), 279--299 (2002).
60 * - C. C. Tscherning and K. Poder,
61 * <a href="http://cct.gfy.ku.dk/publ_cct/cct80.pdf">
62 * Some geodetic applications of Clenshaw summation</a>,
63 * Boll. Geod. Sci. Aff. 41(4), 349--375 (1982).
64 *
65 * Example of use:
66 * \include example-SphericalHarmonic.cpp
67 **********************************************************************/
68
70 public:
71 /**
72 * Supported normalizations for the associated Legendre polynomials.
73 **********************************************************************/
75 /**
76 * Fully normalized associated Legendre polynomials.
77 *
78 * These are defined by
79 * <i>P</i><sub><i>nm</i></sub><sup>full</sup>(\e z)
80 * = (&minus;1)<sup><i>m</i></sup>
81 * sqrt(\e k (2\e n + 1) (\e n &minus; \e m)! / (\e n + \e m)!)
82 * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z), where
83 * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z) is Ferrers
84 * function (also known as the Legendre function on the cut or the
85 * associated Legendre polynomial) https://dlmf.nist.gov/14.7.E10 and
86 * \e k = 1 for \e m = 0 and \e k = 2 otherwise.
87 *
88 * The mean squared value of
89 * <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cos&theta;)
90 * cos(<i>m</i>&lambda;) and
91 * <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cos&theta;)
92 * sin(<i>m</i>&lambda;) over the sphere is 1.
93 *
94 * @hideinitializer
95 **********************************************************************/
97 /**
98 * Schmidt semi-normalized associated Legendre polynomials.
99 *
100 * These are defined by
101 * <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(\e z)
102 * = (&minus;1)<sup><i>m</i></sup>
103 * sqrt(\e k (\e n &minus; \e m)! / (\e n + \e m)!)
104 * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z), where
105 * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z) is Ferrers
106 * function (also known as the Legendre function on the cut or the
107 * associated Legendre polynomial) https://dlmf.nist.gov/14.7.E10 and
108 * \e k = 1 for \e m = 0 and \e k = 2 otherwise.
109 *
110 * The mean squared value of
111 * <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cos&theta;)
112 * cos(<i>m</i>&lambda;) and
113 * <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cos&theta;)
114 * sin(<i>m</i>&lambda;) over the sphere is 1/(2\e n + 1).
115 *
116 * @hideinitializer
117 **********************************************************************/
119 };
120
121 private:
122 typedef Math::real real;
124 real _a;
125 unsigned _norm;
126
127 public:
128 /**
129 * Constructor with a full set of coefficients specified.
130 *
131 * @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
132 * @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
133 * @param[in] N the maximum degree and order of the sum
134 * @param[in] a the reference radius appearing in the definition of the
135 * sum.
136 * @param[in] norm the normalization for the associated Legendre
137 * polynomials, either SphericalHarmonic::FULL (the default) or
138 * SphericalHarmonic::SCHMIDT.
139 * @exception GeographicErr if \e N does not satisfy \e N &ge; &minus;1.
140 * @exception GeographicErr if \e C or \e S is not big enough to hold the
141 * coefficients.
142 *
143 * The coefficients <i>C</i><sub><i>nm</i></sub> and
144 * <i>S</i><sub><i>nm</i></sub> are stored in the one-dimensional vectors
145 * \e C and \e S which must contain (\e N + 1)(\e N + 2)/2 and \e N (\e N +
146 * 1)/2 elements, respectively, stored in "column-major" order. Thus for
147 * \e N = 3, the order would be:
148 * <i>C</i><sub>00</sub>,
149 * <i>C</i><sub>10</sub>,
150 * <i>C</i><sub>20</sub>,
151 * <i>C</i><sub>30</sub>,
152 * <i>C</i><sub>11</sub>,
153 * <i>C</i><sub>21</sub>,
154 * <i>C</i><sub>31</sub>,
155 * <i>C</i><sub>22</sub>,
156 * <i>C</i><sub>32</sub>,
157 * <i>C</i><sub>33</sub>.
158 * In general the (\e n,\e m) element is at index \e m \e N &minus; \e m
159 * (\e m &minus; 1)/2 + \e n. The layout of \e S is the same except that
160 * the first column is omitted (since the \e m = 0 terms never contribute
161 * to the sum) and the 0th element is <i>S</i><sub>11</sub>
162 *
163 * The class stores <i>pointers</i> to the first elements of \e C and \e S.
164 * These arrays should not be altered or destroyed during the lifetime of a
165 * SphericalHarmonic object.
166 **********************************************************************/
167 SphericalHarmonic(const std::vector<real>& C,
168 const std::vector<real>& S,
169 int N, real a, unsigned norm = FULL)
170 : _a(a)
171 , _norm(norm)
172 { _c[0] = SphericalEngine::coeff(C, S, N); }
173
174 /**
175 * Constructor with a subset of coefficients specified.
176 *
177 * @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
178 * @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
179 * @param[in] N the degree used to determine the layout of \e C and \e S.
180 * @param[in] nmx the maximum degree used in the sum. The sum over \e n is
181 * from 0 thru \e nmx.
182 * @param[in] mmx the maximum order used in the sum. The sum over \e m is
183 * from 0 thru min(\e n, \e mmx).
184 * @param[in] a the reference radius appearing in the definition of the
185 * sum.
186 * @param[in] norm the normalization for the associated Legendre
187 * polynomials, either SphericalHarmonic::FULL (the default) or
188 * SphericalHarmonic::SCHMIDT.
189 * @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy
190 * \e N &ge; \e nmx &ge; \e mmx &ge; &minus;1.
191 * @exception GeographicErr if \e C or \e S is not big enough to hold the
192 * coefficients.
193 *
194 * The class stores <i>pointers</i> to the first elements of \e C and \e S.
195 * These arrays should not be altered or destroyed during the lifetime of a
196 * SphericalHarmonic object.
197 **********************************************************************/
198 SphericalHarmonic(const std::vector<real>& C,
199 const std::vector<real>& S,
200 int N, int nmx, int mmx,
201 real a, unsigned norm = FULL)
202 : _a(a)
203 , _norm(norm)
204 { _c[0] = SphericalEngine::coeff(C, S, N, nmx, mmx); }
205
206 /**
207 * A default constructor so that the object can be created when the
208 * constructor for another object is initialized. This default object can
209 * then be reset with the default copy assignment operator.
210 **********************************************************************/
212
213 /**
214 * Compute the spherical harmonic sum.
215 *
216 * @param[in] x cartesian coordinate.
217 * @param[in] y cartesian coordinate.
218 * @param[in] z cartesian coordinate.
219 * @return \e V the spherical harmonic sum.
220 *
221 * This routine requires constant memory and thus never throws an
222 * exception.
223 **********************************************************************/
224 Math::real operator()(real x, real y, real z) const {
225 real f[] = {1};
226 real v = 0;
227 real dummy;
228 switch (_norm) {
229 case FULL:
230 v = SphericalEngine::Value<false, SphericalEngine::FULL, 1>
231 (_c, f, x, y, z, _a, dummy, dummy, dummy);
232 break;
233 case SCHMIDT:
234 default: // To avoid compiler warnings
235 v = SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 1>
236 (_c, f, x, y, z, _a, dummy, dummy, dummy);
237 break;
238 }
239 return v;
240 }
241
242 /**
243 * Compute a spherical harmonic sum and its gradient.
244 *
245 * @param[in] x cartesian coordinate.
246 * @param[in] y cartesian coordinate.
247 * @param[in] z cartesian coordinate.
248 * @param[out] gradx \e x component of the gradient
249 * @param[out] grady \e y component of the gradient
250 * @param[out] gradz \e z component of the gradient
251 * @return \e V the spherical harmonic sum.
252 *
253 * This is the same as the previous function, except that the components of
254 * the gradients of the sum in the \e x, \e y, and \e z directions are
255 * computed. This routine requires constant memory and thus never throws
256 * an exception.
257 **********************************************************************/
258 Math::real operator()(real x, real y, real z,
259 real& gradx, real& grady, real& gradz) const {
260 real f[] = {1};
261 real v = 0;
262 switch (_norm) {
263 case FULL:
264 v = SphericalEngine::Value<true, SphericalEngine::FULL, 1>
265 (_c, f, x, y, z, _a, gradx, grady, gradz);
266 break;
267 case SCHMIDT:
268 default: // To avoid compiler warnings
269 v = SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 1>
270 (_c, f, x, y, z, _a, gradx, grady, gradz);
271 break;
272 }
273 return v;
274 }
275
276 /**
277 * Create a CircularEngine to allow the efficient evaluation of several
278 * points on a circle of latitude.
279 *
280 * @param[in] p the radius of the circle.
281 * @param[in] z the height of the circle above the equatorial plane.
282 * @param[in] gradp if true the returned object will be able to compute the
283 * gradient of the sum.
284 * @exception std::bad_alloc if the memory for the CircularEngine can't be
285 * allocated.
286 * @return the CircularEngine object.
287 *
288 * SphericalHarmonic::operator()() exchanges the order of the sums in the
289 * definition, i.e., &sum;<sub><i>n</i> = 0..<i>N</i></sub>
290 * &sum;<sub><i>m</i> = 0..<i>n</i></sub> becomes &sum;<sub><i>m</i> =
291 * 0..<i>N</i></sub> &sum;<sub><i>n</i> = <i>m</i>..<i>N</i></sub>.
292 * SphericalHarmonic::Circle performs the inner sum over degree \e n (which
293 * entails about <i>N</i><sup>2</sup> operations). Calling
294 * CircularEngine::operator()() on the returned object performs the outer
295 * sum over the order \e m (about \e N operations).
296 *
297 * Here's an example of computing the spherical sum at a sequence of
298 * longitudes without using a CircularEngine object \code
299 SphericalHarmonic h(...); // Create the SphericalHarmonic object
300 double r = 2, lat = 33, lon0 = 44, dlon = 0.01;
301 double
302 phi = lat * Math::degree<double>(),
303 z = r * sin(phi), p = r * cos(phi);
304 for (int i = 0; i <= 100; ++i) {
305 real
306 lon = lon0 + i * dlon,
307 lam = lon * Math::degree<double>();
308 std::cout << lon << " " << h(p * cos(lam), p * sin(lam), z) << "\n";
309 }
310 \endcode
311 * Here is the same calculation done using a CircularEngine object. This
312 * will be about <i>N</i>/2 times faster. \code
313 SphericalHarmonic h(...); // Create the SphericalHarmonic object
314 double r = 2, lat = 33, lon0 = 44, dlon = 0.01;
315 double
316 phi = lat * Math::degree<double>(),
317 z = r * sin(phi), p = r * cos(phi);
318 CircularEngine c(h(p, z, false)); // Create the CircularEngine object
319 for (int i = 0; i <= 100; ++i) {
320 real
321 lon = lon0 + i * dlon;
322 std::cout << lon << " " << c(lon) << "\n";
323 }
324 \endcode
325 **********************************************************************/
326 CircularEngine Circle(real p, real z, bool gradp) const {
327 real f[] = {1};
328 switch (_norm) {
329 case FULL:
330 return gradp ?
331 SphericalEngine::Circle<true, SphericalEngine::FULL, 1>
332 (_c, f, p, z, _a) :
333 SphericalEngine::Circle<false, SphericalEngine::FULL, 1>
334 (_c, f, p, z, _a);
335 break;
336 case SCHMIDT:
337 default: // To avoid compiler warnings
338 return gradp ?
339 SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 1>
340 (_c, f, p, z, _a) :
341 SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 1>
342 (_c, f, p, z, _a);
343 break;
344 }
345 }
346
347 /**
348 * @return the zeroth SphericalEngine::coeff object.
349 **********************************************************************/
351 { return _c[0]; }
352 };
353
354} // namespace GeographicLib
355
356#endif // GEOGRAPHICLIB_SPHERICALHARMONIC_HPP
Header for GeographicLib::CircularEngine class.
Header for GeographicLib::Constants class.
#define GEOGRAPHICLIB_EXPORT
Definition: Constants.hpp:67
GeographicLib::Math::real real
Definition: GeodSolve.cpp:31
Header for GeographicLib::SphericalEngine class.
Spherical harmonic sums for a circle.
Package up coefficients for SphericalEngine.
Spherical harmonic series.
Math::real operator()(real x, real y, real z) const
Math::real operator()(real x, real y, real z, real &gradx, real &grady, real &gradz) const
SphericalHarmonic(const std::vector< real > &C, const std::vector< real > &S, int N, int nmx, int mmx, real a, unsigned norm=FULL)
const SphericalEngine::coeff & Coefficients() const
CircularEngine Circle(real p, real z, bool gradp) const
SphericalHarmonic(const std::vector< real > &C, const std::vector< real > &S, int N, real a, unsigned norm=FULL)
Namespace for GeographicLib.
Definition: Accumulator.cpp:12