GeographicLib 2.7

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
59 namespace 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-dimensional 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 long _nsteps;
131 mutable long _intsteps; // This is advanced by AccelXX which is const
132#if GEOGRAPHICLIB_BOOST_ODE_DENSE_OUT
133 dstep6 _dstep6;
134 dstep10 _dstep10;
135#endif
136 step6 _step6;
137 step10 _step10;
138 real _s;
139 vec6 _y6;
140 vec10 _y10;
141 // These private versions of Accel and Norm assume normalized ellipsoid
142 // with axes = axesn
143 void Norm6(vec6& y) const;
144 void Accel6(const vec6& y, vec6& yp) const;
145 void Accel6N(const vec6& y, vec6& yp) const;
146 void Norm10(vec10& y) const;
147 void Accel10(const vec10& y, vec10& yp) const;
148 void Accel10N(const vec10& y, vec10& yp) const;
149 static std::vector<size_t> sort_indices(const std::vector<real>& v);
150 void Reset();
151
152 public:
153 /**
154 * Basic constructor specifying ellipsoid and starting point.
155 *
156 * @param[in] t the Ellipsoid3 object.
157 * @param[in] R1 the starting position on the ellipsoid.
158 * @param[in] V1 the starting velocity tangent to the ellipsoid.
159 * @param[in] extended (default false), if true solve for reduced length
160 * and geodesic scale.
161 * @param[in] dense (default false), if true use a dense solver allowing
162 * interpolated way points to be computed inexpensively.
163 * @param[in] normp (default false), if true force the solution vector onto
164 * the ellipsoid when computing the acceleration.
165 * @param[in] eps (default 0), if positive the error threshold for the
166 * integrator; otherwise use a good default value related to the machine
167 * precision.
168 *
169 * The values \e R1 and \e V1 are normalized to place \e R1 on the
170 * ellipsoid and \e V1 tangent to the ellipsoid with unit speed.
171 *
172 * Internally, the integration scales the ellipsoid so that the median
173 * semiaxis \b = 1. The \e eps parameter is a measure of the absolution
174 * error on this scaled ellipsoid.
175 **********************************************************************/
176 TriaxialGeodesicODE(const Triaxial::Ellipsoid3& t, vec3 R1, vec3 V1,
177 bool extended = false, bool dense = false,
178 bool normp = false, real eps = 0);
179 /**
180 * Constructor specifying just the ellipsoid.
181 *
182 * @param[in] t the Ellipsoid3 object.
183 * @param[in] extended (default false), if true solve for reduced length
184 * and geodesic scale.
185 * @param[in] dense (default false), if true use a dense solver allowing
186 * interpolated way points to be computed inexpensively.
187 * @param[in] normp (default false), if true force the solution vector onto
188 * the ellipsoid when computing the acceleration.
189 * @param[in] eps (default 0), if positive the error threshold for the
190 * integrator; otherwise use a good default value related to the machine
191 * precision.
192 *
193 * This form starts the geodesic at \e R1 = [\e a, 0, 0], \e V1 = [0, 0,
194 * 1].
195 **********************************************************************/
196 TriaxialGeodesicODE(const Triaxial::Ellipsoid3& t,
197 bool extended = false, bool dense = false,
198 bool normp = false, real eps = 0);
199 /**
200 * Basic constructor specifying ellipsoid and starting point in ellipsoidal
201 * coordinates.
202 *
203 * @param[in] t the Ellipsoid3 object.
204 * @param[in] bet1 the starting latitude.
205 * @param[in] omg1 the starting longitude.
206 * @param[in] alp1 the starting azimuth.
207 * @param[in] extended (default false), if true solve for reduced length
208 * and geodesic scale.
209 * @param[in] dense (default false), if true use a dense solver allowing
210 * interpolated way points to be computed inexpensively.
211 * @param[in] normp (default false), if true force the solution vector onto
212 * the ellipsoid when computing the acceleration.
213 * @param[in] eps (default 0), if positive the error threshold for the
214 * integrator; otherwise use a good default value related to the machine
215 * precision.
216 **********************************************************************/
217 TriaxialGeodesicODE(const Triaxial::Ellipsoid3& t,
218 Angle bet1, Angle omg1, Angle alp1,
219 bool extended = false, bool dense = false,
220 bool normp = false, real eps = 0);
221 /**
222 * Find the position a given distance from the starting point.
223 *
224 * @param[in] s12 the distance between point 1 and point 2.
225 * @param[out] R2 the position of point 2.
226 * @param[out] V2 the velocity at point 2.
227 * @return a pair of error estimates, the distance from the ellipsoid (in
228 * meters) and the deviation of the velocity from a unit tangential
229 * vector.
230 *
231 * The returned values \e R2 and \e V2 are normalized to place \e R2 on the
232 * ellipsoid and \e V2 tangent to the ellipsoid with unit speed.
233 **********************************************************************/
234 std::pair<real, real> Position(real s12, vec3& R2, vec3& V2);
235 /**
236 * Find the position and differential quantities a given distance from the
237 * starting point.
238 *
239 * @param[in] s12 the distance between point 1 and point 2.
240 * @param[out] R2 the position of point 2.
241 * @param[out] V2 the velocity at point 2.
242 * @param[out] m12 the reduced length
243 * @param[out] M12 the geodesic scale at point 2 relative to point 1.
244 * @param[out] M21 the geodesic scale at point 1 relative to point 2.
245 * @return a pair of error estimates, the distance from the ellipsoid (in
246 * meters) and the deviation of the velocity from a unit tangential
247 * vector.
248 *
249 * The returned values \e R2 and \e V2 are normalized to place \e R2 on the
250 * ellipsoid and \e V2 tangent to the ellipsoid with unit speed.
251 *
252 * If the object was constructed with \e extended = false, NaNs are
253 * returned for the differential quantities.
254 **********************************************************************/
255 std::pair<real, real> Position(real s12, vec3& R2, vec3& V2,
256 real& m12, real& M12, real& M21);
257 /**
258 * Find the ellipsoidal coordinates a given distance from the starting
259 * point.
260 *
261 * @param[in] s12 the distance between point 1 and point 2.
262 * @param[out] bet2 the latitude at point 2.
263 * @param[out] omg2 the longitude at point 2.
264 * @param[out] alp2 the azimuth at point 2.
265 * @return a pair of error estimates, the distance from the ellipsoid (in
266 * meters) and the deviation of the velocity from a unit tangential
267 * vector.
268 **********************************************************************/
269 std::pair<real, real> Position(real s12,
270 Angle& bet2, Angle& omg2, Angle& alp2);
271 /**
272 * Find the ellipsoidal coordinates and differential quantities a given
273 * distance from the starting point.
274 *
275 * @param[in] s12 the distance between point 1 and point 2.
276 * @param[out] bet2 the latitude at point 2.
277 * @param[out] omg2 the longitude at point 2.
278 * @param[out] alp2 the azimuth at point 2.
279 * @param[out] m12 the reduced length
280 * @param[out] M12 the geodesic scale at point 2 relative to point 1.
281 * @param[out] M21 the geodesic scale at point 1 relative to point 2.
282 * @return a pair of error estimates, the distance from the ellipsoid (in
283 * meters) and the deviation of the velocity from a unit tangential
284 * vector.
285 *
286 * If the object was constructed with \e extended = false, NaNs are
287 * returned for the differential quantities.
288 **********************************************************************/
289 std::pair<real, real> Position(real s12,
290 Angle& bet2, Angle& omg2, Angle& alp2,
291 real& m12, real& M12, real& M21);
292
293 /**
294 * Find the positions for a series of distances from the starting point.
295 *
296 * @param[in] s12 a vector of distances between point 1 and points 2.
297 * @param[out] R2 a vector of positions of points 2.
298 * @param[out] V2 a vector of velocities at points 2.
299 *
300 * Before starting the integration, the positive and negative vaules in \e
301 * s12 are separated and then sorted in order of increasing magnitude. The
302 * results are placed back in the correct positions in the output vectors.
303 * \e s12 can include NaNs; this can be used to "punctuate" the results.
304 *
305 * The returned values \e R2 and \e V2 are normalized to place \e R2 on the
306 * ellipsoid and \e V2 tangent to the ellipsoid with unit speed.
307 **********************************************************************/
308 void Position(const std::vector<real>& s12,
309 std::vector<vec3>& R2, std::vector<vec3>& V2);
310 /**
311 * Find the positions and differential quantities for a series of distances
312 * from the starting point.
313 *
314 * @param[in] s12 a vector of distances between point 1 and points 2.
315 * @param[out] R2 a vector of positions of points 2.
316 * @param[out] V2 a vector of velocities at points 2.
317 * @param[out] m12 a vector of the reduced lengths.
318 * @param[out] M12 a vector of the geodesic scales at points 2 relative to
319 * point 1.
320 * @param[out] M21 a vector of the geodesic scales at point 1 relative to
321 * points 2.
322 *
323 * Before starting the integration, the positive and negative vaules in \e
324 * s12 are separated and then sorted in order of increasing magnitude. The
325 * results are placed back in the correct positions in the output vectors.
326 * \e s12 can include NaNs; this can be used to "punctuate" the results.
327 *
328 * The returned values \e R2 and \e V2 are normalized to place \e R2 on the
329 * ellipsoid and \e V2 tangent to the ellipsoid with unit speed.
330 *
331 * If the object was constructed with \e extended = false, NaNs are
332 * returned for the differential quantities.
333 **********************************************************************/
334 void Position(const std::vector<real>& s12,
335 std::vector<vec3>& R2, std::vector<vec3>& V2,
336 std::vector<real>& m12,
337 std::vector<real>& M12, std::vector<real>& M21);
338 /**
339 * Find the ellipsoidal coordinates for a series of distances from the
340 * starting point.
341 *
342 * @param[in] s12 a vector of distances between point 1 and points 2.
343 * @param[out] bet2 a vector of latitudes at points 2.
344 * @param[out] omg2 a vector of longitudes at points 2.
345 * @param[out] alp2 a vector of azimuths at points 2.
346 *
347 * Before starting the integration, the positive and negative vaules in \e
348 * s12 are separated and then sorted in order of increasing magnitude. The
349 * results are placed back in the correct positions in the output vectors.
350 * \e s12 can include NaNs; this can be used to "punctuate" the results.
351 **********************************************************************/
352 void Position(const std::vector<real>& s12,
353 std::vector<Angle>& bet2, std::vector<Angle>& omg2,
354 std::vector<Angle>& alp2);
355 /**
356 * Find the ellipsoidal coordinates and differential quantities for a
357 * series of distances from the starting point.
358 *
359 * @param[in] s12 a vector of distances between point 1 and points 2.
360 * @param[out] bet2 a vector of latitudes at points 2.
361 * @param[out] omg2 a vector of longitudes at points 2.
362 * @param[out] alp2 a vector of azimuths at points 2.
363 * @param[out] m12 a vector of the reduced lengths.
364 * @param[out] M12 a vector of the geodesic scales at points 2 relative to
365 * point 1.
366 * @param[out] M21 a vector of the geodesic scales at point 1 relative to
367 * points 2.
368 *
369 * Before starting the integration, the positive and negative vaules in \e
370 * s12 are separated and then sorted in order of increasing magnitude. The
371 * results are placed back in the correct positions in the output vectors.
372 * \e s12 can include NaNs; this can be used to "punctuate" the results.
373 **********************************************************************/
374 void Position(const std::vector<real>& s12,
375 std::vector<Angle>& bet2, std::vector<Angle>& omg2,
376 std::vector<Angle>& alp2,
377 std::vector<real>& m12,
378 std::vector<real>& M12, std::vector<real>& M21);
379 /**
380 * Reset the starting point.
381 *
382 * @param[in] R1 the starting position on the ellipsoid.
383 * @param[in] V1 the starting velocity tangent to the ellipsoid.
384 *
385 * The values \e R1 and \e V1 are normalized to place \e R1 on the
386 * ellipsoid and \e V1 tangent to the ellipsoid with unit speed.
387 **********************************************************************/
388 void Reset(vec3 R1, vec3 V1);
389 /**
390 * Reset the starting point in ellipsoidal coordinates.
391 *
392 * @param[in] bet1 the starting latitude.
393 * @param[in] omg1 the starting longitude.
394 * @param[in] alp1 the starting azimuth.
395 **********************************************************************/
396 void Reset(Angle bet1, Angle omg1, Angle alp1);
397 /**
398 * @return the number of integration steps since the last Reset()
399 **********************************************************************/
400 long NSteps() const { return _nsteps; }
401 /**
402 * @return the number of calls to the acceleration routine since the last
403 * Reset()
404 **********************************************************************/
405 long IntSteps() const { return _intsteps; }
406 /**
407 * @return a pair for the current distance bracket.
408 *
409 * If the object was constructed with \e dense = true this gives the
410 * current interval over which an interpolated waypoint can be found.
411 * Otherwise the two distances are equal to the last distance calculation.
412 **********************************************************************/
413 std::pair<real, real> CurrentDistance() const;
414 /**
415 * @return whether the object was constructed with \e extended = true.
416 **********************************************************************/
417 bool Extended() const { return _extended; }
418 /**
419 * The current starting point.
420 *
421 * @param[out] R1 the starting position.
422 * @param[out] V1 the starting velocity.
423 **********************************************************************/
424 void Position1(vec3& R1, vec3& V1) const { R1 = _r1; V1 = _v1; }
425 /**
426 * The current starting point in ellipsoidal coordinates.
427 *
428 * @param[out] bet1 the starting latitude.
429 * @param[out] omg1 the starting longitude.
430 * @param[out] alp1 the starting azimuth.
431 **********************************************************************/
432 void Position1(Angle& bet1, Angle& omg1, Angle& alp1) const {
433 bet1 = _bet1; omg1 = _omg1; alp1 = _alp1;
434 }
435 /**
436 * @return the Ellipsoid3 object used in the constructor.
437 **********************************************************************/
438 const Triaxial::Ellipsoid3& t() const { return _t; }
439 };
440
441} // namespace experimental
442} // namespace GeographicLib
443
444#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:89
GeographicLib::Triaxial::Ellipsoid3::vec3
std::array< Math::real, 3 > vec3
Definition Ellipsoid3.hpp:95
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:432
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:417
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:424
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:400
GeographicLib::experimental::TriaxialGeodesicODE::IntSteps
long IntSteps() const
Definition TriaxialGeodesicODE.hpp:405
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:438
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