add GeographicLib

This commit is contained in:
Sven Czarnian
2021-11-22 16:16:36 +01:00
parent b1b6b5c790
commit 09e29afe7b
46 changed files with 16958 additions and 0 deletions

View 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 &ge; \e nmx &ge; \e mmx &ge; &minus;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 &ge; &minus;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 &ge;
* \e M &ge; &minus;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&minus;1] * c[L&minus;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