How can I put a sphere of radius 1737 at the location of (384400,0,0)?
This sphere would be the moon in my trajectory.
Everything else with the code is fine, I just don't know how to add a sphere in that location with that radius.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
me = 5.974 * 10 ** (24) # mass of the earth
mm = 7.348 * 10 ** (22) # mass of the moon
G = 6.67259 * 10 ** (-20) # gravitational parameter
re = 6378.0 # radius of the earth in km
rm = 1737.0 # radius of the moon in km
r12 = 384400.0 # distance between the CoM of the earth and moon
M = me + mm
pi1 = me / M
pi2 = mm / M
mue = 398600.0 # gravitational parameter of earth km^3/sec^2
mum = G * mm # grav param of the moon
mu = mue + mum
omega = np.sqrt(mu / r12 ** 3)
nu = -129.21 * np.pi / 180 # true anomaly angle in radian
x = 327156.0 - 4671
# x location where the moon's SOI effects the spacecraft with the offset of the
# Earth not being at (0,0) in the Earth-Moon system
y = 33050.0 # y location
vbo = 10.85 # velocity at burnout
gamma = 0 * np.pi / 180 # angle in radians of the flight path
vx = vbo * (np.sin(gamma) * np.cos(nu) - np.cos(gamma) * np.sin(nu))
# velocity of the bo in the x direction
vy = vbo * (np.sin(gamma) * np.sin(nu) + np.cos(gamma) * np.cos(nu))
# velocity of the bo in the y direction
xrel = (re + 300.0) * np.cos(nu) - pi2 * r12
# spacecraft x location relative to the earth
yrel = (re + 300.0) * np.sin(nu)
# r0 = [xrel, yrel, 0]
# v0 = [vx, vy, 0]
u0 = [xrel, yrel, 0, vx, vy, 0]
def deriv(u, dt):
n1 = -((mue * (u[0] + pi2 * r12) / np.sqrt((u[0] + pi2 * r12) ** 2
+ u[1] ** 2) ** 3)
- (mum * (u[0] - pi1 * r12) / np.sqrt((u[0] - pi1 * r12) ** 2
+ u[1] ** 2) ** 3))
n2 = -((mue * u[1] / np.sqrt((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3)
- (mum * u[1] / np.sqrt((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3))
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
2 * omega * u[5] + omega ** 2 * u[0] + n1, # dotu[3] = that
omega ** 2 * u[1] - 2 * omega * u[4] + n2, # dotu[4] = that
0] # dotu[5] = 0
dt = np.arange(0.0, 320000.0, 1) # 200000 secs to run the simulation
u = odeint(deriv, u0, dt)
x, y, z, x2, y2, z2 = u.T
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z)
plt.show()
You can add the following code to draw the sphere, before the plt.show():
phi = np.linspace(0, 2 * np.pi, 100)
theta = np.linspace(0, np.pi, 100)
xm = rm * np.outer(np.cos(phi), np.sin(theta)) + r12
ym = rm * np.outer(np.sin(phi), np.sin(theta))
zm = rm * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xm, ym, zm)
However, your moon will look all stretched out because the scale is not equal for all axes. In order to change the scales of the axes, you can add something like
ax.auto_scale_xyz([-50000, 400000], [0, 160000], [-130000, 130000])
before plt.show(). The result is still not completely right, but I leave it up to you to play around to get better results -- I just picked some numbers that make it look somewhat better.
All the code for a sphere is here:
http://matplotlib.org/examples/mplot3d/surface3d_demo2.html
From there you just have to shift in whichever direction you want and change the 10 to whatever radius you desire.
Related
I am trying to rotate a triad of orthogonal axis, which origin is located on the surface of a sphere. The aim would be to rotate them in order to pass from Cartesian coordinates, to radial and transverse component of the sphere. I tried to find a python function but I haven't find anything good for my case so I built it by myself.
First I computed the three angles:
#Center of the sphere
xc = 7500
yc = 7500
zc = 8960
#File where the coordinates are located
data = pd.read_csv('file_test', header = None,delimiter =' ')
xa = np.array(data.iloc[1:,1])
ya = np.array(data.iloc[1:,2])
za = np.array(data.iloc[1:,0])
#Angles definition
theta = np.arctan2((ya-yc),(xa-xc))
phi = np.arctan2((za-zc),(xa-xc))
gamma = np.arctan2((za-zc),(ya-yc))
Then I rotate the vector x,y,z in this way:
def rotate_receivers(x, y, z, a, b, c):
x1 = x * np.cos(b) * np.cos(c) - y * (np.sin(c) * np.cos(b)) + z * (np.sin(b))
y1= x * (np.sin(a) * np.sin(b) *np.cos(c) + np.cos(a) * np.sin(c)) + y * ( - np.sin(b) * np.sin(a) * np.sin(c) + np.cos(c) * np.cos(a)) - z * (np.sin(a) * np.cos(b))
z1 = x * ( - np.cos(a) * np.sin(b) * np.cos(c) + np.sin(a) * np.sin(c)) + y * ( np.sin(b) * np.sin(c) * np.cos(a) + np.sin(a) * np.cos(c)) + z * np.cos(a) * np.cos(b)
return x1, y1, z1
Now I am expecting to rotate them in this way:
x1,y1,z1 = rotate_receivers(x[i],y[i],z[i],gamma[i],phi[i],theta[i])
Now the rotation works only for some angles and I don't understand why. It has to be something wrong maybe in the way I defined the angles but I don't understand what.
Thanks!
I wish to set-up an initially circular (e=0) system of planetary rings which I can later perturb over time and see how the eccentricity changes. However, my calculation of the eccentricity vector returns -1 as the value of my initial ring, rather than zero.
The eccentricity vector equation takes this form
import numpy as np
import matplotlib.pyplot as plt
G = 6.674e-20 # km^3 kg^-1 s^-2
day = 60.0 * 60.0 * 24.0
dt = day / 10.0
Mass = 5.683e26
N = 30000
delta = np.random.random(1) * 2.0 * np.pi / N
angles = np.linspace(0.0, 2.0 * np.pi, N) + delta
radius = np.random.uniform(low = 1e6, high = 2e6, size = (N)) # ring radius
xrings, yrings = radius * np.cos(angles), radius * np.sin(angles) # positions
vxrings, vyrings = np.sqrt((G * Mass) / radius) * -np.sin(angles), np.sqrt((G * Mass) / radius) * np.cos(angles) # velocities
dist = np.hypot(xrings, yrings) # distance between particles
# update positions
xrings += (vxrings * dt)
yrings += (vyrings * dt)
#update velocities
vxrings -= (G * Mass * xrings / (dist ** 3.0 + 1.0e6) * dt)
vyrings -= (G * Mass * yrings / (dist ** 3.0 + 1.0e6) * dt)
v = np.hypot(vxrings, vyrings)
mu = G*Mass
e = (((abs(v)**2) / mu) - (1/abs(dist)))*radius - (((radius*v) / mu)*v)
plt.plot(e, radius)
plt.show()
I have tried interchanging dist and radius in various ways within the equation as I know the radius needs to be with respect to the central mass, but to no avail.
I think the problem is arising due to the fact that it is a vector equation and when you have implemented it, you've used the magnitudes of radius and velocity when you should have used their vectors.
Implementing either equation from the wiki (with the vectors for r and v) gives the expected result of e being 0 when dt is 0:
import numpy as np
import matplotlib.pyplot as plt
G = 6.674e-20 # km^3 kg^-1 s^-2
day = 60.0 * 60.0 * 24.0
dt = day / 10.0
Mass = 5.683e26
mu = G*Mass
dt = 0
N = 30000
delta = np.random.random(1) * 2.0 * np.pi / N
angles = np.linspace(0.0, 2.0 * np.pi, N) + delta
radius = np.random.uniform(low = 1e6, high = 2e6, size = (N)) # ring radius
xrings, yrings = radius * np.cos(angles), radius * np.sin(angles) # positions
vxrings, vyrings = np.sqrt((G * Mass) / radius) * -np.sin(angles), np.sqrt((G * Mass) / radius) * np.cos(angles) # velocities
dist = np.hypot(xrings, yrings) # distance between particles
# update positions
xrings += (vxrings * dt)
yrings += (vyrings * dt)
# #update velocities
vxrings -= (G * Mass * xrings / (dist ** 3.0 + 1.0e6) * dt)
vyrings -= (G * Mass * yrings / (dist ** 3.0 + 1.0e6) * dt)
# Convert to array of vectors assuming there is no motion out of the plane
r_vector = np.array([[i, j, 0 ] for i, j in zip(xrings, yrings)])
v_vector = np.array([[i, j, 0] for i, j in zip(vxrings, vyrings)])
# Implement the equation as given in the wiki page
# Cross product method
h = [np.cross(i, j) for i, j in zip(r_vector, v_vector)] # r cross v
v_h = [np.cross(i, j)/mu for i, j in zip(v_vector, h)] # v cross h over mu
r_normalised = [i/np.linalg.norm(i) for i in r_vector]
e_vector_cross = [i-j for i,j in zip(v_h, r_normalised)]
absolute_e_cross = [np.linalg.norm(i) for i in e_vector_cross]
plt.figure()
plt.title('Cross product method')
plt.xlabel('Eccentricity')
plt.ylabel('Radius')
plt.plot(absolute_e_cross, radius)
# Dot product method
first_factor = [np.linalg.norm(i)**2/mu -1/np.linalg.norm(j) for i, j in zip(v_vector, r_vector)]
first = [i*j for i, j in zip(first_factor, r_vector)]
second_factor = [np.dot(i, j)/mu for i, j in zip(r_vector, v_vector)]
second = [i*j for i, j in zip(second_factor, v_vector)]
e_vector_dot = [i-j for i, j in zip(first, second)]
absolute_e_dot = [np.linalg.norm(i) for i in e_vector_dot]
plt.figure()
plt.title('Dot product method')
plt.xlabel('Eccentricity')
plt.ylabel('Radius')
plt.plot(absolute_e_dot, radius)
Output:
I dont understand how to setup and run code with cython.
I added cdef, double, etc to pertinent pieces of my code.
setup.py of course the name hello isn't being used.
cython doc
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [Extension("hello", ["hello.pyx"])]
setup(
name = 'Hello world app',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
)
Edit
Redefining the question to make it more of a concrete example.
I want to run this system of ODEs through Cython. I am not sure if double is the best choice but the numbers are on the order of magnitudes of 10 to the 10 and are non-integer.
So I want to call this in a bigger code where mus is a variable defined in the script calling the module. I have my setup.py file to compile the pyx file. I am not sure with what I need to do so I can call this ode now. Say I name the module 3bodyproblem. I would then call it in the scipt as import 3bodyproblem and then do `3bodyproblem.3bodyproblem(what would this input be)'
I have be reading intro to cython for odes but I am not sure how to use their example with mine. Also, if it needs to be in rk format, see the code below the first code.
Code 1
cdef deriv(double u, dt):
cdef double u[0], u[1], u[2]
return [u[3],
u[4],
u[5],
-mus * u[0] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[1] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[2] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5]
dt = np.linspace(0.0, t12sec, t12sec)
u = odeint(deriv, u0, dt, atol = 1e-13, rtol = 1e-13)
x, y, z, vx, vy, vz = u.T
Code 2
cdef deriv(dt, double u):
cdef double u[0], u[1], u[2], u[3], u[4], u[5]
return [u[3],
u[4],
u[5],
-mus * u[0] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[1] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[2] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5]
solver = ode(deriv).set_integrator('dop853')
solver.set_initial_value(u0)
z = np.zeros(300000)
for ii in range(300000):
z[ii] = solver.integrate(dt[ii])[0]
x, y, z, x2, y2, z2 = z.T
If I just try to compile code 1, cython doesn't know about the variables defined the larger .py file. I want to avoid setting variables in the cython code so it can be used else where with out re compiling. Here are the errors:
Error compiling Cython file:
------------------------------------------------------------
...
import numpy as np
from scipy.integrate import odeint
cdef deriv(double u, dt):
cdef double u[0], u[1], u[2]
^
------------------------------------------------------------
ODEcython.pyx:7:17: 'u' redeclared
Error compiling Cython file:
------------------------------------------------------------
...
import numpy as np
from scipy.integrate import odeint
cdef deriv(double u, dt):
cdef double u[0], u[1], u[2]
^
------------------------------------------------------------
ODEcython.pyx:7:23: 'u' redeclared
Error compiling Cython file:
------------------------------------------------------------
...
import numpy as np
from scipy.integrate import odeint
cdef deriv(double u, dt):
cdef double u[0], u[1], u[2]
^
------------------------------------------------------------
ODEcython.pyx:7:29: 'u' redeclared
Error compiling Cython file:
------------------------------------------------------------
...
-mus * u[0] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[1] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[2] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5]
dt = np.linspace(0.0, t12sec, t12sec)
^
------------------------------------------------------------
ODEcython.pyx:16:28: undeclared name not builtin: t12sec
Error compiling Cython file:
------------------------------------------------------------
...
-mus * u[1] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[2] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5]
dt = np.linspace(0.0, t12sec, t12sec)
u = odeint(deriv, u0, dt, atol = 1e-13, rtol = 1e-13)
^
------------------------------------------------------------
ODEcython.pyx:17:16: Cannot convert 'object (double, object)' to Python object
Error compiling Cython file:
------------------------------------------------------------
...
-mus * u[1] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[2] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5]
dt = np.linspace(0.0, t12sec, t12sec)
u = odeint(deriv, u0, dt, atol = 1e-13, rtol = 1e-13)
^
------------------------------------------------------------
ODEcython.pyx:17:20: undeclared name not builtin: u0
Error compiling Cython file:
------------------------------------------------------------
...
cdef deriv(double u, dt):
cdef double u[0], u[1], u[2]
return [u[3],
u[4],
u[5],
-mus * u[0] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
^
------------------------------------------------------------
ODEcython.pyx:11:17: undeclared name not builtin: mus
building 'ODEcython' extension
creating build
creating build/temp.linux-x86_64-2.7
x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c ODEcython.c -o build/temp.linux-x86_64-2.7/ODEcython.o
ODEcython.c:1:2: error: #error Do not use this file, it is the result of a failed Cython compilation.
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
Maybe this will help
So I want to use the module in a file like this: (different deriv function but just ignore that the idea is the same)
import numpy as np
from scipy.integrate import ode
import pylab
from mpl_toolkits.mplot3d import Axes3D
from scipy.optimize import brentq
from scipy.optimize import fsolve
me = 5.974 * 10 ** 24 # mass of the earth
mm = 7.348 * 10 ** 22 # mass of the moon
G = 6.67259 * 10 ** -20 # gravitational parameter
re = 6378.0 # radius of the earth in km
rm = 1737.0 # radius of the moon in km
r12 = 384400.0 # distance between the CoM of the earth and moon
rs = 66100.0 # distance to the moon SOI
Lambda = np.pi / 6 # angle at arrival to SOI
M = me + mm
d = 300 # distance the spacecraft is above the Earth
pi1 = me / M
pi2 = mm / M
mue = 398600.0 # gravitational parameter of earth km^3/sec^2
mum = G * mm # grav param of the moon
mu = mue + mum
omega = np.sqrt(mu / r12 ** 3)
# distance from the earth to Lambda on the SOI
r1 = np.sqrt(r12 ** 2 + rs ** 2 - 2 * r12 * rs * np.cos(Lambda))
vbo = 10.85 # velocity at burnout
h = (re + d) * vbo # angular momentum
energy = vbo ** 2 / 2 - mue / (re + d) # energy
v1 = np.sqrt(2.0 * (energy + mue / r1)) # refer to the close up of moon diagram
# refer to diagram for angles
theta1 = np.arccos(h / (r1 * v1))
phi1 = np.arcsin(rs * np.sin(Lambda) / r1)
#
p = h ** 2 / mue # semi-latus rectum
a = -mue / (2 * energy) # semi-major axis
eccen = np.sqrt(1 - p / a) # eccentricity
nu0 = 0
nu1 = np.arccos((p - r1) / (eccen * r1))
# Solving for the eccentric anomaly
def f(E0):
return np.tan(E0 / 2) - np.sqrt((1 - eccen) / (1 + eccen)) * np.tan(0)
E0 = brentq(f, 0, 5)
def g(E1):
return np.tan(E1 / 2) - np.sqrt((1 - eccen) / (1 + eccen)) * np.tan(nu1 / 2)
E1 = fsolve(g, 0)
# Time of flight from r0 to SOI
deltat = (np.sqrt(a ** 3 / mue) * (E1 - eccen * np.sin(E1)
- (E0 - eccen * np.sin(E0))))
# Solve for the initial phase angle
def s(phi0):
return phi0 + deltat * 2 * np.pi / (27.32 * 86400) + phi1 - nu1
phi0 = fsolve(s, 0)
nu = -phi0
gamma = 0 * np.pi / 180 # angle in radians of the flight path
vx = vbo * (np.sin(gamma) * np.cos(nu) - np.cos(gamma) * np.sin(nu))
# velocity of the bo in the x direction
vy = vbo * (np.sin(gamma) * np.sin(nu) + np.cos(gamma) * np.cos(nu))
# velocity of the bo in the y direction
xrel = (re + 300.0) * np.cos(nu) - pi2 * r12
yrel = (re + 300.0) * np.sin(nu)
u0 = [xrel, yrel, 0, vx, vy, 0]
def deriv(u, dt):
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
(2 * omega * u[4] + omega ** 2 * u[0] - mue * (u[0] + pi2 * r12) /
np.sqrt(((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3) - mum *
(u[0] - pi1 * r12) /
np.sqrt(((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3)),
# dotu[3] = that
(-2 * omega * u[3] + omega ** 2 * u[1] - mue * u[1] /
np.sqrt(((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3) - mum * u[1] /
np.sqrt(((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3)),
# dotu[4] = that
0] # dotu[5] = 0
dt = np.linspace(0.0, 259200.0, 259200.0) # secs to run the simulation
u = odeint(deriv, u0, dt)
x, y, z, x2, y2, z2 = u.T
fig = pylab.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z, color = 'r')
# adding the moon
phi = np.linspace(0, 2 * np.pi, 100)
theta = np.linspace(0, np.pi, 100)
xm = rm * np.outer(np.cos(phi), np.sin(theta)) + r12 - pi2 * r12
ym = rm * np.outer(np.sin(phi), np.sin(theta))
zm = rm * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xm, ym, zm, color = '#696969', linewidth = 0)
ax.auto_scale_xyz([-8000, 385000], [-8000, 385000], [-8000, 385000])
# adding the earth
xe = re * np.outer(np.cos(phi), np.sin(theta)) - pi2 * r12
ye = re * np.outer(np.sin(phi), np.sin(theta))
ze = re * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xe, ye, ze, color = '#4169E1', linewidth = 0)
ax.auto_scale_xyz([-8000, 385000], [-8000, 385000], [-8000, 385000])
pylab.show()
try something like this:
# hello.pyx
cimport numpy as np
import numpy as np
def deriv(np.ndarray u, double mus):
return [u[3],
u[4],
u[5],
-mus * u[0] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[1] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5,
-mus * u[2] / (u[0] ** 2 + u[1] ** 2 + u[2] ** 2) ** 1.5]
build as before, than use it in python like this:
import hello
[...]
u_new = hello.deriv(u, mus) # or put it into odeint
cdef functions are only callable from the c-side, also you must put all parameters in the function definition.
Edit:
So I found out that NDSolve for ODE is using Runge Kutta to solve the equations.
How can I use the Runge Kutta method on my python code to solve the ODE I have below?
From my post on text files with float entries, I was able to determine that python and mathematica immediately start diverging with a tolerance of 10 to the negative 6.
End Edit
For last few hours, I have been trying to figure out why my solutions in Mathematica and Python differ by 5000 something km.
I am led to believe one program has a higher error tolerance when simulating over millions of seconds in flight time.
My question is which program is more accurate, and if it isn't python, how can I adjust the precision?
With Mathematica, I am less than 10km away from L4 where as with Python I am 5947 km away.
The codes are listed below:
Python
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from numpy import linspace
from scipy.optimize import brentq
me = 5.974 * 10 ** (24) # mass of the earth
mm = 7.348 * 10 ** (22) # mass of the moon
G = 6.67259 * 10 ** (-20) # gravitational parameter
re = 6378.0 # radius of the earth in km
rm = 1737.0 # radius of the moon in km
r12 = 384400.0 # distance between the CoM of the earth and moon
d = 300 # distance the spacecraft is above the Earth
pi1 = me / (me + mm)
pi2 = mm / (me + mm)
mue = 398600.0 # gravitational parameter of earth km^3/sec^2
mum = G * mm # grav param of the moon
mu = mue + mum
omega = np.sqrt(mu / (r12 ** 3))
nu = -np.pi / 4 # true anomaly pick yourself
xl4 = r12 / 2 - 4671 # x location of L4
yl4 = np.sqrt(3) / 2 * r12 # y
print("The location of L4 is", xl4, yl4)
# Solve for Jacobi's constant
def f(C):
return (omega ** 2 * (xl4 ** 2 + yl4 ** 2) + 2 * mue / r12 + 2 * mum / r12
+ 2 * C)
c = brentq(f, -5, 0)
print("Jacobi's constant is",c)
x0 = (re + 200) * np.cos(nu) - pi2 * r12 # x location of the satellite
y0 = (re + 200) * np.sin(nu) # y location
print("The satellite's initial position is", x0, y0)
vbo = (np.sqrt(omega ** 2 * (x0 ** 2 + y0 ** 2) + 2 * mue /
np.sqrt((x0 + pi2 * r12) ** 2 + y0 ** 2) + 2 * mum /
np.sqrt((x0 - pi1 * r12) ** 2 + y0 ** 2) + 2 * -1.21))
print("Burnout velocity is", vbo)
gamma = 0.4678 * np.pi / 180 # flight path angle pick yourself
vx = vbo * (np.sin(gamma) * np.cos(nu) - np.cos(gamma) * np.sin(nu))
# velocity of the bo in the x direction
vy = vbo * (np.sin(gamma) * np.sin(nu) + np.cos(gamma) * np.cos(nu))
# velocity of the bo in the y direction
print("The satellite's initial velocity is", vx, vy)
# r0 = [x, y, 0]
# v0 = [vx, vy, 0]
u0 = [x0, y0, 0, vx, vy, 0]
def deriv(u, dt):
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
(2 * omega * u[4] + omega ** 2 * u[0] - mue * (u[0] + pi2 * r12) /
np.sqrt(((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3) - mum *
(u[0] - pi1 * r12) /
np.sqrt(((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3)),
# dotu[3] = that
(-2 * omega * u[3] + omega ** 2 * u[1] - mue * u[1] /
np.sqrt(((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3) - mum * u[1] /
np.sqrt(((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3)),
# dotu[4] = that
0] # dotu[5] = 0
dt = np.linspace(0.0, 6.0 * 86400.0, 2000000.0) # secs to run the simulation
u = odeint(deriv, u0, dt)
x, y, z, x2, y2, z2 = u.T
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z, color = 'r')
# adding the Lagrange point
phi = np.linspace(0, 2 * np.pi, 100)
theta = np.linspace(0, np.pi, 100)
xm = 2000 * np.outer(np.cos(phi), np.sin(theta)) + xl4
ym = 2000 * np.outer(np.sin(phi), np.sin(theta)) + yl4
zm = 2000 * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xm, ym, zm, color = '#696969', linewidth = 0)
ax.auto_scale_xyz([-8000, 385000], [-8000, 385000], [-8000, 385000])
# adding the earth
phi = np.linspace(0, 2 * np.pi, 100)
theta = np.linspace(0, np.pi, 100)
xm = 2000 * np.outer(np.cos(phi), np.sin(theta))
ym = 2000 * np.outer(np.sin(phi), np.sin(theta))
zm = 2000 * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xm, ym, zm, color = '#696969', linewidth = 0)
ax.auto_scale_xyz([-8000, 385000], [-8000, 385000], [-8000, 385000])
plt.show()
# The code below finds the distance between path and l4
my_x, my_y, my_z = (xl4, yl4, 0.0)
delta_x = x - my_x
delta_y = y - my_y
delta_z = z - my_z
distance = np.array([np.sqrt(delta_x ** 2 + delta_y ** 2 + delta_z ** 2)])
minimum = np.amin(distance)
print("Closet approach to L4 is", minimum)
Mathematica
ClearAll["Global`*"];
me = 5.974*10^(24);
mm = 7.348*10^(22);
G = 6.67259*10^(-20);
re = 6378;
rm = 1737;
r12 = 384400;
\[Pi]1 = me/(me + mm);
\[Pi]2 = mm/(me + mm);
M = me + mm;
\[Mu]1 = 398600;
\[Mu]2 = G*mm;
\[Mu] = \[Mu]1 + \[Mu]2;
\[CapitalOmega] = Sqrt[\[Mu]/r12^3];
\[Nu] = -\[Pi]/4;
xl4 = 384400/2 - 4671;
yl4 = Sqrt[3]/2*384400 // N;
Solve[\[CapitalOmega]^2*(xl4^2 + yl4^2) + 2 \[Mu]1/r12 +
2 \[Mu]2/r12 + 2*C == 0, C]
x = (re + 200)*Cos[\[Nu]] - \[Pi]2*r12 // N
y = (re + 200)*Sin[\[Nu]] // N
{{C -> -1.56824}}
-19.3098
-4651.35
vbo = Sqrt[\[CapitalOmega]^2*((x)^2 + (y)^2) +
2*\[Mu]1/Sqrt[(x + \[Pi]2*r12)^2 + (y)^2] +
2*\[Mu]2/Sqrt[(x - \[Pi]1*r12)^2 + (y)^2] + 2*(-1.21)]
10.8994
\[Gamma] = 0.4678*Pi/180;
vx = vbo*(Sin[\[Gamma]]*Cos[\[Nu]] - Cos[\[Gamma]]*Sin[\[Nu]]);
vy = vbo*(Sin[\[Gamma]]*Sin[\[Nu]] + Cos[\[Gamma]]*Cos[\[Nu]]);
r0 = {x, y, 0};
v0 = {vx, vy, 0}
{7.76974, 7.64389, 0}
s = NDSolve[{x1''[t] -
2*\[CapitalOmega]*x2'[t] - \[CapitalOmega]^2*
x1[t] == -\[Mu]1/((Sqrt[(x1[t] + \[Pi]2*r12)^2 +
x2[t]^2])^3)*(x1[t] + \[Pi]2*
r12) - \[Mu]2/((Sqrt[(x1[t] - \[Pi]1*r12)^2 +
x2[t]^2])^3)*(x1[t] - \[Pi]1*r12),
x2''[t] +
2*\[CapitalOmega]*x1'[t] - \[CapitalOmega]^2*
x2[t] == -\[Mu]1/(Sqrt[(x1[t] + \[Pi]2*r12)^2 + x2[t]^2])^3*
x2[t] - \[Mu]2/(Sqrt[(x1[t] - \[Pi]1*r12)^2 + x2[t]^2])^3*
x2[t], x3''[t] == 0, x1[0] == r0[[1]], x1'[0] == v0[[1]],
x2[0] == r0[[2]], x2'[0] == v0[[2]], x3[0] == r0[[3]],
x3'[0] == v0[[3]]}, {x1, x2, x3}, {t, 0, 1000000}];
ParametricPlot3D[
Evaluate[{x1[t], x2[t], x3[t]} /. s], {t, 0, 10*24*3600},
PlotStyle -> {Red, Thick}]
g1 = ParametricPlot3D[
Evaluate[{x1[t], x2[t], x3[t]} /. s], {t, 0, 5.75*3600*24},
PlotStyle -> {Red},
PlotRange -> {{-10000, 400000}, {-10000, 400000}}];
g2 = Graphics3D[{Blue, Opacity[0.6], Sphere[{-4671, 0, 0}, re]}];
g3 = Graphics3D[{Green, Opacity[0.6], Sphere[{379729, 0, 0}, rm]}];
g4 = Graphics3D[{Black, Sphere[{xl4, yl4, 0}, 2000]}];
Show[g2, g1, g3, g4, Boxed -> False]
(*XYdata=Flatten[Table[Evaluate[{x1[t],x2[t],x3[t]}/.s],{t,5.5*24*\
3600,5.78*24*3600,1}],1];
X1Y1data=Flatten[Table[Evaluate[{x1'[t],x2'[t],x3'[t]}/.s],{t,5.5*24*\
3600,5.78*24*3600,1}],1];
SetDirectory[NotebookDirectory[]];
Export["OrbitData.txt",XYdata,"CSV"];
Export["OrbVeloc.txt",X1Y1data,"CSV"];*)
If at this point your problem has reduced to just wanting to use Runge-Kutta, you can for example replace this line:
u = odeint(deriv, u0, dt)
with something like this:
#reverse the order of arguments
def deriv2(t,u):
return deriv(u,t)
# initialize a 4th order Runge-Kutta ODE solver
solver = ode(deriv2).set_integrator('dopri5')
solver.set_initial_value(u0)
u = np.empty((len(dt), 6))
u[0,:] = u0
for ii in range(1,len(dt)):
u[ii] = solver.integrate(dt[ii])
(+obviously replace the odeint import with ode).
Note that this is significantly slower for this type of ODE.
To use the dop853, use solver.set_integrator('dop853').
I re-wrote the def deriv part of the ode and it works now! So the Mathematica plot and the Python agree.
def deriv(u, dt):
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
(2 * omega * u[4] + omega ** 2 * u[0] - mue * (u[0] + pi2 * r12) /
np.sqrt(((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3) - mum *
(u[0] - pi1 * r12) /
np.sqrt(((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3)),
# dotu[3] = that
(-2 * omega * u[3] + omega ** 2 * u[1] - mue * u[1] /
np.sqrt(((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3) - mum * u[1] /
np.sqrt(((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3)),
# dotu[4] = that
0] # dotu[5] = 0
One way to check the accuracy could be this:
take a time where the 2 trajectories are sufficiently different from one another (the last time/point in the trajectory for example). Use this as the new starting point and integrate back in time to the initial time (or, if you prefer, integrate forward in time, but reverse the velocities of all bodies present in the simulation). The final point in this simulation should be (close to) your initial point in the original simulation. By comparing how close you actually get to your original initial point, you can judge how good python vs mathematica solver routines are. My guess is that the Mathematica routines are much better, so all this Python thing is a waste of time.
Another way could be this:
With Mathematica what you get is an interpolating function which you can symbolically derive and then numerically evaluate its derivatives. Plug these values in the ODE at different points and see if they satisfy the ODE. Do something similar in python and compare the results.
Edit
The only part that I would still like a solution for is getting the spheres to look spherical. If I scale the axis the same, the spheres are too small but spherical. Is there a way to then clip the unused portion off so it is zoomed in? Or can we need set the axis so big and get the desired effect?
End Edit
From plotting a sphere in python, I was able to two add a spheres to my plot.
However, I can't get them to look spherical no matter how I adjust the parameters. Additionally, adding 'g' or any other color to the end of there plots doesn't change there color.
I also tried color='g' but that didn't work either.
How can I get a spherical look (ax autoscale) and change the color?
#!/usr/bin/env python
# This porgram numerically solves the trajectory from the Earth to the moon
# with the specified flight path, true anomaly, and initial conditions.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from numpy import linspace
me = 5.974 * 10 ** (24) # mass of the earth
mm = 7.348 * 10 ** (22) # mass of the moon
G = 6.67259 * 10 ** (-20) # gravitational parameter
re = 6378.0 # radius of the earth in km
rm = 1737.0 # radius of the moon in km
r12 = 384400.0 # distance between the CoM of the earth and moon
M = me + mm
pi1 = me / M
pi2 = mm / M
mue = 398600.0 # gravitational parameter of earth km^3/sec^2
mum = G * mm # grav param of the moon
mu = mue + mum
omega = np.sqrt(mu / r12 ** 3)
nu = -134.979 * np.pi / 180 # true anomaly angle in radian
x = 327156.0 - 4671
# x location where the moon's SOI effects the spacecraft with the offset of the
# Earth not being at (0,0) in the Earth-Moon system
y = 33050.0 # y location
vbo = 10.85 # velocity at burnout
gamma = 0 * np.pi / 180 # angle in radians of the flight path
vx = vbo * (np.sin(gamma) * np.cos(nu) - np.cos(gamma) * np.sin(nu))
# velocity of the bo in the x direction
vy = vbo * (np.sin(gamma) * np.sin(nu) + np.cos(gamma) * np.cos(nu))
# velocity of the bo in the y direction
xrel = (re + 300.0) * np.cos(nu) - pi2 * r12
yrel = (re + 300.0) * np.sin(nu)
# r0 = [xrel, yrel, 0]
# v0 = [vx, vy, 0]
u0 = [xrel, yrel, 0, vx, vy, 0]
def deriv(u, dt):
n1 = -((mue * (u[0] + pi2 * r12) / np.sqrt((u[0] + pi2 * r12) ** 2
+ u[1] ** 2) ** 3)
- (mum * (u[0] - pi1 * r12) / np.sqrt((u[0] - pi1 * r12) ** 2
+ u[1] ** 2) ** 3))
n2 = -((mue * u[1] / np.sqrt((u[0] + pi2 * r12) ** 2 + u[1] ** 2) ** 3)
- (mum * u[1] / np.sqrt((u[0] - pi1 * r12) ** 2 + u[1] ** 2) ** 3))
return [u[3], # dotu[0] = u[3]
u[4], # dotu[1] = u[4]
u[5], # dotu[2] = u[5]
2 * omega * u[4] + omega ** 2 * u[0] + n1, # dotu[3] = that
omega ** 2 * u[1] - 2 * omega * u[3] + n2, # dotu[4] = that
0] # dotu[5] = 0
dt = np.linspace(0.0, 320000.0, 1000000.0) # 200000 secs to run the simulation
u = odeint(deriv, u0, dt)
x, y, z, x2, y2, z2 = u.T
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x, y, z)
# adding the moon
phi = np.linspace(0, 2 * np.pi, 100)
theta = np.linspace(0, np.pi, 100)
xm = rm * np.outer(np.cos(phi), np.sin(theta)) + r12
ym = rm * np.outer(np.sin(phi), np.sin(theta))
zm = rm * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xm, ym, zm)
ax.auto_scale_xyz([-50000, 400000], [0, 160000], [-130000, 130000])
# adding the earth
xe = re * np.outer(np.cos(phi), np.sin(theta)) - 4671
ye = re * np.outer(np.sin(phi), np.sin(theta))
ze = re * np.outer(np.ones(np.size(phi)), np.cos(theta))
ax.plot_surface(xe, ye, ze, 'g')
ax.auto_scale_xyz([-50000, 400000], [0, 160000], [-130000, 130000])
#
plt.show()
# The code below finds the distance between path and the moon
my_x, my_y, my_z = (384400,0,0)
delta_x = x - my_x
delta_y = y - my_y
delta_z = z - my_z
distance = np.array([np.sqrt(delta_x ** 2 + delta_y ** 2 + delta_z ** 2)])
minimum = np.amin(distance)
print(minimum)