How could I use scipy library to get the distance? - python

I just started to learn python, and my teacher asks me to simulate the model rocket trajectory knowing the thrust of the engine.
I have already got the speed and the acceleration of the rocket using odeint function. However, I don't know how to use the speed and time.
I've already got to get the distance the rocket traveled since the speed is solved by odeint function.
here's the code I wrote to get the speed:
def getforce(t):
if 0<=t<0.15:
F = 40*t
elif 0.15<=t<0.7:
F = -9.09*t+7.36
elif 0.7<=t<1.25:
F = 1
elif 1.25<=t<1.65:
F = 7.5*t-8.375
elif 1.65<=t<1.8:
F = -26.6*t+48
else:
F = 0
return F
def getspeed(x,t):
Ft = getforce(t)
y0,y1 = x
dy0 = y1
dy1 = (Ft-0.0001277422*y1**2*np.sign(y1)-0.174)/0.0177
return dy0,dy1
t = np.linspace(0,10,100)
sol = si.odeint(getspeed,(0,0),t)
plt.plot(t,sol[:,0])
plt.show()

Assuming that everything else is correct, you just integrate the speed by hand.
(The reason I can't check on the overall correctness easily is that you are using an unorthodox (given) expression in the speed section, rather than solving the rocked equation that includes the mass loss F= m a --> d(m*v)/dt = dm/dt * v + m * dv/dt.)
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as si
%matplotlib inline
def getforce(t):
if 0<=t<0.15:
F = 40*t
elif 0.15<=t<0.7:
F = -9.09*t+7.36
elif 0.7<=t<1.25:
F = 1
elif 1.25<=t<1.65:
F = 7.5*t-8.375
elif 1.65<=t<1.8:
F = -26.6*t+48
else:
F = 0
return F
def getspeed(x,t):
Ft = getforce(t)
y0,y1 = x
dy0 = y1
dy1 = (Ft-0.0001277422*y1**2*np.sign(y1)-0.174)/0.0177
return dy0,dy1
t = np.linspace(0,10,100)
sol = si.odeint(getspeed,(0,0),t)
v=sol[:,0]
x=0
xs=[]
dt=t[1]-t[0] # use linspace with 101 to get the sample distance you'd normally expect
for i in range(len(v)):
x=x+v[i]*dt
xs.append(x)
plt.subplot(121)
plt.plot(t,v)
plt.subplot(122)
plt.plot(t,xs)
plt.show()
I didn't use numpy or lambda expressions to integrate to keep it easily readable and because the speed of execution is immaterial for this case.

Related

Changing an ODE inside the function with ODEINT and python

This is my first question here on the forum, so I hope I'm doing evrything right. My problem is, I am supposed to solve an ODE with python. The ODE describes the movement of an electron inside of a "bubble" (a bubble is something that occurs in Plasmaphysics when you shoot with a laser at plasma but thats not that important for the question im asking). So my task is to build a programm that can plot the trajectory of such an electron. For that I have to basically solve 2 different ODE's since the electron inside the bubble is moving according to an ODE and if it exits the bubble it is in a field-free-area where it should move according to the classical equations of motion.
In the following I will show you the code if only 1 ODE is solved:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from math import sqrt,pi
def f(r,t):
px = r[0]
py = r[1]
x = r[2]
y = r[3]
gamma = sqrt(1+px**2+py**2)
dpx_dt = -(1+V)*(x-V*t)/4 + py*y/(4*gamma)
dpy_dt = -(1+px/gamma)*y/4
dx_dt = px/gamma
dy_dt = py/gamma
return [dpx_dt, dpy_dt, dx_dt, dy_dt]
def Plot_Aufgabe5(t,p_x,p_y,xi,y):
#Plotten der Trajektorie des Elektrons
plt.plot(xi,y)
return
#Konstanten
r_b = 10
m = 9.109*10**-31
c = 2.99*10**8
roh = 9
#Anfangsbedingungen
gamma_0 = 4
V = np.sqrt(((gamma_0**2) - 1)/(gamma_0**2))
t_end = 300
N = 1000
t = np.linspace(0,t_end,N)
y = [0, 0, np.sqrt(r_b**2 - roh**2), roh]
#Array mit äquidistanten Werten erstellen
Äqui_array = np.linspace(0,2*np.pi,10)
#Array mit verschd. Anfangsimpulsen
Impuls_array = [-1,-0.5,0.5,1]
for j in Impuls_array:
plt.figure()
#Plotten eines Kreises
theta = np.linspace(0,2*np.pi,t_end)
Punkt_x = r_b*np.cos(theta)
Punkt_y = r_b*np.sin(theta)
plt.plot(Punkt_x,Punkt_y)
for i in range(10):
#Neue Anfangsbedingungen aufstellen
y = [j,j,r_b*np.cos(Äqui_array[i]),r_b*np.sin(Äqui_array[i])]
#Ausführen des Solvers
sol = odeint(f,y,t)
p_x = sol[:,0]
p_y = sol[:,1]
x = sol[:,2]
xi = x - V*t
y = sol[:,3]
#Plot
Plot_Aufgabe5(t,p_x,p_y,xi,y)
plt.axis([-15,15,-10,10])
plt.xlabel(r'$xi_{num}$ = $k_p$$\xi$')
plt.ylabel(r'$y_{num}$ = $k_p$y')
plt.title('Trajektorie des Elekrons in einer Bubble mit Impuls p = ' + str(j) + '*mc')
plt.show()
Plots for the code for 1 ODE
As you can see in the plot, the electron goes outside the bubble but it comes back in which should not happen. (Also the plot is for 10 different electrons which start on the edge of the bubble)
Therefore I made an approach to change between 2 ODE's according to the position of the electron however that did not quite work:
def f(r,t):
px = r[0] #boundary conditions for the electron
py = r[1]
x = r[2]
y = r[3]
gamma = sqrt(1+px**2+py**2)
if ((x**2 + y**2) <= r_b**2): #if electron is inside bubble
dpx_dt = -(1+V)*(x-V*t)/4 + py*y/(4*gamma)
dpy_dt = -(1+px/gamma)*y/4
dx_dt = px/gamma
dy_dt = py/gamma
else: #if it is outside of the bubble
dpx_dt = 0
dpy_dt = 0
dx_dt = px/gamma
dy_dt = py/gamma
result = [dpx_dt, dpy_dt, dx_dt, dy_dt]
return result
My Idea here was that with the if-statement i check, whether the electron is inside the bubble or not and change the ODE according to that. However it does not really work out and im not sure why, underneath you can find the plots that come with that function (also the rest of the code stayed the same as above):
Plots for the Function to change between 2 ODE's
I hope my problem gets clear and that maybe someone has an idea that could help me! Good day to evryone :)

Second order coupled ODE using ODEINT

I am new to solving coupled ODEs with python, I am wondering if my approach is correct, currently this code outputs a graph that looks nothing like the expected output. These are the equations I am trying to solve:
And here is the code I am using (for the functions f_gr, f_sc_phi and f_gTheta you can just put any constant value)
import Radial as rd
import ScatteringAzimuthal as sa
import PolarComponent as pc
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
#gamma for now set to 1
g_mm = 1
def f(u,t):
#y1 = thetadot :: y2 = phidot :: y3 = cdot
rho, theta, y1, phi, y2, c, y3 = u
p = [y1, (pc.f_gTheta(theta,524.1+rho)/(c*np.cos(phi))-(g_mm*y1)+(2*y1*y2*np.tan(phi))-(2*y3*y1/c)),
y2, ((sa.f_sc_phi(theta,524.1+rho/c))-(g_mm*y2)-(2*y3*y2/c)-(np.sin(phi)*np.cos(phi)*y2**2)),
y3, (rd.f_gr(theta,524.1+rho)-(g_mm*y3)+(c*y2**2)+(c*(y1**2)*(np.cos(phi)**2))), phi]
return p
time = np.linspace(0,10,100)
z2 = odeint(f,[0.1,np.pi/2,0.1,np.pi/2,0.1,0.1,0.1], time)
rhoPl = z2[:,0]
thetaPl = z2[:,1]
phiPl = z2[:,3]
'''
plt.plot(rhoPl,time)
plt.plot(thetaPl,time)
plt.plot(phiPl,time)
plt.show()
'''
x = rhoPl*np.sin(thetaPl)*np.cos(phiPl)
y = rhoPl*np.sin(thetaPl)*np.sin(phiPl)
z = rhoPl*np.cos(thetaPl)
plt.plot(x,time)
plt.plot(y,time)
plt.plot(z,time)
plt.show()
when I change the time from 0.1 to 5 I get an error:
ODEintWarning: Excess work done on this call (perhaps wrong Dfun type). Run with full_output = 1 to get quantitative information.
Any ideas on how to improve this code or if my approach is completely incorrect?
Code for Radial.py
import numpy as np
from scipy.special import spherical_jn
from scipy.special import spherical_yn
import sympy as sp
import matplotlib.pyplot as plt
R_r = 5.6*10**(-5)
l = 720
n_w = 1.326
#k = 524.5/R_r
X_r = 524.5
# R is constant r is changing
def f_gr(theta,x):
f = ((sp.sin(theta))**(2*l-2))*(1+(sp.cos(theta))**2)
b = (spherical_jn(l,n_w*x)*spherical_jn(l,n_w*x,True))+(spherical_yn(l,n_w*x)*spherical_yn(l,n_w*x,True))
c = (spherical_jn(l,n_w*X_r)*spherical_jn(l,n_w*X_r,True))+(spherical_yn(l,n_w*X_r)*spherical_yn(l,n_w*X_r,True))
n = b/c
f = f*n
return f
Code for ScatteringAzimuthal.py
from scipy.special import spherical_jn, spherical_yn
import numpy as np
import matplotlib.pyplot as plt
l = 720
n_w = 1.326
n_p = 1.572
X_r = 524.5
R_r = 5.6*10**(-5)
R_p = 7.5*10**(-7)
k = X_r/R_r
def f_sc_phi(theta,x):
f = (2/3)*(n_w**2)*((X_r**3)/x)*((R_p**3)/(R_r**3))*(((n_p**2)-(n_w**2))/((n_p**2)+(2*(n_w**2))))
g = np.sin(theta)**(2*l-3)
numerator = (l*(1+np.sin(theta))- np.cos(2*theta))\
*((spherical_jn(l,n_w*x)*spherical_jn(l,n_w*x))+(spherical_yn(l,n_w*x)*spherical_yn(l,n_w*x)))
denominator = ((spherical_jn(l,n_w*X_r)*spherical_jn(l,n_w*X_r,True))\
+(spherical_yn(l,n_w*X_r)*spherical_yn(l,n_w*X_r,True)))
m = numerator/denominator
final = f*g*m
return final
And Code for PolarComponent.py
import numpy as np
from scipy.special import spherical_yn, spherical_jn
import matplotlib.pyplot as plt
l = 720
n_w = 1.326
X_r = 524.5 #this value is implemented in the ode file
#define dimensionless polar component
#X_r is radius, x is variable
def f_gTheta(theta,x):
bessel1 = (spherical_jn(l,n_w*x)*spherical_jn(l,n_w*x)) + \
(spherical_yn(l,n_w*x)*spherical_yn(l,n_w*x))
bessel2 = ((spherical_yn(l,n_w*X_r)*spherical_yn(l,n_w*X_r,True)) + \
(spherical_yn(l,n_w*X_r)*spherical_yn(l,n_w*X_r,True)))*n_w*x
bessels = bessel1/bessel2
rest = (np.sin(theta)**(2*l-3))*((l-1)*(1+(np.cos(theta)**2)) \
-((np.sin(theta)**2)*np.cos(theta)))
final = rest*bessels
return final
Here is a link that I really like for simulating second order odes. It has an optamization twist on it because it is fitting the model to match a simulation. It has a couple of examples for odeint and also gekko.

add PID controller to odeint integration

I have an integral to which I want to control the input by adding a P-controller to.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
xs = []
yd = []
def dY(y, x):
a = 0.001
yin = 1
C = 0.01
N = 1
dC = C/N
b1 = 0
y_diff = -np.copy(y)
y_diff[0] += yin
y_diff[1:] += y[:-1]
xs.append(x)
yd.append(y_diff)
return (a/dC)*y_diff+b1*dC
x = np.linspace(0,20,1000)
y0 = np.zeros(4)
res = odeint(dY, y0, x)
plt.plot(x,res, '-')
plt.gca().set_prop_cycle(plt.rcParams['axes.prop_cycle'])
plt.plot(np.array(xs),np.array(yd), '-.')
plt.show()
y_in is a variable that I want to control by a P/PI-controller in order to reach a certain setpoint. I have looked at it in different ways but I honestly have no idea where to start with implementing the controller.
I am familiar with the concept of PID controller and all related equations however this is a total new thing for me to implement in a case where ODEINT is used.
Hopefully someone could help me out.

I am getting two plots for one data set in python

I am working through example 8.1 titled Euler's Method from Mark Newman's book Computational Physics. I rewrote the example as a method with Numpy arrays but when I plot it I get two plots on the same figure not sure how to correct it. Also is there better way to convert my 2 1D arrays into 1 2D array to use for plotting in Matplotlib, thanks.
Newman's example :
from math import sin
from numpy import arange
from pylab import plot,xlabel,ylabel,show
def f(x,t):
return -x**3 + sin(t)
a = 0.0 # Start of the interval
b = 10.0 # End of the interval
N = 1000 # Number of steps
h = (b-a)/N # Size of a single step
x = 0.0 # Initial condition
tpoints = arange(a,b,h)
xpoints = []
for t in tpoints:
xpoints.append(x)
x += h*f(x,t)
plot(tpoints,xpoints)
xlabel("t")
ylabel("x(t)")
show()
My modifications:
from pylab import plot,show,xlabel,ylabel
from numpy import linspace,exp,sin,zeros,vstack,column_stack
def f(x,t):
return (-x**(3) + sin(t))
def Euler(f,x0,a,b):
N=1000
h = (b-a)/N
t = linspace(a,b,N)
x = zeros(N,float)
y = x0
for i in range(N):
x[i] = y
y += h*f(x[i],t[i])
return column_stack((t,x)) #vstack((t,x)).T
plot(Euler(f,0.0,0.0,10.0))
xlabel("t")
ylabel("x(t)")
show()
The reason you get two lines is that t as well as x are plotted against their index, instead of x plotted against t
I don't see why you'd want to stack the two arrays. Just keep then separate, which will also solve the problem of the two plots.
The following works fine.
import numpy as np
import matplotlib.pyplot as plt
f = lambda x,t: -x**3 + np.sin(t)
def Euler(f,x0,a,b):
N=1000
h = (b-a)/N
t = np.linspace(a,b,N)
x = np.zeros(N,float)
y = x0
for i in range(N):
x[i] = y
y += h*f(x[i],t[i])
return t,x
t,x = Euler(f,0.0,0.0,10.0)
plt.plot(t,x)
plt.xlabel("t")
plt.ylabel("x(t)")
plt.show()

Find an Inverse for the Exponential Integral function

I have a program where I have to find x.
But I have to use the special function Ei - the exponential integral, and x is inside the argument of Ei.
So Python isn't recognizing it.
ei(mx) = te^r + ei(c)
Here the RHS is a constant alongwith m.
I want to find the value of x, and then append it to a list. But Python isn't able to do this.
from scipy import special
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
Y = []
X = np.arange(0,10,.1)
for i in X:
y = scipy.special.expi(i)
Y.append(y)
N_0 = 2
t_f = 100
r = 2
K = 100
N_t = [N_0,]
t = np.arange(0,100,1)
for i in t:
l = i*e**r + scipy.special.expi(r*N_t[i]/K)
N_t.append(l)
plt.plot(X,Y)
plt.plot(t,N_t)
plt.show
I've corrected some mistakes in your code to give the following. You should compare this with your code line by line.
from scipy.special import expi
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
Y = []
X = np.arange(0,10,.1)
for i in X:
y = expi(i)
Y.append(y)
N_0 = 2
t_f = 100
r = 2
K = 100
N_t = [N_0,]
t = np.arange(0,100,1)
for i in t:
l = i*np.exp(r) + expi(r*N_t[i]/K)
N_t.append(l)
plt.plot(X,Y)
plt.plot(t,N_t)
plt.show()
However, there is still one possible flaw that I notice and can't resolve. You plot X and t together in the same graph at the end yet X ranges over 0 to 10 and t ranges over 0 to 100. Is this what you intended?
Also matplotlib complains that the lengths of the vectors supplied to it in the second call to plot are not the same.

Categories

Resources