123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- /**
- * \file SphericalEngine.hpp
- * \brief Header for GeographicLib::SphericalEngine class
- *
- * Copyright (c) Charles Karney (2011-2019) <charles@karney.com> and licensed
- * under the MIT/X11 License. For more information, see
- * https://geographiclib.sourceforge.io/
- **********************************************************************/
- #if !defined(GEOGRAPHICLIB_SPHERICALENGINE_HPP)
- #define GEOGRAPHICLIB_SPHERICALENGINE_HPP 1
- #include <vector>
- #include <istream>
- #include <GeographicLib/Constants.hpp>
- #if defined(_MSC_VER)
- // Squelch warnings about dll vs vector
- # pragma warning (push)
- # pragma warning (disable: 4251)
- #endif
- namespace GeographicLib {
- class CircularEngine;
- /**
- * \brief The evaluation engine for SphericalHarmonic
- *
- * This serves as the backend to SphericalHarmonic, SphericalHarmonic1, and
- * SphericalHarmonic2. Typically end-users will not have to access this
- * class directly.
- *
- * See SphericalEngine.cpp for more information on the implementation.
- *
- * Example of use:
- * \include example-SphericalEngine.cpp
- **********************************************************************/
- class GEOGRAPHICLIB_EXPORT SphericalEngine {
- private:
- typedef Math::real real;
- // CircularEngine needs access to sqrttable, scale
- friend class CircularEngine;
- // Return the table of the square roots of integers
- static std::vector<real>& sqrttable();
- // An internal scaling of the coefficients to avoid overflow in
- // intermediate calculations.
- static real scale() {
- using std::pow;
- static const real
- // Need extra real because, since C++11, pow(float, int) returns double
- s = real(pow(real(std::numeric_limits<real>::radix),
- -3 * (std::numeric_limits<real>::max_exponent < (1<<14) ?
- std::numeric_limits<real>::max_exponent : (1<<14))
- / 5));
- return s;
- }
- // Move latitudes near the pole off the axis by this amount.
- static real eps() {
- using std::sqrt;
- return std::numeric_limits<real>::epsilon() *
- sqrt(std::numeric_limits<real>::epsilon());
- }
- SphericalEngine(); // Disable constructor
- public:
- /**
- * Supported normalizations for associated Legendre polynomials.
- **********************************************************************/
- enum normalization {
- /**
- * Fully normalized associated Legendre polynomials. See
- * SphericalHarmonic::FULL for documentation.
- *
- * @hideinitializer
- **********************************************************************/
- FULL = 0,
- /**
- * Schmidt semi-normalized associated Legendre polynomials. See
- * SphericalHarmonic::SCHMIDT for documentation.
- *
- * @hideinitializer
- **********************************************************************/
- SCHMIDT = 1,
- };
- /**
- * \brief Package up coefficients for SphericalEngine
- *
- * This packages up the \e C, \e S coefficients and information about how
- * the coefficients are stored into a single structure. This allows a
- * vector of type SphericalEngine::coeff to be passed to
- * SphericalEngine::Value. This class also includes functions to aid
- * indexing into \e C and \e S.
- *
- * The storage layout of the coefficients is documented in
- * SphericalHarmonic and SphericalHarmonic::SphericalHarmonic.
- **********************************************************************/
- class GEOGRAPHICLIB_EXPORT coeff {
- private:
- int _Nx, _nmx, _mmx;
- std::vector<real>::const_iterator _Cnm;
- std::vector<real>::const_iterator _Snm;
- public:
- /**
- * A default constructor
- **********************************************************************/
- coeff() : _Nx(-1) , _nmx(-1) , _mmx(-1) {}
- /**
- * The general constructor.
- *
- * @param[in] C a vector of coefficients for the cosine terms.
- * @param[in] S a vector of coefficients for the sine terms.
- * @param[in] N the degree giving storage layout for \e C and \e S.
- * @param[in] nmx the maximum degree to be used.
- * @param[in] mmx the maximum order to be used.
- * @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy
- * \e N ≥ \e nmx ≥ \e mmx ≥ −1.
- * @exception GeographicErr if \e C or \e S is not big enough to hold the
- * coefficients.
- * @exception std::bad_alloc if the memory for the square root table
- * can't be allocated.
- **********************************************************************/
- coeff(const std::vector<real>& C,
- const std::vector<real>& S,
- int N, int nmx, int mmx)
- : _Nx(N)
- , _nmx(nmx)
- , _mmx(mmx)
- , _Cnm(C.begin())
- , _Snm(S.begin())
- {
- if (!((_Nx >= _nmx && _nmx >= _mmx && _mmx >= 0) ||
- // If mmx = -1 then the sums are empty so require nmx = -1 also.
- (_nmx == -1 && _mmx == -1)))
- throw GeographicErr("Bad indices for coeff");
- if (!(index(_nmx, _mmx) < int(C.size()) &&
- index(_nmx, _mmx) < int(S.size()) + (_Nx + 1)))
- throw GeographicErr("Arrays too small in coeff");
- SphericalEngine::RootTable(_nmx);
- }
- /**
- * The constructor for full coefficient vectors.
- *
- * @param[in] C a vector of coefficients for the cosine terms.
- * @param[in] S a vector of coefficients for the sine terms.
- * @param[in] N the maximum degree and order.
- * @exception GeographicErr if \e N does not satisfy \e N ≥ −1.
- * @exception GeographicErr if \e C or \e S is not big enough to hold the
- * coefficients.
- * @exception std::bad_alloc if the memory for the square root table
- * can't be allocated.
- **********************************************************************/
- coeff(const std::vector<real>& C,
- const std::vector<real>& S,
- int N)
- : _Nx(N)
- , _nmx(N)
- , _mmx(N)
- , _Cnm(C.begin())
- , _Snm(S.begin())
- {
- if (!(_Nx >= -1))
- throw GeographicErr("Bad indices for coeff");
- if (!(index(_nmx, _mmx) < int(C.size()) &&
- index(_nmx, _mmx) < int(S.size()) + (_Nx + 1)))
- throw GeographicErr("Arrays too small in coeff");
- SphericalEngine::RootTable(_nmx);
- }
- /**
- * @return \e N the degree giving storage layout for \e C and \e S.
- **********************************************************************/
- int N() const { return _Nx; }
- /**
- * @return \e nmx the maximum degree to be used.
- **********************************************************************/
- int nmx() const { return _nmx; }
- /**
- * @return \e mmx the maximum order to be used.
- **********************************************************************/
- int mmx() const { return _mmx; }
- /**
- * The one-dimensional index into \e C and \e S.
- *
- * @param[in] n the degree.
- * @param[in] m the order.
- * @return the one-dimensional index.
- **********************************************************************/
- int index(int n, int m) const
- { return m * _Nx - m * (m - 1) / 2 + n; }
- /**
- * An element of \e C.
- *
- * @param[in] k the one-dimensional index.
- * @return the value of the \e C coefficient.
- **********************************************************************/
- Math::real Cv(int k) const { return *(_Cnm + k); }
- /**
- * An element of \e S.
- *
- * @param[in] k the one-dimensional index.
- * @return the value of the \e S coefficient.
- **********************************************************************/
- Math::real Sv(int k) const { return *(_Snm + (k - (_Nx + 1))); }
- /**
- * An element of \e C with checking.
- *
- * @param[in] k the one-dimensional index.
- * @param[in] n the requested degree.
- * @param[in] m the requested order.
- * @param[in] f a multiplier.
- * @return the value of the \e C coefficient multiplied by \e f in \e n
- * and \e m are in range else 0.
- **********************************************************************/
- Math::real Cv(int k, int n, int m, real f) const
- { return m > _mmx || n > _nmx ? 0 : *(_Cnm + k) * f; }
- /**
- * An element of \e S with checking.
- *
- * @param[in] k the one-dimensional index.
- * @param[in] n the requested degree.
- * @param[in] m the requested order.
- * @param[in] f a multiplier.
- * @return the value of the \e S coefficient multiplied by \e f in \e n
- * and \e m are in range else 0.
- **********************************************************************/
- Math::real Sv(int k, int n, int m, real f) const
- { return m > _mmx || n > _nmx ? 0 : *(_Snm + (k - (_Nx + 1))) * f; }
- /**
- * The size of the coefficient vector for the cosine terms.
- *
- * @param[in] N the maximum degree.
- * @param[in] M the maximum order.
- * @return the size of the vector of cosine terms as stored in column
- * major order.
- **********************************************************************/
- static int Csize(int N, int M)
- { return (M + 1) * (2 * N - M + 2) / 2; }
- /**
- * The size of the coefficient vector for the sine terms.
- *
- * @param[in] N the maximum degree.
- * @param[in] M the maximum order.
- * @return the size of the vector of cosine terms as stored in column
- * major order.
- **********************************************************************/
- static int Ssize(int N, int M)
- { return Csize(N, M) - (N + 1); }
- /**
- * Load coefficients from a binary stream.
- *
- * @param[in] stream the input stream.
- * @param[in,out] N The maximum degree of the coefficients.
- * @param[in,out] M The maximum order of the coefficients.
- * @param[out] C The vector of cosine coefficients.
- * @param[out] S The vector of sine coefficients.
- * @param[in] truncate if false (the default) then \e N and \e M are
- * determined by the values in the binary stream; otherwise, the input
- * values of \e N and \e M are used to truncate the coefficients read
- * from the stream at the given degree and order.
- * @exception GeographicErr if \e N and \e M do not satisfy \e N ≥
- * \e M ≥ −1.
- * @exception GeographicErr if there's an error reading the data.
- * @exception std::bad_alloc if the memory for \e C or \e S can't be
- * allocated.
- *
- * \e N and \e M are read as 4-byte ints. \e C and \e S are resized to
- * accommodate all the coefficients (with the \e m = 0 coefficients for
- * \e S excluded) and the data for these coefficients read as 8-byte
- * doubles. The coefficients are stored in column major order. The
- * bytes in the stream should use little-endian ordering. IEEE floating
- * point is assumed for the coefficients.
- **********************************************************************/
- static void readcoeffs(std::istream& stream, int& N, int& M,
- std::vector<real>& C, std::vector<real>& S,
- bool truncate = false);
- };
- /**
- * Evaluate a spherical harmonic sum and its gradient.
- *
- * @tparam gradp should the gradient be calculated.
- * @tparam norm the normalization for the associated Legendre polynomials.
- * @tparam L the number of terms in the coefficients.
- * @param[in] c an array of coeff objects.
- * @param[in] f array of coefficient multipliers. f[0] should be 1.
- * @param[in] x the \e x component of the cartesian position.
- * @param[in] y the \e y component of the cartesian position.
- * @param[in] z the \e z component of the cartesian position.
- * @param[in] a the normalizing radius.
- * @param[out] gradx the \e x component of the gradient.
- * @param[out] grady the \e y component of the gradient.
- * @param[out] gradz the \e z component of the gradient.
- * @result the spherical harmonic sum.
- *
- * See the SphericalHarmonic class for the definition of the sum. The
- * coefficients used by this function are, for example, c[0].Cv + f[1] *
- * c[1].Cv + ... + f[L−1] * c[L−1].Cv. (Note that f[0] is \e
- * not used.) The upper limits on the sum are determined by c[0].nmx() and
- * c[0].mmx(); these limits apply to \e all the components of the
- * coefficients. The parameters \e gradp, \e norm, and \e L are template
- * parameters, to allow more optimization to be done at compile time.
- *
- * Clenshaw summation is used which permits the evaluation of the sum
- * without the need to allocate temporary arrays. Thus this function never
- * throws an exception.
- **********************************************************************/
- template<bool gradp, normalization norm, int L>
- static Math::real Value(const coeff c[], const real f[],
- real x, real y, real z, real a,
- real& gradx, real& grady, real& gradz);
- /**
- * Create a CircularEngine object
- *
- * @tparam gradp should the gradient be calculated.
- * @tparam norm the normalization for the associated Legendre polynomials.
- * @tparam L the number of terms in the coefficients.
- * @param[in] c an array of coeff objects.
- * @param[in] f array of coefficient multipliers. f[0] should be 1.
- * @param[in] p the radius of the circle = sqrt(<i>x</i><sup>2</sup> +
- * <i>y</i><sup>2</sup>).
- * @param[in] z the height of the circle.
- * @param[in] a the normalizing radius.
- * @exception std::bad_alloc if the memory for the CircularEngine can't be
- * allocated.
- * @result the CircularEngine object.
- *
- * If you need to evaluate the spherical harmonic sum for several points
- * with constant \e f, \e p = sqrt(<i>x</i><sup>2</sup> +
- * <i>y</i><sup>2</sup>), \e z, and \e a, it is more efficient to construct
- * call SphericalEngine::Circle to give a CircularEngine object and then
- * call CircularEngine::operator()() with arguments <i>x</i>/\e p and
- * <i>y</i>/\e p.
- **********************************************************************/
- template<bool gradp, normalization norm, int L>
- static CircularEngine Circle(const coeff c[], const real f[],
- real p, real z, real a);
- /**
- * Check that the static table of square roots is big enough and enlarge it
- * if necessary.
- *
- * @param[in] N the maximum degree to be used in SphericalEngine.
- * @exception std::bad_alloc if the memory for the square root table can't
- * be allocated.
- *
- * Typically, there's no need for an end-user to call this routine, because
- * the constructors for SphericalEngine::coeff do so. However, since this
- * updates a static table, there's a possible race condition in a
- * multi-threaded environment. Because this routine does nothing if the
- * table is already large enough, one way to avoid race conditions is to
- * call this routine at program start up (when it's still single threaded),
- * supplying the largest degree that your program will use. E.g., \code
- GeographicLib::SphericalEngine::RootTable(2190);
- \endcode
- * suffices to accommodate extant magnetic and gravity models.
- **********************************************************************/
- static void RootTable(int N);
- /**
- * Clear the static table of square roots and release the memory. Call
- * this only when you are sure you no longer will be using SphericalEngine.
- * Your program will crash if you call SphericalEngine after calling this
- * routine.
- *
- * \warning It's safest not to call this routine at all. (The space used
- * by the table is modest.)
- **********************************************************************/
- static void ClearRootTable() {
- std::vector<real> temp(0);
- sqrttable().swap(temp);
- }
- };
- } // namespace GeographicLib
- #if defined(_MSC_VER)
- # pragma warning (pop)
- #endif
- #endif // GEOGRAPHICLIB_SPHERICALENGINE_HPP
|