1 #pragma once
2 /**
3  * \file NETGeographicLib/SphericalHarmonic.h
4  * \brief Header for NETGeographicLib::SphericalHarmonic class
5  *
6  * NETGeographicLib is copyright (c) Scott Heiman (2013)
7  * GeographicLib is Copyright (c) Charles Karney (2010-2012)
8  * <charles@karney.com> and licensed under the MIT/X11 License.
9  * For more information, see
10  * https://geographiclib.sourceforge.io/
11  **********************************************************************/
12 
13 namespace NETGeographicLib
14 {
15     ref class CircularEngine;
16     ref class SphericalCoefficients;
17   /**
18    * \brief .NET wrapper for GeographicLib::SphericalHarmonic.
19    *
20    * This class allows .NET applications to access GeographicLib::SphericalHarmonic.
21    *
22    * This class evaluates the spherical harmonic sum \verbatim
23    V(x, y, z) = sum(n = 0..N)[ q^(n+1) * sum(m = 0..n)[
24      (C[n,m] * cos(m*lambda) + S[n,m] * sin(m*lambda)) *
25      P[n,m](cos(theta)) ] ]
26    \endverbatim
27    * where
28    * - <i>p</i><sup>2</sup> = <i>x</i><sup>2</sup> + <i>y</i><sup>2</sup>,
29    * - <i>r</i><sup>2</sup> = <i>p</i><sup>2</sup> + <i>z</i><sup>2</sup>,
30    * - \e q = <i>a</i>/<i>r</i>,
31    * - &theta; = atan2(\e p, \e z) = the spherical \e colatitude,
32    * - &lambda; = atan2(\e y, \e x) = the longitude.
33    * - P<sub>\e nm</sub>(\e t) is the associated Legendre polynomial of degree
34    *   \e n and order \e m.
35    *
36    * Two normalizations are supported for P<sub>\e nm</sub>
37    * - fully normalized denoted by SphericalHarmonic::FULL.
38    * - Schmidt semi-normalized denoted by SphericalHarmonic::SCHMIDT.
39    *
40    * Clenshaw summation is used for the sums over both \e n and \e m.  This
41    * allows the computation to be carried out without the need for any
42    * temporary arrays.  See GeographicLib::SphericalEngine.cpp for more
43    * information on the implementation.
44    *
45    * References:
46    * - C. W. Clenshaw, A note on the summation of Chebyshev series,
47    *   %Math. Tables Aids Comput. 9(51), 118--120 (1955).
48    * - R. E. Deakin, Derivatives of the earth's potentials, Geomatics
49    *   Research Australasia 68, 31--60, (June 1998).
50    * - W. A. Heiskanen and H. Moritz, Physical Geodesy, (Freeman, San
51    *   Francisco, 1967).  (See Sec. 1-14, for a definition of Pbar.)
52    * - S. A. Holmes and W. E. Featherstone, A unified approach to the Clenshaw
53    *   summation and the recursive computation of very high degree and order
54    *   normalised associated Legendre functions, J. Geodesy 76(5),
55    *   279--299 (2002).
56    * - C. C. Tscherning and K. Poder, Some geodetic applications of Clenshaw
57    *   summation, Boll. Geod. Sci. Aff. 41(4), 349--375 (1982).
58    *
59    * C# Example:
60    * \include example-SphericalHarmonic.cs
61    * Managed C++ Example:
62    * \include example-SphericalHarmonic.cpp
63    * Visual Basic Example:
64    * \include example-SphericalHarmonic.vb
65    *
66    * <B>INTERFACE DIFFERENCES:</B><BR>
67    * This class replaces the GeographicLib::SphericalHarmonic::operator() with
68    * HarmonicSum.
69    *
70    * Coefficients returns a SphericalCoefficients object.
71    *
72    * The Normalization parameter in the constructors is passed in as an
73    * enumeration rather than an unsigned.
74    **********************************************************************/
75     public ref class SphericalHarmonic
76     {
77         private:
78         // a pointer to the unmanaged GeographicLib::SphericalHarmonic
79         const GeographicLib::SphericalHarmonic* m_pSphericalHarmonic;
80         // the finalizer frees the unmanaged memory when the object is destroyed.
81         !SphericalHarmonic();
82         // local containers for the cosine and sine coefficients.  The
83         // GeographicLib::SphericalEngine::coeffs class uses a
84         // std::vector::iterator to access these vectors.
85         std::vector<double> *m_C, *m_S;
86     public:
87         /**
88          * Supported normalizations for the associated Legendre polynomials.
89          **********************************************************************/
90         enum class Normalization {
91           /**
92            * Fully normalized associated Legendre polynomials.
93            *
94            * These are defined by <i>P</i><sub><i>nm</i></sub><sup>full</sup>(\e z)
95            * = (&minus;1)<sup><i>m</i></sup> sqrt(\e k (2\e n + 1) (\e n &minus; \e
96            * m)! / (\e n + \e m)!)
97            * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z), where
98            * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z) is Ferrers
99            * function (also known as the Legendre function on the cut or the
100            * associated Legendre polynomial) https://dlmf.nist.gov/14.7.E10 and \e k
101            * = 1 for \e m = 0 and \e k = 2 otherwise.
102            *
103            * The mean squared value of
104            * <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cos&theta;)
105            * cos(<i>m</i>&lambda;) and
106            * <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cos&theta;)
107            * sin(<i>m</i>&lambda;) over the sphere is 1.
108            *
109            * @hideinitializer
110            **********************************************************************/
111           FULL = GeographicLib::SphericalEngine::FULL,
112           /**
113            * Schmidt semi-normalized associated Legendre polynomials.
114            *
115            * These are defined by <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(\e
116            * z) = (&minus;1)<sup><i>m</i></sup> sqrt(\e k (\e n &minus; \e m)! /
117            * (\e n + \e m)!)  <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z),
118            * where <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z) is Ferrers
119            * function (also known as the Legendre function on the cut or the
120            * associated Legendre polynomial) https://dlmf.nist.gov/14.7.E10 and \e k
121            * = 1 for \e m = 0 and \e k = 2 otherwise.
122            *
123            * The mean squared value of
124            * <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cos&theta;)
125            * cos(<i>m</i>&lambda;) and
126            * <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cos&theta;)
127            * sin(<i>m</i>&lambda;) over the sphere is 1/(2\e n + 1).
128            *
129            * @hideinitializer
130            **********************************************************************/
131           SCHMIDT = GeographicLib::SphericalEngine::SCHMIDT,
132         };
133 
134         /**
135          * Constructor with a full set of coefficients specified.
136          *
137          * @param[in] C the coefficients \e C<sub>\e nm</sub>.
138          * @param[in] S the coefficients \e S<sub>\e nm</sub>.
139          * @param[in] N the maximum degree and order of the sum
140          * @param[in] a the reference radius appearing in the definition of the
141          *   sum.
142          * @param[in] norm the normalization for the associated Legendre
143          *   polynomials, either SphericalHarmonic::full (the default) or
144          *   SphericalHarmonic::schmidt.
145          * @exception GeographicErr if \e N does not satisfy \e N &ge; &minus;1.
146          * @exception GeographicErr if \e C or \e S is not big enough to hold the
147          *   coefficients.
148          *
149          * The coefficients \e C<sub>\e nm</sub> and \e S<sub>\e nm</sub> are
150          * stored in the one-dimensional vectors \e C and \e S which must contain
151          * (\e N + 1)(\e N + 2)/2 and N (\e N + 1)/2 elements, respectively, stored
152          * in "column-major" order.  Thus for \e N = 3, the order would be:
153          * <i>C</i><sub>00</sub>,
154          * <i>C</i><sub>10</sub>,
155          * <i>C</i><sub>20</sub>,
156          * <i>C</i><sub>30</sub>,
157          * <i>C</i><sub>11</sub>,
158          * <i>C</i><sub>21</sub>,
159          * <i>C</i><sub>31</sub>,
160          * <i>C</i><sub>22</sub>,
161          * <i>C</i><sub>32</sub>,
162          * <i>C</i><sub>33</sub>.
163          * In general the (\e n,\e m) element is at index \e m \e N &minus; \e m
164          * (\e m &minus; 1)/2 + \e n.  The layout of \e S is the same except that
165          * the first column is omitted (since the \e m = 0 terms never contribute
166          * to the sum) and the 0th element is <i>S</i><sub>11</sub>
167          *
168          * The class stores <i>pointers</i> to the first elements of \e C and \e S.
169          * These arrays should not be altered or destroyed during the lifetime of a
170          * SphericalHarmonic object.
171          **********************************************************************/
172         SphericalHarmonic(array<double>^ C,
173                           array<double>^ S,
174                           int N, double a, Normalization norm );
175 
176         /**
177          * Constructor with a subset of coefficients specified.
178          *
179          * @param[in] C the coefficients \e C<sub>\e nm</sub>.
180          * @param[in] S the coefficients \e S<sub>\e nm</sub>.
181          * @param[in] N the degree used to determine the layout of \e C and \e S.
182          * @param[in] nmx the maximum degree used in the sum.  The sum over \e n is
183          *   from 0 thru \e nmx.
184          * @param[in] mmx the maximum order used in the sum.  The sum over \e m is
185          *   from 0 thru min(\e n, \e mmx).
186          * @param[in] a the reference radius appearing in the definition of the
187          *   sum.
188          * @param[in] norm the normalization for the associated Legendre
189          *   polynomials, either SphericalHarmonic::FULL (the default) or
190          *   SphericalHarmonic::SCHMIDT.
191          * @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy
192          *   \e N &ge; \e nmx &ge; \e mmx &ge; &minus;1.
193          * @exception GeographicErr if \e C or \e S is not big enough to hold the
194          *   coefficients.
195          *
196          * The class stores <i>pointers</i> to the first elements of \e C and \e S.
197          * These arrays should not be altered or destroyed during the lifetime of a
198          * SphericalHarmonic object.
199          **********************************************************************/
200         SphericalHarmonic(array<double>^ C,
201                           array<double>^ S,
202                           int N, int nmx, int mmx,
203                           double a, Normalization norm);
204 
205         /**
206          * The destructor calls the finalizer
207          **********************************************************************/
~SphericalHarmonic()208         ~SphericalHarmonic() { this->!SphericalHarmonic(); }
209 
210         /**
211          * Compute the spherical harmonic sum.
212          *
213          * @param[in] x cartesian coordinate.
214          * @param[in] y cartesian coordinate.
215          * @param[in] z cartesian coordinate.
216          * @return \e V the spherical harmonic sum.
217          *
218          * This routine requires constant memory and thus never throws an
219          * exception.
220          **********************************************************************/
221         double HarmonicSum(double x, double y, double z);
222 
223         /**
224          * Compute a spherical harmonic sum and its gradient.
225          *
226          * @param[in] x cartesian coordinate.
227          * @param[in] y cartesian coordinate.
228          * @param[in] z cartesian coordinate.
229          * @param[out] gradx \e x component of the gradient
230          * @param[out] grady \e y component of the gradient
231          * @param[out] gradz \e z component of the gradient
232          * @return \e V the spherical harmonic sum.
233          *
234          * This is the same as the previous function, except that the components of
235          * the gradients of the sum in the \e x, \e y, and \e z directions are
236          * computed.  This routine requires constant memory and thus never throws
237          * an exception.
238          **********************************************************************/
239         double HarmonicSum(double x, double y, double z,
240             [System::Runtime::InteropServices::Out] double% gradx,
241             [System::Runtime::InteropServices::Out] double% grady,
242             [System::Runtime::InteropServices::Out] double% gradz);
243 
244         /**
245          * Create a CircularEngine to allow the efficient evaluation of several
246          * points on a circle of latitude.
247          *
248          * @param[in] p the radius of the circle.
249          * @param[in] z the height of the circle above the equatorial plane.
250          * @param[in] gradp if true the returned object will be able to compute the
251          *   gradient of the sum.
252          * @exception std::bad_alloc if the memory for the CircularEngine can't be
253          *   allocated.
254          * @return the CircularEngine object.
255          *
256          * SphericalHarmonic::operator()() exchanges the order of the sums in the
257          * definition, i.e., &sum;<sub>n = 0..N</sub> &sum;<sub>m = 0..n</sub>
258          * becomes &sum;<sub>m = 0..N</sub> &sum;<sub>n = m..N</sub>.
259          * SphericalHarmonic::Circle performs the inner sum over degree \e n (which
260          * entails about <i>N</i><sup>2</sup> operations).  Calling
261          * CircularEngine::operator()() on the returned object performs the outer
262          * sum over the order \e m (about \e N operations).
263          **********************************************************************/
264         CircularEngine^ Circle(double p, double z, bool gradp);
265 
266         /**
267         * @return the zeroth SphericalCoefficients object.
268         **********************************************************************/
269         SphericalCoefficients^ Coefficients();
270     };
271 } // namespace NETGeographicLib
272