-
Notifications
You must be signed in to change notification settings - Fork 104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add python and C implementation of dop853 #363
Conversation
which seems to work in python 3, but not in python 2.7. |
Codecov Report
@@ Coverage Diff @@
## master #363 +/- ##
=========================================
+ Coverage 99.8% 99.8% +<.01%
=========================================
Files 144 147 +3
Lines 23079 23578 +499
=========================================
+ Hits 23034 23533 +499
Misses 45 45
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went through all but the leung_dop853.py
code, here are some minor changes that are required.
Started testing this, looking at how the actual orbit (like
The |
Great, that test seems to pass now! You should also add There are a few lines in
|
I found a bug that the time step is being reset to |
I try to trace every variable and found python and C version differ even in very early in initial step estimation. And I check with the initial condition array given by galpy, the initial condition array in c-implementation is |
Yes, that's expected. In C the equation of motion is integrated in rectangular coordinates, so the initial conditions are |
But this is a good point and I think the difference in the equations of motion might explain the difference. Because the potential is axisymmetric and angular momentum is conserved, integrating the system in cylindrical coordinates is more stable. So there is likely nothing wrong with the DOP853 integrator! |
I thought the C version also integrate in different coordinate between symplectic and non-symplectic like the python one you told me? Because I see the calls to C integrators are different between RK/DOP54 and symplectic integrators. I do find lowering the error tolerance of |
The cause is clearly coordinates now. I use my standalone python implementation of import pylab as plt
import numpy as np
from leung_dop853 import dop853
from scipy.integrate import odeint
G = 4*np.pi**2 # define G for convenience.
# Set initial position and speed of satellite.
# Compute and display some values for a circular orbit to help decide on initial conditions.
Ro = 1.0 # Radius of the orbit
M = 1.0 # mass of the central mass
v = np.sqrt(G*M/Ro) # v assuming circular orbit
# Set initial conditions and define needed array.
x0 = 1.
vy0 = 5.
t0 = 0.
tf = 10.
tau = 1e-4
X0 = [x0, 0, 0, vy0] # set initial state of the system
t = np.arange(t0,tf,tau) # create time array starting at t0, ending at tf with a spacing tau
# Define function to return f(X,t)
def f_func(state, time):
f = np.zeros(4)
f[0] = state[2]
f[1] = state[3]
r = np.sqrt(state[0]**2 + state[1]**2)
f[2] = -(G*M*state[0])/r**3
f[3] = -(G*M*state[1])/r**3
return f
X_dop853 = dop853(func=f_func, x=X0, t=t, rtol=1e-12)
x_dop853 = X_dop853[:,0] # Giving a ':' as the index specifies all of the elements for that index so
y_dop853 = X_dop853[:,1] # x, y, vx, and vy are arrays that specify their
vx_dop853 = X_dop853[:,2] # values at any given time index
vy_dop853 = X_dop853[:,3]
X0 = [x0, 0, 0, vy0] # set initial state of the system, same as retengular but in [r, phi, vr, vphi]
# Define function to return f(X,t) in polar
def f_func_polar(state, time):
f = np.zeros(4)
f[0] = state[2]
f[1] = state[3]
f[2] = -(G*M)/state[0]**2 + state[0]*state[3]**2
f[3] = (-2 * state[2]*state[3])/state[0]
return f
X_dop853_polar = dop853(func=f_func_polar, x=X0, t=t, rtol=1e-12)
# Plot the results
plt.figure(figsize=(10, 10))
plt.clf()
# calculate KE + PE
eps_dop853 = 0.5*(vx_dop853**2 + vy_dop853**2) - G*M/np.sqrt(x_dop853**2 + y_dop853**2)
eps_dop853_polar = 0.5*(X_dop853_polar[:,2]**2 + X_dop853_polar[:,0]**2*X_dop853_polar[:,3]**2) - G*M/X_dop853_polar[:,0]
# Plot the energy
plt.subplot(2,1,2)
plt.title("Energy Plot", fontsize=15)
plt.plot(t, eps_dop853, label='dop853 in rectangular')
plt.plot(t, eps_dop853_polar, label='dop853 in polar')
# plt.axis([min(t),max(t),eps_plot_min,0])
# plt.ylim((-31.4784, -31.47845))
plt.xlabel('Time', fontsize=15)
plt.ylabel('Normalized Energy', fontsize=15)
plt.legend(loc="best", fontsize=15)
plt.tight_layout()
plt.savefig("standalone_integration.png") |
Okay, great, thanks for checking this in detail! I think all is good then and will merge this. Yes, the integration in C is different for symplectic and non-symplectic integrators, because they use very different algorithms, so they can't be the same. But I think they are always in rectangular coordinates, even though the calls are different. |
This PR adds python and C implementation of DOP853 integrator to galpy and test. DOP853 is an explicit Runge-Kutta method of order 8(5,3) by Dormand & Prince with dense output.
Here is a simple testing I have done so far to see the performance and accuracy of
leapfrog
anddop853
in galpyFor
leapfrog
:For
dop853
:For
dop853_c
:For
dopr54_c
:For
symplec4_c
: