How do I numerically solve an ODE in Python?
Consider
\ddot{u}(\phi) = -u + \sqrt{u}
with the following conditions
u(0) = 1.49907
and
\dot{u}(0) = 0
with the constraint
0 <= \phi <= 7\pi.
Then finally, I want to produce a parametric plot where the x and y coordinates are generated as a function of u.
The problem is, I need to run odeint twice since this is a second order differential equation.
I tried having it run again after the first time but it comes back with a Jacobian error. There must be a way to run it twice all at once.
Here is the error:
odepack.error: The function and its Jacobian must be callable functions
which the code below generates. The line in question is the sol = odeint.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from numpy import linspace
def f(u, t):
return -u + np.sqrt(u)
times = linspace(0.0001, 7 * np.pi, 1000)
y0 = 1.49907
yprime0 = 0
yvals = odeint(f, yprime0, times)
sol = odeint(yvals, y0, times)
x = 1 / sol * np.cos(times)
y = 1 / sol * np.sin(times)
plot(x,y)
plt.show()
Edit
I am trying to construct the plot on page 9
Classical Mechanics Taylor
Here is the plot with Mathematica
In[27]:= sol =
NDSolve[{y''[t] == -y[t] + Sqrt[y[t]], y[0] == 1/.66707928,
y'[0] == 0}, y, {t, 0, 10*\[Pi]}];
In[28]:= ysol = y[t] /. sol[[1]];
In[30]:= ParametricPlot[{1/ysol*Cos[t], 1/ysol*Sin[t]}, {t, 0,
7 \[Pi]}, PlotRange -> {{-2, 2}, {-2.5, 2.5}}]
import scipy.integrate as integrate
import matplotlib.pyplot as plt
import numpy as np
pi = np.pi
sqrt = np.sqrt
cos = np.cos
sin = np.sin
def deriv_z(z, phi):
u, udot = z
return [udot, -u + sqrt(u)]
phi = np.linspace(0, 7.0*pi, 2000)
zinit = [1.49907, 0]
z = integrate.odeint(deriv_z, zinit, phi)
u, udot = z.T
# plt.plot(phi, u)
fig, ax = plt.subplots()
ax.plot(1/u*cos(phi), 1/u*sin(phi))
ax.set_aspect('equal')
plt.grid(True)
plt.show()
The code from your other question is really close to what you want. Two changes are needed:
You were solving a different ODE (because you changed two signs inside function deriv)
The y component of your desired plot comes from the solution values, not from the values of the first derivative of the solution, so you need to replace u[:,0] (function values) for u[:, 1] (derivatives).
This is the end result:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
def deriv(u, t):
return np.array([u[1], -u[0] + np.sqrt(u[0])])
time = np.arange(0.01, 7 * np.pi, 0.0001)
uinit = np.array([1.49907, 0])
u = odeint(deriv, uinit, time)
x = 1 / u[:, 0] * np.cos(time)
y = 1 / u[:, 0] * np.sin(time)
plt.plot(x, y)
plt.show()
However, I suggest that you use the code from unutbu's answer because it's self documenting (u, udot = z) and uses np.linspace instead of np.arange. Then, run this to get your desired figure:
x = 1 / u * np.cos(phi)
y = 1 / u * np.sin(phi)
plt.plot(x, y)
plt.show()
You can use scipy.integrate.ode. To solve dy/dt = f(t,y), with initial condition y(t0)=y0, at time=t1 with 4th order Runge-Kutta you could do something like this:
from scipy.integrate import ode
solver = ode(f).set_integrator('dopri5')
solver.set_initial_value(y0, t0)
dt = 0.1
while t < t1:
y = solver.integrate(t+dt)
t += dt
Edit: You have to get your derivative to first order to use numerical integration. This you can achieve by setting e.g. z1=u and z2=du/dt, after which you have dz1/dt = z2 and dz2/dt = d^2u/dt^2. Substitute these into your original equation, and simply iterate over the vector dZ/dt, which is first order.
Edit 2: Here's an example code for the whole thing:
import numpy as np
import matplotlib.pyplot as plt
from numpy import sqrt, pi, sin, cos
from scipy.integrate import ode
# use z = [z1, z2] = [u, u']
# and then f = z' = [u', u''] = [z2, -z1+sqrt(z1)]
def f(phi, z):
return [z[1], -z[0]+sqrt(z[0])]
# initialize the 4th order Runge-Kutta solver
solver = ode(f).set_integrator('dopri5')
# initial value
z0 = [1.49907, 0.]
solver.set_initial_value(z0)
values = 1000
phi = np.linspace(0.0001, 7.*pi, values)
u = np.zeros(values)
for ii in range(values):
u[ii] = solver.integrate(phi[ii])[0] #z[0]=u
x = 1. / u * cos(phi)
y = 1. / u * sin(phi)
plt.figure()
plt.plot(x,y)
plt.grid()
plt.show()
scipy.integrate() does ODE integration. Is that what you are looking for?
Related
I am working on the following code, which solves a system of coupled differential equations. I have been able to solve them, and I plotted one of them. I am curious how to compute and plot the derivative of this graph numerically (I know the derivative is given in the first function, but suppose I didn't have that). I was thinking that I could use a for-loop, but is there a faster way?
import numpy as np
from scipy.integrate import odeint
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
import math
def hiv(x,t):
kr1 = 1e5
kr2 = 0.1
kr3 = 2e-7
kr4 = 0.5
kr5 = 5
kr6 = 100
h = x[0] # Healthy Cells -- function of time
i= x[1] #Infected Cells -- function of time
v = x[2] # Virus -- function of time
p = kr3 * h * v
dhdt = kr1 - kr2*h - p
didt = p - kr4*i
dvdt = -p -kr5*v + kr6*i
return [dhdt, didt, dvdt]
print(hiv([1e6, 0, 100], 0))
x0 = [1e6, 0, 100] #initial conditions
t = np.linspace(0,15,1000) #time in years
x = odeint(hiv, x0, t) #vector of the functions H(t), I(t), V(t)
h = x[:,0]
i = x[:,1]
v = x[:,2]
plt.semilogy(t,h)
plt.show()
I want to solve the equation in python over the time Interval I = [0,10] with initial condition (x_0, y_0) = (1,0) and the parameter values μ ∈ {-2, -1, 0, 1, 2} using the function
scipy.integrate.odeint
Then I want to plot the solutions (x(t;x_0,y_0), y(t;x_0,y_0)) in the xy-plane.
The originally given linear system is
dx/dt = y, x(0) = x_0
dy/dt = - x - μy, y(0) = y_0
Please see my code below:
import numpy as np
from scipy.integrate import odeint
sol = odeint(myode, y0, t , args=(mu,1)) #mu and 1 are the coefficients when set equation to 0
y0 = 0
myode(y, t, mu) = -x-mu*y
def t = np.linspace(0,10, 101) #time interval
dydt = [y[1], -y[0] - mu*y[1]]
return dydt
Could anyone check if I defined the callable function myode correctly? This function evaluates the right hand side of the ODE.
Also an syntax error message showed up for this line of code
def t = np.linspace(0,10, 101) #time interval
saying there is invalid syntax. Should I somehow use
for * in **
to get rid of the error message? If yes, how exactly?
I am very new to Python and ODE. Could anyone help me with this question? Thank you very much!
myode should be a function definition, thus
def myode(u, t, mu): x,y = u; return [ y, -x-mu*y]
The time array is a simple variable declaration/assignment, there should be no def there. As the system is two-dimensional, the initial value also needs to have dimension two
sol = odeint(myode, [x0,y0], t, args=(mu,) )
Thus a minimal modification of your script is
def myode(u, t, mu): x,y = u; return [ y, -x-mu*y]
t = np.linspace(0,10, 101) #time interval
x0,y0 = 1,0 # initial conditions
for mu in [-2,-1,0,1,2]:
sol = odeint(myode, [x0,y0], t, args=(mu,) )
x,y = sol.T
plt.plot(x,y)
a=5; plt.xlim(-a,a); plt.ylim(-a,a)
plt.grid(); plt.show()
giving the plot
Try using the solve_ivp method.
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
import numpy as np
i = 0
u = [-2,-1,0,1,2]
for x in u:
def rhs2(t,y):
return [y[1], -1*y[0] - u[x]*y[1]]
value = u[i]
res2 = solve_ivp(rhs2, [0,10], [1,0] , t_eval=[0,1,2,3,4,5,6,7,8,9,10], method = 'RK45')
t = np.array(res2.t[1:-1])
x = np.array(res2.y[0][1:-1])
y = np.array(res2.y[1][1:-1])
fig = plt.figure()
plt.plot(t, x, 'b-', label='X(t)')
plt.plot(t, y, 'g-', label='Y(t)')
plt.title("u = {}".format(value))
plt.legend(loc='lower right')
plt.show()
i = i + 1
Here is the solve_ivp method Documentation
Here is a very similar problem with a better explanation.
I would like to indicate the starting point of the graph - where the line started. This is my code
import numpy as np
from scipy.integrate import odeint
from numpy import sin, cos, pi, array
import matplotlib
from matplotlib import rcParams
import matplotlib.pyplot as plt
from pylab import figure, axes, title, show
import xlsxwriter
def deriv(z, t):
l = 0.3 #unextended length of the spring, in m
m = 1 #mass of the bob, in kg
k = 1 #spring constant, in Nm^-1
g = 9.81 #gravitational acceleration, in ms^-2
x, y, dxdt, dydt = z
dx2dt2 = (l+x)*(dydt)**2 - k/m*x + g*cos(y)
dy2dt2 = (-g*sin(y) - 2*(dxdt)*(dydt))/(l+x)
#equations of motion
return np.array([dxdt, dydt, dx2dt2, dy2dt2])
init = array([0.3, pi/2, 0.0, 2])
#initial conditions (x, y, xdot, ydot)
time = np.linspace(0, 100, 10000)
#time intervals (start, end, number of intervals)
sol = odeint(deriv, init, time)
#solving the equations of motion
x = sol[:,0]
y = sol[:,1]
l = 0.3 #unextended length of the spring, in m
n = (l+x) * sin(y)
u = -(l+x) * cos(y)
#converting x and y to Cartesian coordinates
plt.plot(n,u)
plt.xlabel('$n$ (m)')
plt.ylabel('$u$ (m)')
plt.title('$n$ versus $u$ for 'r'$\theta_0 = \frac{\pi}{2}+0.001$')
plt.show()
which generates this graph:
However, it is unclear where the line actually started (somewhere in the upper right, I think, near where it ended). Is there some way I can add a brightly coloured dot to the starting point not specific just to this graph (i.e. so I can reproduce in on other graphs with different conditions)?
Thank you!
Plotting the first point can be done by adding plt.plot(n[0], u[0], '*') to your code, see below.
Full documentation for the plot function (thanks for the comment mostlyoxygen), to get a better idea of how you can change the colour, size and shape of the dot.
from scipy.integrate import odeint
from numpy import array, linspace, sin, cos, pi, array
from matplotlib import rcParams
import matplotlib.pyplot as plt
def deriv(z, t):
l = 0.3 #unextended length of the spring, in m
m = 1 #mass of the bob, in kg
k = 1 #spring constant, in Nm^-1
g = 9.81 #gravitational acceleration, in ms^-2
x, y, dxdt, dydt = z
dx2dt2 = (l+x)*(dydt)**2 - k/m*x + g*cos(y)
dy2dt2 = (-g*sin(y) - 2*(dxdt)*(dydt))/(l+x)
#equations of motion
return array([dxdt, dydt, dx2dt2, dy2dt2])
init = array([0.3, pi/2, 0.0, 2])
#initial conditions (x, y, xdot, ydot)
time = linspace(0, 100, 10000)
#time intervals (start, end, number of intervals)
sol = odeint(deriv, init, time)
#solving the equations of motion
x = sol[:,0]
y = sol[:,1]
l = 0.3 #unextended length of the spring, in m
n = (l+x) * sin(y)
u = -(l+x) * cos(y)
#converting x and y to Cartesian coordinates
plt.plot(n,u)
plt.plot(n[0], u[0], '*')
plt.xlabel('$n$ (m)')
plt.ylabel('$u$ (m)')
plt.title('$n$ versus $u$ for 'r'$\theta_0 = \frac{\pi}{2}+0.001$')
plt.show()
I am trying to solve the ivp y'=-y-5 * exp(-t) * sin(5 t), y(0)=1, using the following code:
%pylab inline
%matplotlib inline
from scipy.integrate import odeint
def mif(t, y):
return -y-5*exp(-t)*sin(5*t)
tspan = np.arange(0, 3, 0.000001)
y0 = 1.0
y_result = odeint(mif, y0, tspan)
y_result = y_result[:, 0] # convert the returned 2D array to a 1D array
plt.figure()
plt.plot(tspan, y_result)
plt.show()
However, the plot I get is wrong, it does not match what I obtain, say, with Matlab or Mathematica. It is actually different from the following alternative integration:
from scipy.integrate import ode
# initialize the 4th order Runge-Kutta solver
solver = ode(mif).set_integrator('dop853')
# initial value
y0 = 1.0
solver.set_initial_value(y0, 0)
values = 1000
t = np.linspace(0.0001, 3, values)
y = np.zeros(values)
for ii in range(values):
y[ii] = solver.integrate(t[ii])[0] #z[0]=u
which does yield correct result. What am I doing wrong with the odeint?
The function arguments change between ode and odeint. For odeint you need
def mif(y, t):
and for ode
def mif(t, y):
e.g.
%pylab inline
%matplotlib inline
from scipy.integrate import odeint
def mif(t,y):
return y
tspan = np.arange(0, 3, 0.000001)
y0 = 0.0
y_result = odeint(mif, y0, tspan)
plt.figure()
plt.plot(tspan, y_result)
plt.show()
and
from scipy.integrate import ode
def mif(y, t):
return y
# initialize the 4th order Runge-Kutta solver
solver = ode(mif).set_integrator('dop853')
# initial value
y0 = 0.000000
solver.set_initial_value([y0], 0.0)
values = 1000
t = np.linspace(0.0000001, 3, values)
y = np.zeros(values)
for ii in range(values):
y[ii] = solver.integrate(t[ii]) #z[0]=u
plt.figure()
plt.plot(t, y)
plt.show()
I have a trajectory formed by a sequence of (x,y) pairs. I would like to interpolate points on this trajectory using splines.
How do I do this? Using scipy.interpolate.UnivariateSpline doesn't work because neither x nor y are monotonic. I could introduce a parametrization (e.g. length d along the trajectory), but then I have two dependent variables x(d) and y(d).
Example:
import numpy as np
import matplotlib.pyplot as plt
import math
error = 0.1
x0 = 1
y0 = 1
r0 = 0.5
alpha = np.linspace(0, 2*math.pi, 40, endpoint=False)
r = r0 + error * np.random.random(len(alpha))
x = x0 + r * np.cos(alpha)
y = x0 + r * np.sin(alpha)
plt.scatter(x, y, color='blue', label='given')
# For this special case, the following code produces the
# desired results. However, I need something that depends
# only on x and y:
from scipy.interpolate import interp1d
alpha_i = np.linspace(alpha[0], alpha[-1], 100)
r_i = interp1d(alpha, r, kind=3)(alpha_i)
x_i = x0 + r_i * np.cos(alpha_i)
y_i = x0 + r_i * np.sin(alpha_i)
plt.plot(x_i, y_i, color='green', label='desired')
plt.legend()
plt.show()
Using splprep you can interpolate over curves of any geometry.
from scipy import interpolate
tck,u=interpolate.splprep([x,y],s=0.0)
x_i,y_i= interpolate.splev(np.linspace(0,1,100),tck)
Which produces a plot like the one given, but only using the x and y points and not the alpha and r paramters.
Sorry about my original answer, I misread the question.