-
-
Notifications
You must be signed in to change notification settings - Fork 299
Propagator mean_motion hangs for some r, v vectors around Earth #475
Description
Comes from #474, by @TimothySHamilton
The problem can be better seen by disabling Numba JIT, which shows some NumPy floating point warnings, and then turning them into errors:
$ NUMBA_DISABLE_JIT=1 ipython --no-banner
In [1]: import numpy as np
In [2]: np.seterr(all="raise")
Out[2]: {'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}
In [3]: import numpy as np
...: import math
...: from astropy import units as u
...: from poliastro.bodies import Earth, Moon
...: from poliastro.twobody import Orbit
...:
...: r=[8.e3, 1.e3, 0.]*u.km
...: v=[-0.5, -0.5, 0.]*u.km/u.s
...: orbit1=Orbit.from_vectors(Earth,r,v)
...: orbit2=orbit1.propagate(1.*u.h)
...:
...:
---------------------------------------------------------------------------
FloatingPointError Traceback (most recent call last)
<ipython-input-3-08d7b74965c9> in <module>()
8 v=[-0.5, -0.5, 0.]*u.km/u.s
9 orbit1=Orbit.from_vectors(Earth,r,v)
---> 10 orbit2=orbit1.propagate(1.*u.h)
~/.miniconda36/envs/poliastro37/lib/python3.7/site-packages/poliastro/twobody/orbit.py in propagate(self, value, method, rtol, **kwargs)
403 time_of_flight = time.TimeDelta(value)
404
--> 405 return propagate(self, time_of_flight, method=method, rtol=rtol, **kwargs)
406
407 def sample(self, values=None, method=mean_motion):
~/.miniconda36/envs/poliastro37/lib/python3.7/site-packages/poliastro/twobody/propagation.py in propagate(orbit, time_of_flight, method, rtol, **kwargs)
177
178 """
--> 179 r, v = method(orbit, time_of_flight.to(u.s).value, rtol=rtol, **kwargs)
180 return orbit.from_vectors(orbit.attractor, r * u.km, v * u.km / u.s, orbit.epoch + time_of_flight, orbit.plane)
~/.miniconda36/envs/poliastro37/lib/python3.7/site-packages/poliastro/twobody/propagation.py in mean_motion(orbit, tofs, **kwargs)
118
119 if not hasattr(tofs, '__len__'):
--> 120 return mean_motion_fast(k, r0, v0, tofs)
121
122 results = [mean_motion_fast(k, r0, v0, tof) for tof in tofs]
~/.miniconda36/envs/poliastro37/lib/python3.7/site-packages/poliastro/core/propagation.py in mean_motion(k, r0, v0, tof)
33
34 # get the initial mean anomaly
---> 35 M0 = nu_to_M(nu0, ecc)
36 # strong elliptic or strong hyperbolic orbits
37 if np.abs(ecc - 1.0) > 1e-2:
~/.miniconda36/envs/poliastro37/lib/python3.7/site-packages/poliastro/core/angles.py in nu_to_M(nu, ecc, delta)
183 else:
184 D = nu_to_D(nu)
--> 185 M = D_to_M(D, ecc)
186 return M
187
~/.miniconda36/envs/poliastro37/lib/python3.7/site-packages/poliastro/core/angles.py in D_to_M(D, ecc)
155 @jit
156 def D_to_M(D, ecc):
--> 157 M = _kepler_equation_parabolic(D, 0.0, ecc)
158 return M
159
~/.miniconda36/envs/poliastro37/lib/python3.7/site-packages/poliastro/core/angles.py in _kepler_equation_parabolic(D, M, ecc)
26 @jit
27 def _kepler_equation_parabolic(D, M, ecc):
---> 28 return M_parabolic(ecc, D) - M
29
30
~/.miniconda36/envs/poliastro37/lib/python3.7/site-packages/poliastro/core/angles.py in M_parabolic(ecc, D, tolerance)
41 k = 0
42 while not small_term:
---> 43 term = (ecc - 1.0 / (2.0 * k + 3.0)) * (x ** k)
44 small_term = np.abs(term) < tolerance
45 S += term
FloatingPointError: overflow encountered in double_scalarsAnd then, mean_motion fails for true anomalies very close to pi in the near parabolic case:
In [14]: M_parabolic(ecc, nu_to_D(np.pi + 0.01)) # overflow
In [15]: M_parabolic(ecc, nu_to_D(np.pi - 0.01)) # overflowThis deserves closer inspection. @nikita-astronaut was the original author that read the corresponding paper, but I don't think he will have the time to answer. Perhaps one solution would be to use the elliptic solution when the true anomaly is close to pi even if the eccentricity is close to one:
poliastro/src/poliastro/core/angles.py
Lines 176 to 186 in 1f12764
| def nu_to_M(nu, ecc, delta=1e-2): | |
| if ecc > 1 + delta: | |
| F = nu_to_F(nu, ecc) | |
| M = F_to_M(F, ecc) | |
| elif ecc < 1 - delta: | |
| E = nu_to_E(nu, ecc) | |
| M = E_to_M(E, ecc) | |
| else: | |
| D = nu_to_D(nu) | |
| M = D_to_M(D, ecc) | |
| return M |
We should also find out if we could prevent at least the hangs by compiling some functions with error_model="python", as advised in numba/numba#1256 (comment).