odeint from scipy.integrate in Python giving wrong result? - python

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()

Related

How to solve a system of ODEs with Scipy when the variables in the equations are autogenerated

I'm generating a system of ODEs in the form of a list of equations where the variables are sympy.Symbol(), for example [3*sympy.Symbol('x')**3+sympy.Symbol('y') , sympy.Symbol('x')-sympy.Symbol('y')**4].
So using this example, I can solve this system through the code
import numpy as np
from sympy import Symbol
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
def kin(t, z):
x, y = z
eqs = [3*x**3+y , x-y**4]
return eqs
y0 = [0, 10]
t = np.linspace(0, 10000, 10000)
sol = solve_ivp(kin, [0, 10000], y0, dense_output=True)
print(sol.sol(t).T)
plt.plot(t, sol.sol(t).T)
plt.show()
And it will give this result https://imgur.com/3KvOkrU
But since the variables of x and y are in reality generated by a different code, I figured I could do
import numpy as np
from sympy import Symbol
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
dic = {'x': Symbol('x'), 'y': Symbol('y')}
vars = ('x', 'y')
#eqs =[3x^3+y , x-y^4]
eqs = [3*dic[vars[0]]**3+dic[vars[1]], dic[vars[0]]-dic[vars[1]]**4]
def kin(t, z):
dics = {}
for v in range(len(vars)):
dics[vars[v]] = z[v]
for e in range(len(eqs)):
eqs[e] = eqs[e].subs(dics)
return eqs
y0 = [0, 10]
t = np.linspace(0, 10000, 10000)
sol = solve_ivp(kin, [0, 10000], y0, dense_output=True)
print(sol.sol(t).T)
plt.plot(t, sol.sol(t).T)
plt.show()
Where it merely replaces the values of x and y from the first part by z[0] and z[1] but the output of this code is completely different https://imgur.com/ovi3mk8
I dont see where the issue is and would appreciate if anyone code point out where the problem is or help me find a way to evaluate these auto-generated systems of ODEs .
The reason you are getting that results is because you are overwriting eqs inside kin at each iteration. Also, as mentioned by Lutz, you should use lambdify which is going to convert symbolic expressions to numerical functions so that they can be evaluated by Numpy (much faster).
import numpy as np
from sympy import Symbol
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
dic = {'x': Symbol('x'), 'y': Symbol('y')}
vars = ('x', 'y')
eqs = [3*dic[vars[0]]**3+dic[vars[1]], dic[vars[0]]-dic[vars[1]]**4]
# convert symbolic expressions to numerical functions
eqs = [lambdify(list(dic.values()), e) for e in eqs]
def kin(t, z):
# evaluate each numerical function with the current values
return [e(*z) for e in eqs]
y0 = [0, 10]
t = np.linspace(0, 10000, 10000)
sol = solve_ivp(kin, [0, 10000], y0, dense_output=True)
plt.plot(t, sol.sol(t).T)
plt.show()
This will give you the first picture you posted.

Solving a PDE using FFT ( Cahn Hilliard equation)

I am trying to solve the cahn hilliard equation using fft on python. My code is simple and logical however it is not working, the final mixture is not mixing correctly , I am obtaining a random concentration at the end. Here is my code, any help is greatly appreciated. The equation has two laplacian derivatives. (dc/dt = laplacian( c^3 - c - a^2*laplacian(c)) )
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
import matplotlib.animation as ani
from scipy.fft import fft, ifft
C=np.random.uniform(-1,1, size=(128,128))
C=np.reshape(C,(len(C),len(C)))
print(C)
w2xx=np.zeros((len(C),len(C)))
w2yy=np.zeros((len(C),len(C)))
f2xx=np.zeros((len(C),len(C)))
f2yy=np.zeros((len(C),len(C)))
E=np.zeros((len(C),len(C)))
Cold=np.zeros((len(C),len(C)))
Cnew=E=np.zeros((len(C),len(C)))
#w2xx=np.reshape(A,(len(C),len(C)))
#w2yy=np.reshape(B,(len(C),len(C)))
#f2xx=np.reshape(D,(len(C),len(C)))
E=np.reshape(E,(len(C),len(C)))
delta_t=10**-8
a=0.01
delta_x=7.814*(10**-3)
x = np.linspace(0, 1, len(C))
y = np.linspace(0, 1,len(C))
x1 =np.arange(0, 127 , 1)
y1= np.linspace(0,1, len(C))
X, Y = np.meshgrid(x, y)
plt.contourf(X, Y, C, 10, cmap='RdGy')
plt.show()
time= np.arange(0, 0.012/delta_t , 1)
cc1=1
cc2=1
N=128
k1=np.arange(0,(N/2),1)
k2=np.arange((-N/2)+1,0,1)
k=np.r_[k1,0,k2]
k=np.fft.fftshift(k)
for t in time:
for i in x1:
vx_hat=fft(C[i,0:len(C)])
vy_hat=fft(C[0:len(C),i])
wx_hat=np.multiply(-(k**2),(vx_hat.real))
wy_hat=np.multiply(-(k**2),((vy_hat.real)))
w2xx[i,:]=ifft(wx_hat).real
w2yy[:,i]=ifft(wy_hat).real
F=(np.power(C,3))-C-((a**2)*(w2xx+w2yy))
for i in x1:
vx_hat=fft(F[i,:])
vy_hat=fft(F[:,i])
wx_hat=np.multiply(-(k**2),(vx_hat.real))
wy_hat=np.multiply(-(k**2),((vy_hat.real)))
f2xx[i,:]=ifft(wx_hat).real
f2yy[:,i]=ifft(wy_hat).real
# if t<0.005:
# Cold=C
C=C+(((2*10**-6))*(f2xx+f2yy))
# else:
# Cnew = 2*C - Cold + ((delta_t**2)*(f2xx+f2yy))
# Cold = C
# C = Cnew
plt.contourf(X, Y, C, 10, cmap='RdGy')
plt.show()

Singular Jacobian in Boundary Value Problem

I am solving a set of coupled ODEs using solve_bvp in python. I have solved the equations for the boundary conditions that U = 0 and B = 0 on the boundaries, however I am trying to solve them such that U' = 0 and B = 0 on the boundary. My problem is that I keep encountering a singular jacobian in the return message - my guess is that the initial guess is diverging, however I have tried a range of initial guesses and still no solution. Is there a more systematic way of figuring this out? My code is below:
import numpy as np
from scipy import integrate
from scipy.integrate import solve_bvp
import matplotlib.pyplot as plt
%matplotlib inline
x = np.linspace(-0.5, 0.5, 1000)
y = np.ones((4, x.size))
y[0] = U_0*np.tanh(Q*x)
U_0 = 0.1
Q = 30
## Then we calculate the total energy
def FullSolver(x,y,Ha, Rm):
def fun(x, y):
U, dU, B, dB = y;
d2U = -2*U_0*Q**2*(1/np.cosh(Q*x))**2*np.tanh(Q*x)-((Ha**2)/(Rm))*dB;
d2B = -Rm*dU;
return dU, d2U, dB, d2B
def bc(ya, yb):
return ya[1], yb[1], ya[2]-0, yb[2]+0
# sol will give us the solutions which are accessible if we need them
sol = solve_bvp(fun, bc, x, y, tol=1e-12)
return(sol.x, sol.yp[0], sol.y[2], sol.message)
FullSolver(x, y, 0.000005, 0.00009)

Solve equation in python over a given time interval with initial condition given

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.

Numerical ODE solving in Python

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?

Categories

Resources