GeographicLib  2.6
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>
17 #include <GeographicLib/Angle.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  **********************************************************************/
104  public:
105  /**
106  * A type to hold three-dimentional positions and velocities in Cartesian
107  * coordinates.
108  **********************************************************************/
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  **********************************************************************/
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  **********************************************************************/
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  **********************************************************************/
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
std::array< Math::real, 3 > vec3
Definition: Ellipsoid3.hpp:88
TriaxialGeodesicODE(const Triaxial::Ellipsoid3 &t, vec3 R1, vec3 V1, bool extended=false, bool dense=false, bool normp=false, real eps=0)
void Position1(Angle &bet1, Angle &omg1, Angle &alp1) const
std::pair< real, real > CurrentDistance() const
AngleT< Math::real > Angle
Definition: Angle.hpp:760
std::pair< real, real > Position(real s12, vec3 &R2, vec3 &V2)
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
Header for GeographicLib::Triaxial::Ellipsoid3 class.
The ODE solution of direct geodesic problem for triaxial ellipsoids.
Header for GeographicLib::Constants class.
Header for the GeographicLib::AngleT class.