GeographicLib: TriaxialGeodesicODE.hpp Source File

GeographicLib 2.6

Loading...
Searching...
No Matches
TriaxialGeodesicODE.hpp
Go to the documentation of this file.
1/**
2 * \file TriaxialGeodesicODE.hpp
3 * \brief Header for GeographicLib::Triaxial class
4 *
5 * Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
6 * under the MIT/X11 License. For more information, see
7 * https://geographiclib.sourceforge.io/
8 **********************************************************************/
9
10#if !defined(GEOGRAPHICLIB_TRIAXIALGEODESICODE_HPP)
11#define GEOGRAPHICLIB_TRIAXIALGEODESICODE_HPP 1
12
13#include <vector>
14#include <array>
15#include <utility>
16#include <GeographicLib/Constants.hpp>
17#include <GeographicLib/Angle.hpp>
18#include <GeographicLib/Triaxial/Ellipsoid3.hpp>
19
20// Boost's dense output expects numeric_limits<real>::digits to be a constant
21// and not a function. So we can't use GEOGRAPHICLIB_PRECISION == 5. High
22// precision results can be obtained with GEOGRAPHICLIB_PRECISION > 5, e.g.,
23// GEOGRAPHICLIB_PRECISION == 256.
24#if GEOGRAPHICLIB_PRECISION == 5
25# define GEOGRAPHICLIB_BOOST_ODE_DENSE_OUT 0
26#else
27# define GEOGRAPHICLIB_BOOST_ODE_DENSE_OUT 1
28#endif
29
30// Boost bugs when using high precision:
31// https://github.com/boostorg/odeint/issues/40
32// https://github.com/boostorg/odeint/issues/75 (duplicate)
33// fixed in
34// https://github.com/boostorg/odeint/pull/63
35// Commit 68950d8
36//
37// This will be included in Boost 1.85. (Fedora 42 uses Boost 1.83.)
38//
39// In the meantime, put the patch for commit 68950d8 in
40// /usr/include/boost/odeint.patch and applied it with
41// patch -p3 -b < odeint.patch
42// -> patching file numeric/odeint/algebra/detail/extract_value_type.hpp
43//
44// Removed my temporary fix to
45// numeric/odeint/stepper/controlled_runge_kutta.hpp
46
47#if __clang__
48// Ignore clang warnings for boost headers
49#pragma clang diagnostic ignored "-Wunused-parameter"
50#pragma clang diagnostic ignored "-Wreorder-ctor"
51#endif
52
53#include <boost/numeric/odeint.hpp>
54#if GEOGRAPHICLIB_BOOST_ODE_DENSE_OUT
55#include <boost/numeric/odeint/stepper/bulirsch_stoer_dense_out.hpp>
56#endif
57#include <boost/numeric/odeint/stepper/bulirsch_stoer.hpp>
58
59namespace GeographicLib {
60 /**
61 * \brief Namespace for experimental components of %GeographicLib
62 *
63 * These routines are distributed as source code with %GeographicLib but are
64 * not incorporated into the library itself.
65 **********************************************************************/
66 namespace experimental {
67
68 /**
69 * \brief The ODE solution of direct geodesic problem for triaxial ellipsoids.
70 *
71 * This determines the course of a geodesic by solving the equations of
72 * motion for a particle sliding without friction on the surface of the
73 * ellipsoid. The solution is carried out in Cartesian coordinates. The
74 * same approach was used by
75 * <a href="https://doi.org/10.1515/jogs-2019-0001"> Panou and Korakitis
76 * (2019)</a>. Significant differences are:
77 * * The code is provided.
78 * * This method uses a high order method provided by Boost. This allows
79 * reasonably high accuracy to be achieved using double precision.
80 * * It solver optionally offers "dense" output. It takes as large steps as
81 * possible while meeting the accuracy requirements. The results at
82 * specific distances are then found by interpolation. There is little
83 * penalty in requesting many waypoints.
84 * * Because the ODE only "works" in one direction, the solver has to restart
85 * if the direction is reversed (but this is hidden from the user). To
86 * simplify usage, you can provide a vector of distances to the solver and
87 * this is sorted appropriately before calling the underlying Boost
88 * integrator. This vector can include negative as well as positive
89 * distances.
90 * * This class can also, optionally, solve for the reduced length \e m12,
91 * and the geodesic scales \e M12 and \e M21.
92 *
93 * These routines are distributed as source code with %GeographicLib but,
94 * because of the dependency on the Boost library, are not incorporated into
95 * the library itself.
96 *
97 * The recommended way to solve the direct and indirect geodesic problems on
98 * a triaxial ellipsoid is with the class Triaxial::Geodesic3.
99 *
100 * Geod3ODE.cpp is a utility which uses this class to solve direct geodesic
101 * problems. Use `Geod3ODE --help` for brief documentation.
102 **********************************************************************/
103 class TriaxialGeodesicODE {
104 public:
105 /**
106 * A type to hold three-dimentional positions and velocities in Cartesian
107 * coordinates.
108 **********************************************************************/
109 using vec3 = Triaxial::Ellipsoid3::vec3;
110 private:
111 using real = Math::real;
112 using vec6 = std::array<real, 6>;
113 using vec10 = std::array<real, 10>;
114 using ang = Angle;
115#if GEOGRAPHICLIB_BOOST_ODE_DENSE_OUT
116 using dstep6 =
117 boost::numeric::odeint::bulirsch_stoer_dense_out<vec6, real>;
118 using dstep10 =
119 boost::numeric::odeint::bulirsch_stoer_dense_out<vec10, real>;
120#endif
121 using step6 = boost::numeric::odeint::bulirsch_stoer<vec6, real>;
122 using step10 = boost::numeric::odeint::bulirsch_stoer<vec10, real>;
123 const Triaxial::Ellipsoid3 _t;
124 const real _b, _eps;
125 const vec3 _axesn, _axes2n;
126 vec3 _r1, _v1;
127 Angle _bet1, _omg1, _alp1;
128 bool _extended, _dense, _normp;
129 int _dir;
130 mutable long _nsteps, _intsteps;
131#if GEOGRAPHICLIB_BOOST_ODE_DENSE_OUT
132 dstep6 _dstep6;
133 dstep10 _dstep10;
134#endif
135 step6 _step6;
136 step10 _step10;
137 real _s;
138 vec6 _y6;
139 vec10 _y10;
140 // These private versions of Accel and Norm assume normalized ellipsoid
141 // with axes = axesn
142 void Norm6(vec6& y) const;
143 void Accel6(const vec6& y, vec6& yp) const;
144 void Accel6N(const vec6& y, vec6& yp) const;
145 void Norm10(vec10& y) const;
146 void Accel10(const vec10& y, vec10& yp) const;
147 void Accel10N(const vec10& y, vec10& yp) const;
148 static std::vector<size_t> sort_indices(const std::vector<real>& v);
149 void Reset();
150
151 public:
152 /**
153 * Basic constructor specifying ellipsoid and starting point.
154 *
155 * @param[in] t the Ellipsoid3 object.
156 * @param[in] R1 the starting position on the ellipsoid.
157 * @param[in] V1 the starting velocity tangent to the ellipsoid.
158 * @param[in] extended (default false), if true solve for reduced length
159 * and geodesic scale.
160 * @param[in] dense (default false), if true use a dense solver allowing
161 * interpolated way points to tbe computed inexpensively.
162 * @param[in] normp (default false), if true force the solution vector onto
163 * the ellipsoid when computing the acceleration.
164 * @param[in] eps (default 0), if positive the error threshold for the
165 * integrator; otherwise use a good default value related to the machine
166 * precision.
167 *
168 * The values \e R1 and \e V1 are normalized to place \e R1 on the
169 * ellipsoid and \e V1 tangent to the ellipsoid with unit speed.
170 *
171 * Internally, the integration scales the ellipsoid so that the median
172 * semiaxis \b = 1. The \e eps parameter is a measure of the absolution
173 * error on this scaled ellipsoid.
174 **********************************************************************/
175 TriaxialGeodesicODE(const Triaxial::Ellipsoid3& t, vec3 R1, vec3 V1,
176 bool extended = false, bool dense = false,
177 bool normp = false, real eps = 0);
178 /**
179 * Constructor specifying just the ellipsoid.
180 *
181 * @param[in] t the Ellipsoid3 object.
182 * @param[in] extended (default false), if true solve for reduced length
183 * and geodesic scale.
184 * @param[in] dense (default false), if true use a dense solver allowing
185 * interpolated way points to tbe computed inexpensively.
186 * @param[in] normp (default false), if true force the solution vector onto
187 * the ellipsoid when computing the acceleration.
188 * @param[in] eps (default 0), if positive the error threshold for the
189 * integrator; otherwise use a good default value related to the machine
190 * precision.
191 *
192 * This form starts the geodesic at \e R1 = [\e a, 0, 0], \e V1 = [0, 0,
193 * 1].
194 **********************************************************************/
195 TriaxialGeodesicODE(const Triaxial::Ellipsoid3& t,
196 bool extended = false, bool dense = false,
197 bool normp = false, real eps = 0);
198 /**
199 * Basic constructor specifying ellipsoid and starting point in ellipsoidal
200 * coordinates.
201 *
202 * @param[in] t the Ellipsoid3 object.
203 * @param[in] bet1 the starting latitude.
204 * @param[in] omg1 the starting longitude.
205 * @param[in] alp1 the starting azimuth.
206 * @param[in] extended (default false), if true solve for reduced length
207 * and geodesic scale.
208 * @param[in] dense (default false), if true use a dense solver allowing
209 * interpolated way points to tbe computed inexpensively.
210 * @param[in] normp (default false), if true force the solution vector onto
211 * the ellipsoid when computing the acceleration.
212 * @param[in] eps (default 0), if positive the error threshold for the
213 * integrator; otherwise use a good default value related to the machine
214 * precision.
215 **********************************************************************/
216 TriaxialGeodesicODE(const Triaxial::Ellipsoid3& t,
217 Angle bet1, Angle omg1, Angle alp1,
218 bool extended = false, bool dense = false,
219 bool normp = false, real eps = 0);
220 /**
221 * Find the position a given distance from the starting point.
222 *
223 * @param[in] s12 the distance between point 1 and point 2.
224 * @param[out] R2 the position of point 2.
225 * @param[out] V2 the velocity at point 2.
226 * @return a pair of error estimates, the distance from the ellipsoid (in
227 * meters) and the deviation of the velocity from a unit tangential
228 * vector.
229 *
230 * The returned values \e R2 and \e V2 are normalized to place \e R2 on the
231 * ellipsoid and \e V2 tangent to the ellipsoid with unit speed.
232 **********************************************************************/
233 std::pair<real, real> Position(real s12, vec3& R2, vec3& V2);
234 /**
235 * Find the position and differential quantities a given distance from the
236 * starting point.
237 *
238 * @param[in] s12 the distance between point 1 and point 2.
239 * @param[out] R2 the position of point 2.
240 * @param[out] V2 the velocity at point 2.
241 * @param[out] m12 the reduced length
242 * @param[out] M12 the geodesic scale at point 2 relative to point 1.
243 * @param[out] M21 the geodesic scale at point 1 relative to point 2.
244 * @return a pair of error estimates, the distance from the ellipsoid (in
245 * meters) and the deviation of the velocity from a unit tangential
246 * vector.
247 *
248 * The returned values \e R2 and \e V2 are normalized to place \e R2 on the
249 * ellipsoid and \e V2 tangent to the ellipsoid with unit speed.
250 *
251 * If the object was constructed with \e extended = false, nans are
252 * returned for the differential quantities.
253 **********************************************************************/
254 std::pair<real, real> Position(real s12, vec3& R2, vec3& V2,
255 real& m12, real& M12, real& M21);
256 /**
257 * Find the ellipsoidal coordinates a given distance from the starting
258 * point.
259 *
260 * @param[in] s12 the distance between point 1 and point 2.
261 * @param[out] bet2 the latitude at point 2.
262 * @param[out] omg2 the longitude at point 2.
263 * @param[out] alp2 the azimuth at point 2.
264 * @return a pair of error estimates, the distance from the ellipsoid (in
265 * meters) and the deviation of the velocity from a unit tangential
266 * vector.
267 **********************************************************************/
268 std::pair<real, real> Position(real s12,
269 Angle& bet2, Angle& omg2, Angle& alp2);
270 /**
271 * Find the ellipsoidal coordinates and differential quantities a given
272 * distance from the starting point.
273 *
274 * @param[in] s12 the distance between point 1 and point 2.
275 * @param[out] bet2 the latitude at point 2.
276 * @param[out] omg2 the longitude at point 2.
277 * @param[out] alp2 the azimuth at point 2.
278 * @param[out] m12 the reduced length
279 * @param[out] M12 the geodesic scale at point 2 relative to point 1.
280 * @param[out] M21 the geodesic scale at point 1 relative to point 2.
281 * @return a pair of error estimates, the distance from the ellipsoid (in
282 * meters) and the deviation of the velocity from a unit tangential
283 * vector.
284 *
285 * If the object was constructed with \e extended = false, nans are
286 * returned for the differential quantities.
287 **********************************************************************/
288 std::pair<real, real> Position(real s12,
289 Angle& bet2, Angle& omg2, Angle& alp2,
290 real& m12, real& M12, real& M21);
291
292 /**
293 * Find the positions for a series of distances from the starting point.
294 *
295 * @param[in] s12 a vector of distances between point 1 and points 2.
296 * @param[out] R2 a vector of positions of points 2.
297 * @param[out] V2 a vector of velocities at points 2.
298 *
299 * Before starting the integration, \e s12 is sorted to separate the
300 * positive and negative values and place each in order of increasing
301 * magnitude. The results are placed back in the correct positions in the
302 * output vectors.
303 *
304 * The returned values \e R2 and \e V2 are normalized to place \e R2 on the
305 * ellipsoid and \e V2 tangent to the ellipsoid with unit speed.
306 **********************************************************************/
307 void Position(const std::vector<real>& s12,
308 std::vector<vec3>& R2, std::vector<vec3>& V2);
309 /**
310 * Find the positions and differential quantities for a series of distances
311 * from the starting point.
312 *
313 * @param[in] s12 a vector of distances between point 1 and points 2.
314 * @param[out] R2 a vector of positions of points 2.
315 * @param[out] V2 a vector of velocities at points 2.
316 * @param[out] m12 a vector of the reduced lengths.
317 * @param[out] M12 a vector of the geodesic scales at points 2 relative to
318 * point 1.
319 * @param[out] M21 a vector of the geodesic scales at point 1 relative to
320 * points 2.
321 *
322 * Before starting the integration, \e s12 is sorted to separate the
323 * positive and negative values and place each in order of increasing
324 * magnitude. The results are placed back in the correct positions in the
325 * output vectors.
326 *
327 * The returned values \e R2 and \e V2 are normalized to place \e R2 on the
328 * ellipsoid and \e V2 tangent to the ellipsoid with unit speed.
329 *
330 * If the object was constructed with \e extended = false, nans are
331 * returned for the differential quantities.
332 **********************************************************************/
333 void Position(const std::vector<real>& s12,
334 std::vector<vec3>& R2, std::vector<vec3>& V2,
335 std::vector<real>& m12,
336 std::vector<real>& M12, std::vector<real>& M21);
337 /**
338 * Find the ellipsoidal coordinates for a series of distances from the
339 * starting point.
340 *
341 * @param[in] s12 a vector of distances between point 1 and points 2.
342 * @param[out] bet2 a vector of latitudes at points 2.
343 * @param[out] omg2 a vector of longitudes at points 2.
344 * @param[out] alp2 a vector of azimuths at points 2.
345 *
346 * Before starting the integration, \e s12 is sorted to separate the
347 * positive and negative values and place each in order of increasing
348 * magnitude. The results are placed back in the correct positions in the
349 * output vectors.
350 **********************************************************************/
351 void Position(const std::vector<real>& s12,
352 std::vector<Angle>& bet2, std::vector<Angle>& omg2,
353 std::vector<Angle>& alp2);
354 /**
355 * Find the ellipsoidal coordinates and differential quantities for a
356 * series of distances from the starting point.
357 *
358 * @param[in] s12 a vector of distances between point 1 and points 2.
359 * @param[out] bet2 a vector of latitudes at points 2.
360 * @param[out] omg2 a vector of longitudes at points 2.
361 * @param[out] alp2 a vector of azimuths at points 2.
362 * @param[out] m12 a vector of the reduced lengths.
363 * @param[out] M12 a vector of the geodesic scales at points 2 relative to
364 * point 1.
365 * @param[out] M21 a vector of the geodesic scales at point 1 relative to
366 * points 2.
367 *
368 * Before starting the integration, \e s12 is sorted to separate the
369 * positive and negative values and place each in order of increasing
370 * magnitude. The results are placed back in the correct positions in the
371 * output vectors.
372 **********************************************************************/
373 void Position(const std::vector<real>& s12,
374 std::vector<Angle>& bet2, std::vector<Angle>& omg2,
375 std::vector<Angle>& alp2,
376 std::vector<real>& m12,
377 std::vector<real>& M12, std::vector<real>& M21);
378 /**
379 * Reset the starting point.
380 *
381 * @param[in] R1 the starting position on the ellipsoid.
382 * @param[in] V1 the starting velocity tangent to the ellipsoid.
383 *
384 * The values \e R1 and \e V1 are normalized to place \e R1 on the
385 * ellipsoid and \e V1 tangent to the ellipsoid with unit speed.
386 **********************************************************************/
387 void Reset(vec3 R1, vec3 V1);
388 /**
389 * Reset the starting point in ellipsoidal coordinates.
390 *
391 * @param[in] bet1 the starting latitude.
392 * @param[in] omg1 the starting longitude.
393 * @param[in] alp1 the starting azimuth.
394 **********************************************************************/
395 void Reset(Angle bet1, Angle omg1, Angle alp1);
396 /**
397 * @return the number of integration steps since the last Reset()
398 **********************************************************************/
399 long NSteps() const { return _nsteps; }
400 /**
401 * @return the number of calls to the acceleration routine since the last
402 * Reset()
403 **********************************************************************/
404 long IntSteps() const { return _intsteps; }
405 /**
406 * @return a pair for the current distance bracket.
407 *
408 * If the object was constructed with \e dense = true this gives the
409 * current interval over which an interpolated waypoint can be found.
410 * Otherwise the two distances are equal to the last distance calculation.
411 **********************************************************************/
412 std::pair<real, real> CurrentDistance() const;
413 /**
414 * @return whether the object was constructed with \e extended = true.
415 **********************************************************************/
416 bool Extended() const { return _extended; }
417 /**
418 * The current starting point.
419 *
420 * @param[out] R1 the starting position.
421 * @param[out] V1 the starting velocity.
422 **********************************************************************/
423 void Position1(vec3& R1, vec3& V1) const { R1 = _r1; V1 = _v1; }
424 /**
425 * The current starting point in ellipsoidal coordinates.
426 *
427 * @param[out] bet1 the starting latitude.
428 * @param[out] omg1 the starting longitude.
429 * @param[out] alp1 the starting azimuth.
430 **********************************************************************/
431 void Position1(Angle& bet1, Angle& omg1, Angle& alp1) const {
432 bet1 = _bet1; omg1 = _omg1; alp1 = _alp1;
433 }
434 /**
435 * @return the Ellipsoid3 object used in the constructor.
436 **********************************************************************/
437 const Triaxial::Ellipsoid3& t() const { return _t; }
438 };
439
440} // namespace experimental
441} // namespace GeographicLib
442
443#endif // GEOGRAPHICLIB_TRIAXIALGEODESICODE_HPP
Angle.hpp
Header for the GeographicLib::AngleT class.
Constants.hpp
Header for GeographicLib::Constants class.
Ellipsoid3.hpp
Header for GeographicLib::Triaxial::Ellipsoid3 class.
GeographicLib::Math::real
double real
Definition Math.hpp:115
GeographicLib::Triaxial::Ellipsoid3
A triaxial ellipsoid.
Definition Ellipsoid3.hpp:82
GeographicLib::Triaxial::Ellipsoid3::vec3
std::array< Math::real, 3 > vec3
Definition Ellipsoid3.hpp:88
GeographicLib::experimental::TriaxialGeodesicODE::vec3
Triaxial::Ellipsoid3::vec3 vec3
Definition TriaxialGeodesicODE.hpp:109
GeographicLib::experimental::TriaxialGeodesicODE::Position
void Position(const std::vector< real > &s12, std::vector< Angle > &bet2, std::vector< Angle > &omg2, std::vector< Angle > &alp2)
GeographicLib::experimental::TriaxialGeodesicODE::Position
std::pair< real, real > Position(real s12, vec3 &R2, vec3 &V2, real &m12, real &M12, real &M21)
GeographicLib::experimental::TriaxialGeodesicODE::Position1
void Position1(Angle &bet1, Angle &omg1, Angle &alp1) const
Definition TriaxialGeodesicODE.hpp:431
GeographicLib::experimental::TriaxialGeodesicODE::Reset
void Reset(vec3 R1, vec3 V1)
GeographicLib::experimental::TriaxialGeodesicODE::TriaxialGeodesicODE
TriaxialGeodesicODE(const Triaxial::Ellipsoid3 &t, Angle bet1, Angle omg1, Angle alp1, bool extended=false, bool dense=false, bool normp=false, real eps=0)
GeographicLib::experimental::TriaxialGeodesicODE::TriaxialGeodesicODE
TriaxialGeodesicODE(const Triaxial::Ellipsoid3 &t, bool extended=false, bool dense=false, bool normp=false, real eps=0)
GeographicLib::experimental::TriaxialGeodesicODE::Position
void Position(const std::vector< real > &s12, std::vector< vec3 > &R2, std::vector< vec3 > &V2)
GeographicLib::experimental::TriaxialGeodesicODE::Position
void Position(const std::vector< real > &s12, std::vector< vec3 > &R2, std::vector< vec3 > &V2, std::vector< real > &m12, std::vector< real > &M12, std::vector< real > &M21)
GeographicLib::experimental::TriaxialGeodesicODE::Position
std::pair< real, real > Position(real s12, Angle &bet2, Angle &omg2, Angle &alp2)
GeographicLib::experimental::TriaxialGeodesicODE::Reset
void Reset(Angle bet1, Angle omg1, Angle alp1)
GeographicLib::experimental::TriaxialGeodesicODE::Extended
bool Extended() const
Definition TriaxialGeodesicODE.hpp:416
GeographicLib::experimental::TriaxialGeodesicODE::TriaxialGeodesicODE
TriaxialGeodesicODE(const Triaxial::Ellipsoid3 &t, vec3 R1, vec3 V1, bool extended=false, bool dense=false, bool normp=false, real eps=0)
GeographicLib::experimental::TriaxialGeodesicODE::Position1
void Position1(vec3 &R1, vec3 &V1) const
Definition TriaxialGeodesicODE.hpp:423
GeographicLib::experimental::TriaxialGeodesicODE::CurrentDistance
std::pair< real, real > CurrentDistance() const
GeographicLib::experimental::TriaxialGeodesicODE::Position
std::pair< real, real > Position(real s12, vec3 &R2, vec3 &V2)
GeographicLib::experimental::TriaxialGeodesicODE::Position
std::pair< real, real > Position(real s12, Angle &bet2, Angle &omg2, Angle &alp2, real &m12, real &M12, real &M21)
GeographicLib::experimental::TriaxialGeodesicODE::NSteps
long NSteps() const
Definition TriaxialGeodesicODE.hpp:399
GeographicLib::experimental::TriaxialGeodesicODE::IntSteps
long IntSteps() const
Definition TriaxialGeodesicODE.hpp:404
GeographicLib::experimental::TriaxialGeodesicODE::Position
void Position(const std::vector< real > &s12, std::vector< Angle > &bet2, std::vector< Angle > &omg2, std::vector< Angle > &alp2, std::vector< real > &m12, std::vector< real > &M12, std::vector< real > &M21)
GeographicLib::experimental::TriaxialGeodesicODE::t
const Triaxial::Ellipsoid3 & t() const
Definition TriaxialGeodesicODE.hpp:437
GeographicLib::experimental
Namespace for experimental components of GeographicLib.
Definition TriaxialGeodesicODE.hpp:66
GeographicLib
Namespace for GeographicLib.
Definition Accumulator.cpp:12
GeographicLib::Angle
AngleT< Math::real > Angle
Definition Angle.hpp:760