Python simulation shows slow performance, how to speed array calculation up - python

I'm trying to write a simple Python program which calculates the interference pattern from 2 incoming laser beams. Everything is working as it should but it's quite slow. I'm using a 400x400 array and it takes about 1.3 seconds to recalculate the intensity after I change a parameter. However, running the code with C++ takes about 0.18 seconds. So I'm wondering if I could improve something to speed things up?
My code so far:
def calculate_intensity_array():
laser1 = zeros((400, 400), dtype=complex)
laser2 = zeros((400, 400), dtype=complex)
data = zeros((400, 400), dtype=complex)
onoff_1 = laser1_onoff_var.get()
A_1 = laser1_intensity_var.get()
sigma_1 = laser1_sigma_var.get()
sin_phi_1 = sin((laser1_phi_var.get() / 180) * pi)
cos_phi_1 = cos((laser1_phi_var.get() / 180) * pi)
sin_theta_1 = sin((laser1_theta_var.get() / 180) * pi)
cos_theta_1 = cos((laser1_theta_var.get() / 180) * pi)
mu_x_1 = laser1_xpos_var.get()
mu_y_1 = laser1_ypos_var.get()
onoff_2 = laser2_onoff_var.get()
A_2 = laser2_intensity_var.get()
sigma_2 = laser2_sigma_var.get()
sin_phi_2 = sin((laser2_phi_var.get() / 180) * pi)
sin_theta_2 = sin((laser2_theta_var.get() / 180) * pi)
cos_phi_2 = cos((laser2_phi_var.get() / 180) * pi)
cos_theta_2 = cos((laser2_theta_var.get() / 180) * pi)
mu_x_2 = laser2_xpos_var.get()
mu_y_2 = laser2_ypos_var.get()
if onoff_1 == 0:
laser1 = zeros((400, 400), dtype=complex)
elif onoff_1 == 1:
for i in range(400):
for k in range(400):
laser1[i][k] = calculate_amplitude(
(k - 200) * 10,
(i - 200) * 10,
A_1,
sigma_1,
sin_phi_1,
cos_phi_1,
sin_theta_1,
cos_theta_1,
mu_x_1,
mu_y_1)
if onoff_2 == 0:
laser2 = zeros((400, 400), dtype=complex)
elif onoff_2 == 1:
for i in range(400):
for k in range(400):
laser2[i][k] = calculate_amplitude(
(k - 200) * 10,
(i - 200) * 10,
A_2,
sigma_2,
sin_phi_2,
cos_phi_2,
sin_theta_2,
cos_theta_2,
mu_x_2,
mu_y_2)
data = abs(laser1 + laser2) ** 2
return data
def calculate_amplitude(x, y, A, sigma, sin_phi, cos_phi,
sin_theta, cos_theta, mu_x, mu_y):
amplitude = A * (1 / (sqrt(2 * pi * (sigma ** 2 / cos_theta ** 2)))) *
exp(-((cos_phi *(x - mu_x) + sin_phi *(y - mu_y)) ** 2 * cos_theta ** 2) /
(2 * sigma ** 2)) *
(1 / (sqrt(2 * pi * sigma ** 2))) *
exp(-((-sin_phi * (x - mu_x) + cos_phi * (y - mu_y)) ** 2) /
(2 * sigma ** 2)) *
cmath.exp(1j *(2 * pi / 0.650) * sin_theta *
(cos_phi * (x - mu_x) + sin_phi * (y - mu_y)))
return amplitude
start = time.clock()
draw_data = calculate_intensity_array()
print time.clock()-start
Perhaps there is something that catches your eye that should be done different? The main calculation happens in calculate_amplitude, but I tried to input only sin, cos values, such that they don't have to be evaluated each time again.
The C++ equivalent looks like:
void calculate_intensity_array()
{
double intensity_array[400][400];
static complex<double> laser1[400][400];
static complex<double> laser2[400][400];
double A1 = 1;
double sigma1 = 2000;
double cos_theta1 = 0.9999619;
double sin_theta1 = 0.00872654;
double cos_phi1 = 1;
double sin_phi1 = 0;
double mu_x1 = 0.0;
double mu_y1 = 0.0;
double A2 = 1;
double sigma2 = 2000;
double cos_theta2 = 0.9999619;
double sin_theta2 = 0.00872654;
double cos_phi2 = 1;
double sin_phi2 = 0;
double mu_x2 = 0.0;
double mu_y2 = 0.0;
for (int i=0; i<400; i++)
{
for (int j=0; j<400; j++)
{
laser1[i][j] = calculate_amplitude((i-200)*10, (j-200)*10, A1,
sigma1, sin_phi1, cos_phi1,
sin_theta1, cos_theta1,
mu_x1, mu_y1);
laser2[i][j]=calculate_amplitude((i-200)*10, (j-200)*10, A2,
sigma2, sin_phi2, cos_phi2,
sin_theta2, cos_theta2,
mu_x2, mu_y2);
intensity_array[i][j] = pow(abs(laser1[i][j] + laser2[i][j]), 2);
}
}
}
complex<double> calculate_amplitude(double x, double y, double A,
double sigma, double sin_phi,
double cos_phi, double sin_theta,
double cos_theta, double mu_x,
double mu_y)
{
complex<double> output;
output = A * (1 / (sqrt(2 * M_PI * pow(sigma / cos_theta, 2)))) *
exp(-(pow(cos_phi * (x - 200 - mu_x) + sin_phi * (y - 200 - mu_y), 2) *
pow(cos_theta, 2)) / (2 * pow(sigma, 2))) *
(1 / (sqrt(2 * M_PI * pow(sigma, 2)))) *
exp(-(pow(-sin_phi * (x - 200 - mu_x) + cos_phi *
(y - 200 - mu_y), 2)) / (2 * pow(sigma, 2))) *
exp(complex<double>(0, (2 * M_PI / 0.650) * sin_theta *
(cos_phi * (x - 200 - mu_x) + sin_phi * (y - 200 - mu_y))));
return output;
}

Turn your code into C++ automagically!
The following python code looks like yours:
#pythran export loop(int, float, float, float, float, float, float, float, float)
from math import exp, sqrt, pi
import cmath
from numpy import empty
def loop(n,A, sigma, sin_phi, cos_phi,
sin_theta, cos_theta, mu_x, mu_y):
out = empty((n,n), dtype=complex)
for x in range(n):
for y in range(n):
out[x,y] = calculate_amplitude(x,y,A, sigma, sin_phi, cos_phi,
sin_theta, cos_theta, mu_x, mu_y)
return out
def calculate_amplitude(x, y, A, sigma, sin_phi, cos_phi,
sin_theta, cos_theta, mu_x, mu_y):
amplitude = (A * (1 / (sqrt(2 * pi * (sigma ** 2 / cos_theta ** 2)))) *
exp(-((cos_phi *(x - mu_x) + sin_phi *(y - mu_y)) ** 2 * cos_theta ** 2) /
(2 * sigma ** 2)) *
(1 / (sqrt(2 * pi * sigma ** 2))) *
exp(-((-sin_phi * (x - mu_x) + cos_phi * (y - mu_y)) ** 2) /
(2 * sigma ** 2)) *
cmath.exp(1j *(2 * pi / 0.650) * sin_theta *
(cos_phi * (x - mu_x) + sin_phi * (y - mu_y))))
return amplitude
Then compiling it with pythran:
$ pythran laser.py
And run it through timeit:
$ python -m timeit -s 'import laser' 'laser.loop(20,1,200,0.9,0.08,1,.5,.5,2)'
10000 loops, best of 3: 84.7 usec per loop
While the original code ran in:
$ python -m timeit -s 'import laser' 'laser.loop(20,1,200,0.9,0.08,1,.5,.5,2)'
100 loops, best of 3: 2.65 msec per loop
You can probably achieve a similar result with numba or cython :-)

Related

how to pass two arrays into a fuction

I have a function that typically takes in constant args and calculates volatility. I want to pass in a vector of different C's and K's to get an array of volatilities each associated with C[i], K[i]
def vol_calc(S, T, C, K, r, q, sigma):
d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
vega = (1 / np.sqrt(2 * np.pi)) * np.exp(-q * T) * np.sqrt(T) * np.exp((-si.norm.cdf(d1, 0.0, 1.0) ** 2) * 0.5)
tolerance = 0.000001
x0 = sigma
xnew = x0
xold = x0 - 1
while abs(xnew - xold) > tolerance:
xold = xnew
xnew = (xnew - fx - C) / vega
return abs(xnew)
but if I want to pass two arrays without turning into a nested loop, I thought I could just do:
def myfunction(S, T, r, q, sigma):
for x in K,C:
return same size as K,C
but I can't get it to work
How about this?
def vol_calc(S, T, C, K, r, q, sigma):
import numpy as np
output = np.zeros(len(C))
for num, (c, k) in enumerate(zip(C, K)):
d1 = (np.log(S / k) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
vega = (1 / np.sqrt(2 * np.pi)) * np.exp(-q * T) * np.sqrt(T) * np.exp((-si.norm.cdf(d1, 0.0, 1.0) ** 2) * 0.5)
tolerance = 0.000001
x0 = sigma
xnew = x0
xold = x0 - 1
while abs(xnew - xold) > tolerance:
xold = xnew
xnew = (xnew - fx - c) / vega
output[num] = abs(xnew)
return output

How to solve equation using sympy?

I need to write a code to solve the McLachlan model equation.
to find value for c after substituting with different parameters (x and h ) from for-loops
how to do that ??!
I have the code written in matlab that makes what I exactly need .. but the same idea is not working for python I am getting errors !!
Traceback (most recent call last):
File "func.py", line 18, in <module>
(x * f ** (1 / h) - x * c ** (1 / h))
NameError: name 'c' is not defined
here is my code in python
import numpy
from sympy import Symbol, solve
v_p = input("Enter perculion threshold:")
sigma_P = input("Enter MOFs conductivity:")
sigma_F = input("Enter filler conductivity:")
p = float(sigma_P)
f = float(sigma_F)
x = Symbol('c')
A = (1 - float(v_p) / float(v_p))
for h in numpy.arange(1, 1.5, 0.1):
for x in numpy.arange(0, 1, 0.1):
print(solve([
(
(x * f ** (1 / h) - x * c ** (1 / h))
/
(f ** (1 / h) + A * c ** (1 / h))
)
/
(
(p ** (1 / h) - c ** (1 / h) - x * p ** (1 / h) + x * c ** (1 / h))
/
(p ** (1 / h) + A * c ** (1 / h))
)
], [c]))
and this is the code written in matlab
syms sigma_c
A=4.777
sigma_f = 550
sigma_p = 1.7 * 10 ^ (-11)
for h = 2:10
for j = 1:10
v_f = 0.1 * j;
ans = solve([(((v_f) * (((sigma_f) ^ (1 / h)) - ((sigma_c) ^ (1 / h))))/(((sigma_f) ^ (1 / h)) + ((A) * ((sigma_c) ^ (1 / h))))) + (((1 - v_f) * (((sigma_p) ^ (1 / h)) - ((sigma_c) ^ (1 / h))))/(((sigma_p) ^ (1 / h)) + ((A) * ((sigma_c) ^ (1 / h))))) == 0], [sigma_c]);
answer = double(ans)
arr(h,j) = answer;
end
end
disp(arr)
You receive the "SyntaxError: invalid syntax" because not all parentheses are closed. The code below suggests are formatting to give more overview in the computation. I expect the ')' should be added on line 25, however this is obviously ambigious and you should verify this with your own idea.
Note that 'c' is still undefined, your code won't work without it.
import numpy
from sympy import Symbol, solve
v_p = input("Enter perculion threshold:")
sigma_P = input("Enter MOFs conductivity:")
sigma_F = input("Enter filler conductivity:")
p = float(sigma_P)
f = float(sigma_F)
x = Symbol('c')
A = (1 - float(v_p) / float(v_p))
for h in numpy.arange(1, 1.5, 0.1):
for x in numpy.arange(0, 1, 0.1):
print(solve([
(
(x * f ** (1 / h) - x * c ** (1 / h))
/
(f ** (1 / h) + A * c ** (1 / h))
)
/
(
(p ** (1 / h) - c ** (1 / h) - x * p ** (1 / h) + x * c ** (1 / h))
/
(p ** (1 / h) + A * c ** (1 / h))
)
], [c]))
SymPy can help a lot with the symbolic part. If you copy and paste your working equation and then replace it with the symbols you are trying to use in the Python version you will get an expression that is not the same as the one you entered in the Python version:
>>> eq2=S('''(((v_f) * (((sigma_f) ^ (1 / h)) - ((sigma_c) ^ (1 / h)))
)/(((sigma_f) ^ (1 / h)) + ((A) * ((sigma_c) ^ (1 / h))))) + ((
(1 - v_f) * (((sigma_p) ^ (1 / h)) - ((sigma_c) ^ (1 / h))))/((
(sigma_p) ^ (1 / h)) + ((A) * ((sigma_c) ^ (1 / h)))))'''.replace('^','**'))
>>> eq2 = eq2.subs(
'v_f','x').subs(
'sigma_f','f').subs(
'sigma_c','c').subs(
'sigma_p','p')
>>> factor_terms(eq2)
x*(-c**(1/h) + f**(1/h))/(A*c**(1/h) + f**(1/h)) + (1 - x)*(-c**(1/h) + p**(1/h))/(
A*c**(1/h) + p**(1/h))
But the good news is that either equation can be solved symbolically for c**(1/h), since it is quadratic in that expression, so you can then substitute values for x and h into the solutions after you compute them. A convenient way to do this is, for example, is
>>> soln = Tuple(*solve(x**2 - y, x))
>>> for yi in (2, 3):
... print(soln.subs(y, yi).n()) # the .n() to evaluate the values

Python3: How do I make this equation readable?

So for this internship I am doing, I need to use the integral of (x^(9/2))/((1-x)^2) as part of an equation I am graphing. However, the variable that I am graphing along the x axis appears in both limits of integration. Since I am a complete and total novice at python, my code is atrocious, but I ended up copy+pasting the indefinite integral twice, plugging in the limits of integration, and subtracting. How can I make the code better?
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
x = np.arange(0,2.5,0.00001)
zs = 8
zr = 6
rbub = 5
sig = 2.5
XHI = 0.5
sigma = 10**-sig
L = 10**-3
zb = zs - (((1 + zs)/8)**(3/2)) * (0.0275) * (rbub/10)
a = (1+zr)/((1+zs)*(x+1))
b = (1+zb)/((1+zs)*(x+1))
def f(x):
ans = 0.000140092
ans = ans * ((1+zs)**(3/2))
ans = ans * ((x+1)**(3/2))
ans = ans * XHI
return ans * ((9/2)*(np.log(1-np.sqrt(b)) - np.log(np.sqrt(b)+1)) + (1/35 * (1/(b-1)) * (10*(b**(9/2)) + 18*(b**(7/2)) + 42*(b**(5/2)) + 210*(b**(3/2)) - 315*(b**(1/2))) - ((9/2)*(np.log(1-np.sqrt(a)) - np.log(np.sqrt(a)+1)) + (1/35 * (1/(a-1)) * (10*(a**(9/2)) + 18*(a**(7/2)) + 42*(a**(5/2)) + 210*(a**(3/2)) - 315*(a**(1/2)))))))
Here is one take. Of course, I have no idea what this is doing. You should be in a much better position to add comments / sensible variable names, as others have pointed out. Still, here is what you could do.
First, run a code formatter to make the code more human-friendly.
def f(x):
ans = 0.000140092
ans = ans * ((1 + zs) ** (3 / 2))
ans = ans * ((x + 1) ** (3 / 2))
ans = ans * XHI
return ans * (
(9 / 2) * (np.log(1 - np.sqrt(b)) - np.log(np.sqrt(b) + 1))
+ (
1
/ 35
* (1 / (b - 1))
* (
10 * (b ** (9 / 2))
+ 18 * (b ** (7 / 2))
+ 42 * (b ** (5 / 2))
+ 210 * (b ** (3 / 2))
- 315 * (b ** (1 / 2))
)
- (
(9 / 2) * (np.log(1 - np.sqrt(a)) - np.log(np.sqrt(a) + 1))
+ (
1
/ 35
* (1 / (a - 1))
* (
10 * (a ** (9 / 2))
+ 18 * (a ** (7 / 2))
+ 42 * (a ** (5 / 2))
+ 210 * (a ** (3 / 2))
- 315 * (a ** (1 / 2))
)
)
)
)
)
Right away you see some symemtry. This chunk
10 * (b ** (9 / 2))
+ 18 * (b ** (7 / 2))
+ 42 * (b ** (5 / 2))
+ 210 * (b ** (3 / 2))
- 315 * (b ** (1 / 2))
is a dot product of some weights and a b raised to a vector of powers. If b were scalar, we could write it as np.dot(weights, np.sqrt(b) ** powers). Maybe we would even score some optimization points from using integral powers.
Putting thigs together, we can get something like this:
weights = np.array([10, 18, 42, 210, -315])
powers = np.array([9, 7, 5, 3, 1])
def log_term(x):
return (9 / 2) * (np.log(1 - np.sqrt(x)) - np.log(np.sqrt(x) + 1))
def dot_term(x):
return (1 / 35) * 1 / (x - 1) * np.dot(np.sqrt(x)[..., None] ** powers, weights)
def integrate(x):
return log_term(x) + dot_term(x)
factor1 = integrate(b) - integrate(a)
factor2 = 0.000140092 * ((1 + zs) ** (3 / 2)) * XHI
factor = factor1 * factor2
def f(x):
return factor * ((x + 1) ** (3 / 2))
With better variable names and comments this could almost be readable.
Side comment. Both in your original code and in this version, you define x in the body of your script. You also define several variables as a function of x, such as a and b.
Python scoping rules mean that these variables will not change if you pass a different x to f. If you want all of your variables to change with x, you should move the definitions inside the function.
Using good variable names will help a lot for who gonna read the code or even for you, and comments will help too.
For the rest, it is a equation, there is no good way of putting it.

Python error: operands could not be broadcast together with shapes

I'm going to do 2D Gaussian fit in Python 3 by:
def gaussian_fit_2_d(data, amplitude, x0, y0, sigma_x, sigma_y, theta, offset):
x0 = np.float64(x0)
y0 = np.float64(y0)
a = (np.cos(theta) ** 2) / (2 * sigma_x ** 2) + (np.sin(theta) ** 2) / (2 * sigma_y ** 2)
b = -np.sin(2 * theta) / (4 * sigma_x ** 2) + np.sin(2 * theta) / (4 * sigma_y ** 2)
c = (np.sin(theta) ** 2) / (2 * sigma_x ** 2) + (np.cos(theta) ** 2) / (2 * sigma_y ** 2)
x = data["x"]
y = data["y"]
f = offset + amplitude * np.exp(-((a * (x - x0)) ** 2 + 2 * b * (x - x0) * (y - y0) + c * (y - y0) ** 2))
return f.ravel()
image = sio.loadmat("File Name")
intensity = np.float64(np. array(image["p1"]))
data_size = intensity.shape
x = np.arange(0, data_size[1])
y = np.arange(0, data_size[0])
# print(data_size)
# print(x)
[X, Y] = np.meshgrid(x, y)
data = {"x": X, "y": Y}
popt, pcov = opt.curve_fit(gaussian_fit_2_d, data, intensity)
,but in the last line I got the following error:
ValueError: operands could not be broadcast together with shapes
(1558,) (41,38)
any idea?

ode integration in python versus mathematica results

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.

Categories

Resources