Hello! Does anyone know to achieve something similar to what the graph shows, using matpltolib? I need to draw some lines, such as CC' and DD', that do not cross over a given line AB. Is there any way to achieve this?
To draw a line from a point C, perpendicular to a line AB and stopping at AB, a projection point P can be calculated. P should be on AB, so P = t*A + (1-t)*B. This equation on points means it should hold both for the x and for the y coordinates: xp = t * xa + (1 - t) * xb and yp = t * ya + (1 - t) * yb) for some t. P should also be on a line containing C, and having the perpendicular vector as its direction. The perpendicular vector is yb - ya, - (xb - xa). So, P = C + u * Perp for some u.
Sympy, Python's symbolic math library can be used to solve these equations:
from sympy import symbols, Eq, solve
t, u, xa, ya, xb, yb, xc, yc = symbols('t u xa ya xb yb xc yc ')
eqpx = Eq(xc + u * (yb - ya), t * xa + (1 - t) * xb)
eqpy = Eq(yc + u * (xa - xb), t * ya + (1 - t) * yb)
solution = solve([eqpx, eqpy])
This finds:
{t: -(xa*xb - xa*xc - xb**2 + xb*xc + ya*yb - ya*yc - yb**2 + yb*yc)/(xa**2 - 2*xa*xb + xb**2 + ya**2 - 2*ya*yb + yb**2),
u: (xa*yb - xa*yc - xb*ya + xb*yc + xc*ya - xc*yb)/(xa**2 - 2*xa*xb + xb**2 + ya**2 - 2*ya*yb + yb**2)}
The solution can be used to define a function to project a point C onto a line AB. Here is an example:
from matplotlib import pyplot as plt
def project_on_line(xc, yc, xa, ya, xb, yb):
t = -(xa * xb - xa * xc - xb ** 2 + xb * xc + ya * yb - ya * yc - yb ** 2 + yb * yc) / (
xa ** 2 - 2 * xa * xb + xb ** 2 + ya ** 2 - 2 * ya * yb + yb ** 2)
return t * xa + (1 - t) * xb, t * ya + (1 - t) * yb
def show_point(x, y, name):
plt.text(x, y, f' {name}', ha='left', va='top')
xa, ya = 0, 2
xb, yb = 6, 5
xc, yc = 5, 1
xd, yd = 7, 0
xcp, ycp = project_on_line(xc, yc, xa, ya, xb, yb)
xdp, ydp = project_on_line(xd, yd, xa, ya, xb, yb)
show_point(xa, ya, "A")
show_point(xb, yb, "B")
show_point(xc, yc, "C")
show_point(xcp, ycp, "C'")
show_point(xd, yd, "D")
show_point(xdp, ydp, "D'")
plt.plot([xa, xb], [ya, yb], color='dodgerblue')
plt.plot([xc, xcp], [yc, ycp], color='dodgerblue')
plt.plot([xd, xdp], [yd, ydp], color='dodgerblue')
plt.axis('equal')
plt.show()
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'd like to use an implementation of RK4 I found online for something, but I'm having a bit of difficulty wrapping my head around the implementations I have found online.
For example:
def rk4(f, x0, y0, x1, n):
vx = [0] * (n + 1)
vy = [0] * (n + 1)
h = (x1 - x0) / float(n)
vx[0] = x = x0
vy[0] = y = y0
for i in range(1, n + 1):
k1 = h * f(x, y)
k2 = h * f(x + 0.5 * h, y + 0.5 * k1)
k3 = h * f(x + 0.5 * h, y + 0.5 * k2)
k4 = h * f(x + h, y + k3)
vx[i] = x = x0 + i * h
vy[i] = y = y + (k1 + k2 + k2 + k3 + k3 + k4) / 6
return vx, vy
Could someone please help me understand what exactly the parameters are? If possible, I'd like a more general explanation, but, if being more specific makes it easier to explain, I'm going to be using it specifically for an ideal spring system.
You are asking for the parameters here:
def rk4(f, x0, y0, x1, n):
...
return vx, vy
f is the ODE function, declared as def f(x,y) for the differential equation y'(x)=f(x,y(x)),
(x0,y0) is the initial point and value,
x1 is the end of the integration interval [x0,x1]
n is the number of sub-intervals or integration steps
vx,vx are the computed sample points, vy[k] approximates y(vx[k]).
You can not use this for the spring system, as that code only works for a scalar v. You would need to change it to work with numpy for vector operations.
def rk4(func, x0, y0, x1, n):
y0 = np.array(y0)
f = lambda x,y: np.array(func(x,y))
vx = [0] * (n + 1)
vy = np.zeros( (n + 1,)+y0.shape)
h = (x1 - x0) / float(n)
vx[0] = x = x0
vy[0] = y = y0[:]
for i in range(1, n + 1):
k1 = h * f(x, y)
k2 = h * f(x + 0.5 * h, y + 0.5 * k1)
k3 = h * f(x + 0.5 * h, y + 0.5 * k2)
k4 = h * f(x + h, y + k3)
vx[i] = x = x0 + i * h
vy[i] = y = y + (k1 + 2*(k2 + k3) + k4) / 6
return vx, vy
I have a square with center x0, y0. I wish to rotate the vertex of this square for a given angle (theta) expressed in degree and return the new rotated vertex in clockwise direction. I am using this approach to rotate a single point applied for each vertex
rotate point (px, py) around point (x0, y0) by angle theta you'll get:
p'x = cos(theta) * (px-x0) - sin(theta) * (py-y0) + x0
p'y = sin(theta) * (px-x0) + cos(theta) * (py-y0) + y0
where:
px, py = coordinate of the point
y0, x0, = centre of rotation
theta = angle of rotation
I wrote a function in Python where the parameters are: x, y (=centre of the square), side of the square, and theta_degree (angle of rotation in degree) but the returns is in anticlockwise direction
from math import cos, sin
def get_square_plot(x, y, side, theta_degree=0):
theta = theta_degree * pi/180
xa = x-side/2
ya = y+side/2
xb = x+side/2
yb = y+side/2
xc = x+side/2
yc = y-side/2
xd = x-side/2
yd = y-side/2
xa_new = cos(theta) * (xa - x) - sin(theta) * (ya - y) + x
ya_new = sin(theta) * (xa - x) - cos(theta) * (ya - y) + y
xb_new = cos(theta) * (xb - x) - sin(theta) * (yb - y) + x
yb_new = sin(theta) * (xb - x) - cos(theta) * (yb - y) + y
xc_new = cos(theta) * (xc - x) - sin(theta) * (yc - y) + x
yc_new = sin(theta) * (xc - x) - cos(theta) * (yc - y) + y
xd_new = cos(theta) * (xd - x) - sin(theta) * (yd - y) + x
yd_new = sin(theta) * (xd - x) - cos(theta) * (yd - y) + y
return [(xa_new, ya_new),(xb_new, yb_new),(xc_new, yc_new),(xd_new, yd_new)]
get_square_plot(0, 0, 10, 0)
[(-5.0, -5.0), (5.0, -5.0), (5.0, 5.0), (-5.0, 5.0)]
instead of
[(-5.0, 5.0), (5.0, 5.0), (5.0, -5.0), (-5.0, -5.0)]
It's such a simple thing -- you've got the formula wrong for all of your y-values.
It should be:
ya_new = sin(theta) * (xa - x) + cos(theta) * (ya - y) + y
addition instead of subtraction.
Don't forget about the geometry module, either. It can deal with a variety of basic shapes and handle translation, rotation, etc...
A square can be constructed with RegularPolygon. It does so by locating vertices a given radius from the center; to get a square with a given side length, divide by sqrt(2). Here is a function to rotate the diamond-orientation so sides are parallel to the axes and then rotate the desired angle, a:
>>> Square = lambda c, r, a: RegularPolygon(c, r/sqrt(2), 4, -rad(a) - pi/4)
>>> Square((0,0),10,0).vertices
[Point(5, -5), Point(5, 5), Point(-5, 5), Point(-5, -5)]
>>> [w.n(2) for w in Square((0,0),10,1).vertices]
[Point(4.9, -5.1), Point(5.1, 4.9), Point(-4.9, 5.1), Point(-5.1, -4.9)]
Note that the slight CW rotation of 1 degree (-rad(1)) puts the first vertex a little closer to the y-axis and a little lower as we expect. You can also enter a symbol for the angle:
>>> from sympy.utilities.misc import filldedent
>>> print filldedent(Square((0,0),10,a).vertices)
[Point(5*sqrt(2)*cos(pi*a/180 + pi/4), -5*sqrt(2)*sin(pi*a/180 +
pi/4)), Point(5*sqrt(2)*sin(pi*a/180 + pi/4), 5*sqrt(2)*cos(pi*a/180 +
pi/4)), Point(-5*sqrt(2)*cos(pi*a/180 + pi/4), 5*sqrt(2)*sin(pi*a/180
+ pi/4)), Point(-5*sqrt(2)*sin(pi*a/180 + pi/4),
-5*sqrt(2)*cos(pi*a/180 + pi/4))]
You can also check your point rotation formula by rotating a point -theta (for CW):
>>> var('px py theta x0 y0')
(px, py, theta, x0, y0)
>>> R = Point(px,py).rotate(-theta, Point(x0,y0))
>>> R.x
x0 + (px - x0)*cos(theta) + (py - y0)*sin(theta)
>>> R.y
y0 + (-px + x0)*sin(theta) + (py - y0)*cos(theta)
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)