add GeographicLib
This commit is contained in:
384
external/include/GeographicLib/SphericalEngine.hpp
vendored
Normal file
384
external/include/GeographicLib/SphericalEngine.hpp
vendored
Normal file
@@ -0,0 +1,384 @@
|
||||
/**
|
||||
* \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
|
||||
Reference in New Issue
Block a user