python non linear ODE with 2 variables - python

I am trying to solve the Brusselator model, a non-linear ODE, using python. I used to do this with MATLAB but now am building an application with python as a backend. That's why I want to switch ti python.
dx/dt = A + (x^2)(y) - Bx - x
dy/dt = Bx - (x^2)(y)
I have checked stackoverflow and most of examples I found are simple non-linear ODE with a single variable that can be converted into a system of linear equation. [- Sorry for my notation. I don't know how to add latex in stackoverflow]

I know this is an old question. Nevertheless, I managed to formulate your question in the code below.
There, I implemented your two differential equations in a single function, that I later integrate through ODEINT module from scypy.
I hope this answer your problem.
Sincerely yours,
import scipy.integrate
import numpy as np
import matplotlib.pyplot as plt
def Integrate(y, t, B, A):
X, Y = y
dX_dt = A + (X**2)*(Y) - B*X - X
dY_dt = B*X - (X**2)*(Y)
return [dX_dt, dY_dt]
A0 = 0.9
B0 = 0.6
X0 = 0.1
Y0 = 0.0
B0 = 0.35
t = np.linspace(0,100, 10000)
solution = scipy.integrate.odeint(Integrate, y0=[X0, Y0], t=t, args=(A0, B0) )
plt.plot(t, solution[:,1], label='solution')
plt.legend()
plt.xlabel('time')
plt.ylabel('Y')
plt.show()

Related

fsolve gives weird answers

I want to use fsolve to numerically find roots of a nonlinear transcendent equation.
The following code does this job.
import numpy as np
from scipy.optimize import fsolve
import matplotlib.pyplot as plt
kappa = 0.1
tau = 90
def equation(x, * parameters):
kappa,tau = parameters
return -x + kappa * np.sin(-tau*x)
x = np.linspace(-0.5,0.5, 35)
roots = fsolve(equation,x, (kappa,tau))
x_2 = np.linspace(-1.5,1.5,1500)
plt.plot(x_2 ,x_2 )
plt.plot(x_2 , kappa*np.sin(-x_2 *tau))
plt.scatter(x, roots)
plt.show()
I can double check the solutions graphically by plotting the two graphs f1(x)=x and f2(x)=k * sin(-x * tau), which i also included in the code.
fsolve gives me some wrong answers, without throwing any errors or convergence problems.
The Problem is, that I would like to automatize the procedure for varying kappa and tau, without me checking which answers are wrong and which are right. But with wrong answers as output, i can't use this method. Is there any other method or an option I can use, to be on the safe side?
Thanks for the help.
I couldn't run your code, there were errors and anyway, according to the documentation on scipy.fsolve, you're supposed to add an initial guess as the second input argument, not a range as what you did there fsolve(equation, x0, (kappa,tau))
You could of course however pass this in a loop, looping for every value in the array np.linspace(0.5, 0.5, 25). Although I do not understand what you are trying to achieve by varying kappa and tau, but if I take it that for those given parameters, you are interested in looking for the roots, here's how I would do it.
import numpy as np
from scipy.optimize import fsolve
import matplotlib.pyplot as plt
# Take it as it is
kappa = 0.1
tau = 90
def equation(x, parameters):
kappa,tau = parameters
return -x + kappa * np.sin(-tau*x)
# Initial guess of x = -0.1
SolutionStack = []
x0 = -kappa
y = fsolve(equation, x0, [kappa, tau])
SolutionStack.append(y[0])
y = fsolve(equation, SolutionStack[-1], [kappa, tau])
SolutionStack.append(y[0])
deltaY = SolutionStack[-1] - SolutionStack[0]
# Define tolerance
tol = 5e-4
while ((SolutionStack[-1] <= kappa) and (deltaY <= tol)):
y = fsolve(equation, SolutionStack[-1], [kappa, tau])
SolutionStack.append(y[0])
deltaY = SolutionStack[-1] - SolutionStack[-2]
# Obviously a little guesswork is involved here, as it pertains to 0.07
if deltaY <= tol:
SolutionStack[-1] = SolutionStack[-1] + 0.07
# Obtaining the roots
Roots = []
Roots.append(SolutionStack[0])
for i in range(len(SolutionStack)-1):
if (SolutionStack[i+1] - SolutionStack[i]) <= tol:
continue
else:
Roots.append(SolutionStack[i+1]
Probably not the smartest way to do it (assuming I even understood you correctly), but perhaps you have an idea now.

How to solve a 9-equations system of non linear DE with python?

I'm desperately trying to solve (and display the graph) a system made of nine nonlinear differential equations which model the path of a boomerang. The system is the following:
All the letters on the left side are variables, the others are either constants or known functions depending on v_G and w_z
I have tried with scipy.odeint with no conclusive results (I had this issue but the workaround did not work.)
I begin to think that the problem is linked with the fact that these equations are nonlinear or that the function in denominator might cause a singularity that the scipy solver is simply unable to handle. However, I am not familiar with that sort of mathematical knowledge.
What possibilities python-wise do I have to solve this set of equations?
EDIT : Sorry if I was not clear enough. Since it models the path of a boomerang, my goal is not to solve analytically this system (ie I don't care about the mathematical expression of each function), but rather to get the values of each function for a specific time range (say, from t1 = 0s to t2 = 15s with an interval of 0.01s between each value) in order to display the graph of each function and the graph of the center of mass of the boomerang (X,Y,Z are its coordinates).
Here is the code I tried :
import scipy.integrate as spi
import numpy as np
#Constants
I3 = 10**-3
lamb = 1
L = 5*10**-1
mu = I3
m = 0.1
Cz = 0.5
rho = 1.2
S = 0.03*0.4
Kz = 1/2*rho*S*Cz
g = 9.81
#Initial conditions
omega0 = 20*np.pi
V0 = 25
Psi0 = 0
theta0 = np.pi/2
phi0 = 0
psi0 = -np.pi/9
X0 = 0
Y0 = 0
Z0 = 1.8
INPUT = (omega0, V0, Psi0, theta0, phi0, psi0, X0, Y0, Z0) #initial conditions
def diff_eqs(t, INP):
'''The main set of equations'''
Y=np.zeros((9))
Y[0] = (1/I3) * (Kz*L*(INP[1]**2+(L*INP[0])**2))
Y[1] = -(lamb/m)*INP[1]
Y[2] = -(1/(m * INP[1])) * ( Kz*L*(INP[1]**2+(L*INP[0])**2) + m*g) + (mu/I3)/INP[0]
Y[3] = (1/(I3*INP[0]))*(-mu*INP[0]*np.sin(INP[6]))
Y[4] = (1/(I3*INP[0]*np.sin(INP[3]))) * (mu*INP[0]*np.cos(INP[5]))
Y[5] = -np.cos(INP[3])*Y[4]
Y[6] = INP[1]*(-np.cos(INP[5])*np.cos(INP[4]) + np.sin(INP[5])*np.sin(INP[4])*np.cos(INP[3]))
Y[7] = INP[1]*(-np.cos(INP[5])*np.sin(INP[4]) - np.sin(INP[5])*np.cos(INP[4])*np.cos(INP[3]))
Y[8] = INP[1]*(-np.sin(INP[5])*np.sin(INP[3]))
return Y # For odeint
t_start = 0.0
t_end = 20
t_step = 0.01
t_range = np.arange(t_start, t_end, t_step)
RES = spi.odeint(diff_eqs, INPUT, t_range)
However, I keep getting the same problem as shown here and especially the error message :
Excess work done on this call (perhaps wrong Dfun type)
I am not quite sure what it means but it looks like the solver have troubles solving the system. In any case, when I try to display the 3D path thanks to the XYZ coordinates, I just get 3 or 4 points where there should be something like 2000.
So my questions are : - Am I doing something wrong in my code ?
- If not, is there an other maybe more sophisticated tool to solve this sytem ?
- If not, is it even possible to get what I want from this system of ODEs ?
Thanks in advance
There are several issues:
if I copy the code, it does not run
the workaround you mention does not work with odeint, the given
solution uses ode
The scipy reference for odeint says:"For new code, use
scipy.integrate.solve_ivp to solve a differential equation."
the call RES = spi.odeint(diff_eqs, INPUT, t_range) should be
consistent to the function head def diff_eqs(t, INP) . Mind the
order: RES = spi.odeint(diff_eqs,t_range, INPUT)
There are some issues about to mathematical formulas too:
have a look at the 3rd formula on your picture. It has no tendency term, it starts with a zero - what does that mean ?
it's hard to check wether you have translated the formula correctly into code since the code does not follow the formulas strictly.
Below I tried a solution with scipy solve_ivp. In case A I'm able to run a pendulum, but in case B no meaningful solution for the boomerang can be found. So check the maths, I guess some error in the mathematical expressions.
For the graphics use pandas to plot all variables together (see code below).
import scipy.integrate as spi
import numpy as np
import pandas as pd
def diff_eqs_boomerang(t,Y):
INP = Y
dY = np.zeros((9))
dY[0] = (1/I3) * (Kz*L*(INP[1]**2+(L*INP[0])**2))
dY[1] = -(lamb/m)*INP[1]
dY[2] = -(1/(m * INP[1])) * ( Kz*L*(INP[1]**2+(L*INP[0])**2) + m*g) + (mu/I3)/INP[0]
dY[3] = (1/(I3*INP[0]))*(-mu*INP[0]*np.sin(INP[6]))
dY[4] = (1/(I3*INP[0]*np.sin(INP[3]))) * (mu*INP[0]*np.cos(INP[5]))
dY[5] = -np.cos(INP[3])*INP[4]
dY[6] = INP[1]*(-np.cos(INP[5])*np.cos(INP[4]) + np.sin(INP[5])*np.sin(INP[4])*np.cos(INP[3]))
dY[7] = INP[1]*(-np.cos(INP[5])*np.sin(INP[4]) - np.sin(INP[5])*np.cos(INP[4])*np.cos(INP[3]))
dY[8] = INP[1]*(-np.sin(INP[5])*np.sin(INP[3]))
return dY
def diff_eqs_pendulum(t,Y):
dY = np.zeros((3))
dY[0] = Y[1]
dY[1] = -Y[0]
dY[2] = Y[0]*Y[1]
return dY
t_start, t_end = 0.0, 12.0
case = 'A'
if case == 'A': # pendulum
Y = np.array([0.1, 1.0, 0.0]);
Yres = spi.solve_ivp(diff_eqs_pendulum, [t_start, t_end], Y, method='RK45', max_step=0.01)
if case == 'B': # boomerang
Y = np.array([omega0, V0, Psi0, theta0, phi0, psi0, X0, Y0, Z0])
print('Y initial:'); print(Y); print()
Yres = spi.solve_ivp(diff_eqs_boomerang, [t_start, t_end], Y, method='RK45', max_step=0.01)
#---- graphics ---------------------
yy = pd.DataFrame(Yres.y).T
tt = np.linspace(t_start,t_end,yy.shape[0])
with plt.style.context('fivethirtyeight'):
plt.figure(1, figsize=(20,5))
plt.plot(tt,yy,lw=8, alpha=0.5);
plt.grid(axis='y')
for j in range(3):
plt.fill_between(tt,yy[j],0, alpha=0.2, label='y['+str(j)+']')
plt.legend(prop={'size':20})

How do I convert the x and y values in polar form from these coupled ODEs to to cartesian form and graph them?

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:

How to integrate coupled differential equations?

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

Using dopri5 for ODE Integration and Stochastic Simulations

I'm still very much a beginner in python, but had some questions about running simulations (I apologize if this is the wrong area to post my question in which case, if someone would kindly show me where it would be better suited, I will gladly move it). My goal is to simulate the following system of differential equations:
Because the dynamical system was experiencing stiffness, the odeint method of integration I learned kept having problems, so I tried to integrate it another way, namely using the dopri5 method I have heard about. It seems to work but I've noticed some odd behavior in the plots. The code I was using is as follows:
import scipy as sp
import pylab as plt
import numpy as np
import scipy.integrate as spi
#Constants
c13 = 6.2
c14 = 1.0
c21 = 7.3
c32 = 2.4
c34 = 12.7
c42 = 5.7
c43 = 5.0
e12 = 1.5
e23 = 2.5
e24 = 2.0
e31 = 3.0
e41 = 4.8
#Time
t_end = 300.
t_start = 0.
t_step = 1.
t_interval = sp.arange(t_start, t_end, t_step)
#Initial Condition
ic = [0.6,0.3,0.2,0.5]
#Noise
sigma1 = 0.1
sigma2 = 0.1
sigma3 = 0.1
sigma4 = 0.1
def model(t,ic):
Eqs= np.zeros((4))
Eqs[0] = ic[0]+(ic[0]*(1-ic[0]*ic[0]-ic[1]*ic[1]-ic[2]*ic[2]-ic[3]*ic[3])-c21*((ic[1]*ic[1])*ic[0])+e31*((ic[2]*ic[2])*ic[0])+e41*((ic[3]*ic[3])*ic[0]))
Eqs[1] = ic[1]+(ic[1]*(1-ic[0]*ic[0]-ic[1]*ic[1]-ic[2]*ic[2]-ic[3]*ic[3])+e12*((ic[0]*ic[0])*ic[1])-c32*((ic[2]*ic[2])*ic[1])-c42*((ic[3]*ic[3])*ic[1]))
Eqs[2] = ic[2]+(ic[2]*(1-ic[0]*ic[0]-ic[1]*ic[1]-ic[2]*ic[2]-ic[3]*ic[3])-c13*((ic[0]*ic[0])*ic[2])+e23*((ic[1]*ic[1])*ic[2])-c43*((ic[3]*ic[3])*ic[2]))
Eqs[3] = ic[3]+(ic[3]*(1-ic[0]*ic[0]-ic[1]*ic[1]-ic[2]*ic[2]-ic[3]*ic[3])-c14*((ic[0]*ic[0])*ic[3])+e24*((ic[1]*ic[1])*ic[3])-c34*((ic[2]*ic[2])*ic[3]))
return Eqs
ode = spi.ode(model)
ode.set_integrator('dopri5',method='bdf')
ode.set_initial_value(ic,t_start)
ts = []
ys = []
while ode.successful() and ode.t < t_end:
ode.integrate(ode.t + t_step)
ts.append(ode.t)
ys.append(ode.y)
t = np.vstack(ts)
x1,x2,x3,x4 = np.vstack(ys).T
plt.subplot(4, 1, 1)
plt.plot(t, x1, 'b')
plt.xlim([0,t_end])
plt.subplot(4, 1, 2)
plt.plot(t, x2, 'r')
plt.xlim([0,t_end])
plt.subplot(4, 1, 3)
plt.plot(t, x3, 'g')
plt.xlim([0,t_end])
plt.subplot(4, 1, 4)
plt.plot(t, x4, 'purple')
plt.xlim([0,t_end])
plt.xlabel('Time')
plt.show()
However, the plot looks like this:
What I don't understand is why the plots reach a maximum of about 1.4. I did some analytical work and cross-referenced it with another paper that used these equations, and the maximum value it should reach is 1. Is it something in the dopri5 code that makes it go above 1? I also don't understand why there's a bit of spike appearing early on in each of the blue peaks (x1).
My other question is that I would like to do some stochastic simulation with these equations. So for this part, the equations I would like to work with are of the form:
dx_i = ()dt + sigma_i * x_i * dW_t
where dW_t is white noise. However, when I tried to modify the above equation and do this for example:
Eqs[0] = ic[0]+(ic[0]*(1-ic[0]*ic[0]-ic[1]*ic[1]-ic[2]*ic[2]-ic[3]*ic[3])-c21*((ic[1]*ic[1])*ic[0])+e31*((ic[2]*ic[2])*ic[0])+e41*((ic[3]*ic[3])*ic[0]))+sigma1*ic[0]*np.random.normal(0,1)
the code fails to run properly. It seems that what I wrote is not the proper method for making these equations stochastic. What is the best way to put stochastic perturbations into these equations? I've recently heard of something called PyS3DE but I know nothing about it.

Categories