I have to solve the following differential equation:
or
Without the F_1 term the code is straight forward. But I fail to solve it with the F_1 term included though I know the solution should look like a damped harmonic oscillation.
from scipy.integrate import odeint
import numpy as np
import matplotlib.pyplot as plt
def harmonic_motion(X,t,k,m,tau):
x,v=X
F=-(k/m)*x
F_1=tau/v*(np.gradient(x,t)**2) # This line doesn't work.
dx_dt=v
dv_dt=F-F_1
dX_dt=[dx_dt,dv_dt]
return dX_dt
m=1
k=1
tau=0.1
X0=-3
V0=0
t = np.linspace(0, 15, 250)
sol = odeint(harmonic_motion, [X0,V0], t,args=(k,m,tau))
x=sol[:,0]
v=sol[:,1]
plt.plot(t,x,label='x')
plt.plot(t,v,label='v')
plt.legend()
plt.xlabel('t (s)')
plt.ylabel('x,v (m,m/s)')
plt.show()
The correct version for a spring with air friction is
m*x'' + tau*abs(x')*x' + k*x = 0
The first order system for this is
def harmonic_motion(X,t,k,m,tau):
x,v = X
return v, -(tau*abs(v)*v + k*x)/m
The phase plot of this should now give a nice spiral to the origin.
Related
I tried solving a very simple equation f = t**2 numerically. I coded a for-loop, so as to use f for the first time step and then use the solution of every loop through as the inital function for the next loop.
I am not sure if my approach to solve it numerically is correct and for some reason my loop only works twice (one through the if- then the else-statement) and then just gives zeros.
Any help very much appreciatet. Thanks!!!
## IMPORT PACKAGES
import numpy as np
import math
import sympy as sym
import matplotlib.pyplot as plt
## Loop to solve numerically
for i in range(1,4,1):
if i == 1:
f_old = t**2
print(f_old)
else:
f_old = sym.diff(f_old, t).evalf(subs={t: i})
f_new = f_old + dt * (-0.5 * f_old)
f_old = f_new
print(f_old)
Scipy.integrate package has a function called odeint that is used for solving differential equations
Here are some resources
Link 1
Link 2
y = odeint(model, y0, t)
model: Function name that returns derivative values at requested y and t values as dydt = model(y,t)
y0: Initial conditions of the differential states
t: Time points at which the solution should be reported. Additional internal points are often calculated to maintain accuracy of the solution but are not reported.
Example that plots the results as well :
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
# function that returns dy/dt
def model(y,t):
k = 0.3
dydt = -k * y
return dydt
# initial condition
y0 = 5
# time points
t = np.linspace(0,20)
# solve ODE
y = odeint(model,y0,t)
# plot results
plt.plot(t,y)
plt.xlabel('time')
plt.ylabel('y(t)')
plt.show()
I have written this code to model the motion of a spring pendulum
import numpy as np
from scipy.integrate import odeint
from numpy import sin, cos, pi, array
import matplotlib.pyplot as plt
def deriv(z, t):
x, y, dxdt, dydt = z
dx2dt2=(0.415+x)*(dydt)**2-50/1.006*x+9.81*cos(y)
dy2dt2=(-9.81*1.006*sin(y)-2*(dxdt)*(dydt))/(0.415+x)
return np.array([x,y, dx2dt2, dy2dt2])
init = array([0,pi/18,0,0])
time = np.linspace(0.0,10.0,1000)
sol = odeint(deriv,init,time)
def plot(h,t):
n,u,x,y=h
n=(0.4+x)*sin(y)
u=(0.4+x)*cos(y)
return np.array([n,u,x,y])
init2 = array([0.069459271,0.393923101,0,pi/18])
time2 = np.linspace(0.0,10.0,1000)
sol2 = odeint(plot,init2,time2)
plt.xlabel("x")
plt.ylabel("y")
plt.plot(sol2[:,0], sol2[:, 1], label = 'hi')
plt.legend()
plt.show()
where x and y are two variables, and I'm trying to convert x and y to the polar coordinates n (x-axis) and u (y-axis) and then graph n and u on a graph where n is on the x-axis and u is on the y-axis. However, when I graph the code above it gives me:
Instead, I should be getting an image somewhat similar to this:
The first part of the code - from "def deriv(z,t): to sol:odeint(deriv..." is where the values of x and y are generated, and using that I can then turn them into rectangular coordinates and graph them. How do I change my code to do this? I'm new to Python, so I might not understand some of the terminology. Thank you!
The first solution should give you the expected result, but there is a mistake in the implementation of the ode.
The function you pass to odeint should return an array containing the solutions of a 1st-order differential equations system.
In your case what you are solving is
While instead you should be solving
In order to do so change your code to this
import numpy as np
from scipy.integrate import odeint
from numpy import sin, cos, pi, array
import matplotlib.pyplot as plt
def deriv(z, t):
x, y, dxdt, dydt = z
dx2dt2 = (0.415 + x) * (dydt)**2 - 50 / 1.006 * x + 9.81 * cos(y)
dy2dt2 = (-9.81 * 1.006 * sin(y) - 2 * (dxdt) * (dydt)) / (0.415 + x)
return np.array([dxdt, dydt, dx2dt2, dy2dt2])
init = array([0, pi / 18, 0, 0])
time = np.linspace(0.0, 10.0, 1000)
sol = odeint(deriv, init, time)
plt.plot(sol[:, 0], sol[:, 1], label='hi')
plt.show()
The second part of the code looks like you are trying to do a change of coordinate.
I'm not sure why you try to solve the ode again instead of just doing this.
x = sol[:,0]
y = sol[:,1]
def plot(h):
x, y = h
n = (0.4 + x) * sin(y)
u = (0.4 + x) * cos(y)
return np.array([n, u])
n,u = plot( (x,y))
As of now, what you are doing there is solving this system:
Which leads to x=e^t and y=e^t and n' = (0.4 + e^t) * sin(e^t) u' = (0.4 + e^t) * cos(e^t).
Without going too much into the details, with some intuition you could see that this will lead to an attractor as the derivative of n and u will start to switch sign faster and with greater magnitude at an exponential rate, leading to n and u collapsing onto an attractor as shown by your plot.
If you are actually trying to solve another differential equation I would need to see it in order to help you further
This is what happen if you do the transformation and set the time to 1000:
I've got a system of equations that I've been tryin to get Python to solve and plot but the plot is not coming out right.
This is my code:
from scipy.integrate import odeint
import numpy as np
import matplotlib.pyplot as plt
#function that returns dx/dt and dy/dt
def func(z,t):
for r in range(-10,10):
beta=2
gamma=0.8
c = z[0]
tau = z[1]
dcdt = r*c+c**2-c**3-beta*c*tau**2
dtaudt = -gamma*tau+0.5*beta*c*tau
return [dcdt,dtaudt]
#inital conditions
z0 = [2,0]
#time points
t = np.linspace(0,24,100)
#solve ODE
z = odeint(func,z0,t)
#seperating answers out
c = z[:,0]
tau = z[:,1]
print(z)
#plot results
plt.plot(t,c,'r-')
plt.plot(t,tau,'b--')
plt.legend(['c(t)','tau(t)'])
plt.show()
Let me explain. I am studying doubly diffusive convection. I din't want any assumptions to be made on the value of r, but beta and gamma are positive. So I thougt to assign values to them but not to r.
This is the plot I get and from understanding the problem, that the graph is not right. The tau plot should efinitely not be stuck on 0 and the c plot should be doing more. I am relitively new to Python and am taking courses but really want to understand what I've done wrong, so help in a simple language would be appreciated.
I see 2 problems in your function that you should check.
for r in range(-10,10):
Here you are doing a for loop just reevaluating dcdt and dtaudt. As a result, the output value is the same as just evaluating r=9 (last value in the loop)
dtaudt = -gamma*tau+0.5*beta*c*tau
Here you have dtaudt = tau*(beta*c/2. -gamma). Your choice tau[0]=0 implies that tau will remain 0.
Try this:
from scipy.integrate import odeint
import numpy as np
import matplotlib.pyplot as plt
r = 1
beta=2
gamma=0.8
#function that returns dx/dt and dy/dt
def func(z,t):
c = z[0]
tau = z[1]
dcdt = r*c+c**2-c**3-beta*c*tau**2
dtaudt = -gamma*tau+0.5*beta*c*tau
print(dtaudt)
return [dcdt,dtaudt]
#inital conditions
z0 = [2,0.2] #tau[0] =!0.0
#time points
t = np.linspace(0,24,100)
#solve ODE
z = odeint(func,z0,t)
#seperating answers out
c = z[:,0]
tau = z[:,1]
#plot results
plt.plot(t,c,'r-')
plt.plot(t,tau,'b--')
plt.legend(['c(t)','tau(t)'])
plt.show()
I am calculating the convolution of two functions, exp(-bt^2 + iat) and exp(-c|t| + iat). If try to analytically calculate it in Mathematica with
Convolve[Exp[-b*t^2 + I*a*t], Exp[-c*Abs[t] + I*a*t], t, \[Tau]]
I get the solution
Then if I plot this solution in Python, and compare it with the result from numerical convolution scipy.signal.fftconvolve, they don't agree with each other. The x-intercepts seem ok but there is a problem with the vertical scaling.
Context: This is a MWE - I ultimately want to work just in Python after finding an analytical solution in Mathematica. It would be simpler to use an explicit expression instead of having to calculate a numerical convolution.
import numpy as np
from scipy.special import erfc
from scipy.signal import fftconvolve
import matplotlib.pyplot as plt
t = np.arange(-100,100,1e6) # time
a = np.random.rand()
b = np.random.rand()
c = np.random.rand()
x = np.exp(-b*t**2+1j*a*t)
y = np.exp(-c*abs(t)+1j*a*t)
z = 2*np.sqrt(np.pi/b)*np.exp(1j*a*t+c**2/(4*b)) * ( \
np.exp(-c*t)*erfc((c-2*b*t)/2*np.sqrt(b)) + \
np.exp(c*t)*erfc((c+2*b*t)/2*np.sqrt(b)) )
zc = fftconvolve(x,y,mode='same')*(t[1]-t[0])
plt.figure(1,figsize=(12,4))
plt.subplot(121)
plt.plot(t,np.real(zc),'--',label='conv')
plt.plot(t,np.real(z),label='analytic')
plt.xlabel('time')
plt.title('real part')
plt.legend(loc='best')
plt.grid()
plt.xlim((-20,20))
plt.subplot(122)
plt.plot(t,np.imag(zc),'--',label='conv')
plt.plot(t,np.imag(z),'-',label='analytic')
plt.xlabel('time')
plt.title('imag part')
plt.legend(loc='best')
plt.grid()
plt.xlim((-20,20))
You must multiply final result with the step size. If you write convolution integral as a Riemann sum you will see that it differs by dx from numerical definition.
I was trying to integrate a square wave using python 3.5 and the scipy.integrate.odeint function but the results don't make any sense and vary wildly with the array of time points selected.
The square wave has a period of 10sec and the simulation runs for 100sec. Since the array of time points has size 500, there will be 50 time points on each period of the square wave, but that doesn't seem to be happening.
Using the optional parameter hmax=0.02 fixes it, but shouldn't it be inferred automatically?
Here's the code:
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
# dx/dt = f(t), where f(t) is a square wave
def f(x, t):
return float(t % 10.0 < 5.0) * 0.3
T = 100
tt = np.linspace(0, T, 500)
xx = integrate.odeint(f, 0, tt, hmax=0.2)
plt.figure()
plt.subplot(2,1,1)
plt.plot(tt, xx)
plt.axis([0,T,0,16])
plt.subplot(2,1,2)
plt.plot(tt, [f(None,t) for t in tt])
plt.axis([0, T, 0, 1])
plt.show()
I'm hoping someone can put some light into what is happening here.
Try changing T between 80 and 100 (simulation time).
I think your problem is that the odeint function takes continuous Ordinary Differential Equations which a square wave is not.
i'd start by redefining your square-wave function to:
def g(t):
return float(t % 10.0 < 5.0) * 0.3
then define a function to calculate the integral step-by-step:
def get_integral(tt):
intarray = np.zeros_like(tt)
step_size = tt[1] -tt[0]
for i,t in enumerate(tt):
intarray[i] = intarray[i-1] + g(t)*step_size
return intarray
Then:
xx = get_integral(tt)
should give you the result you're looking for.