Using dopri5 for ODE Integration and Stochastic Simulations - python

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.

Related

Numerically Solving A Multivariate Differential Equation Without odeint

I am curious. I know this can be solved by using odeint, but I'm trying to do it from scratch, and I've encountered an interesting behaviour.
Assume a simple oscillator, of equation m * x_ddot + k * x = 0. Wikipedia
Initial conditions are x0 != 0;
Theoretically, the solution is a sine function.
But, on python, the solution is a sine that keeps growing in amplitude. Now I'm curious why that's happening, because it shouldn't. Does it have to do with numerical stability, or something similar? Like, why is it diverging? From a physics point of view, there is no reason it should, so why is it behaving as such?
Here's the code.
dt = 0.05
t_start = 0
t_finish = 20
t = 0
x1 = 1
X1 = []
x2 = 0
X2 = []
while t <= t_finish:
X1.append(x1)
X2.append(x2)
# state space representation
x1_dot = x2
x2_dot = -9.81*x1
x1 += dt*x1_dot
x2 += dt*x2_dot
t += dt
# to make sure the vectors are of equal size for plotting
if len(X1) > len(time):
X1 = X1[:len(X1)-1]
elif len(X1) < len(time):
time = time[:len(time)-1]
plt.figure(figsize=(10,10))
plt.plot(time,X1)
plt.grid()
Here's the plot.
I'm thankful for any insight you guys can offer.
I think the issue is in your understanding of Euler scheme. This is a very simple ODE, in fact its a textbook example for the harmonic oscillation system. The Euler scheme is pretty much based off N = T/(dt) where N is the number of steps, T the final time and dt the step size. So, if your step size or final time is not small relative to the N, the solution drifts up.
There is no need to use RK4, Euler will get the job done. The trick though is that you need a small dt. I have rewritten your scheme in a more clear way and used an appropriate dt respectively.
import numpy as np
import matplotlib.pyplot as plt
# Parameters
t_finish = 20.0
dt = 0.00005 # Very small dt (infinitesmal dt)
n = int(t_finish/dt)
tvalue = np.linspace(0, t_finish, n+1)
x1 = np.zeros(n+1)
x2 = np.zeros(n+1)
# create canvas
plt.figure(figsize=(10, 5))
# Initialize
x1[0] = 1.0 # Not at zero
x2[0] = 0.0
# Simulation with Euler scheme
for i in range(n):
t = (i+1)*dt
x1[i+1] = x1[i] + x2[i]*dt
x2[i+1] = x2[i] -9.81*x1[i]*dt
# Plot paths
plt.plot(tvalue, x1, label=r'$x_1$')
plt.plot(tvalue, x2, label=r'$x_2$')
# Add legend and axes labels
plt.legend(loc=0)
plt.xlabel(r'$t$')
plt.ylabel(r'$x_{t}$')
plt.show()

Why is my Python BZ Oregonator unable to compile?

I am trying to simulate the colour changes within the solution of the Belousov-Zhabotinsky reaction by solving ODEs and to produce a graph which will demonstrate oscillations using odeint.
I have an error message 'AxisError: axis -1 is out of bounds for array of dimension 0' and I do not know why this is happening, I am very new to Python and I am struggling.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
# Dimensionless parameters
c1 = 10
c2 = 0.15
c3 = 0.005
c4 = 0.02
#pack 3 initial conditions with state of x,y,z into y0
y0 = [1,0,0]
k = 1
def Oregonator (t,Y):
x = Y[0]
y = Y[1]
z = Y[2]
dxdt = c1 + c2*x - x - x*y**2
dydt = (x + x*y**2 - y)/c3
dzdt = (y-z)/c4
return [dxdt,dydt,dzdt]
t = np.linspace(0, 10,100)
Y = odeint(y0,t,Oregonator)
plt.plot(t,Y)
plt.xlabel('time')
plt.ylabel('other side')
plt.show()
I am using Spyder to process the Python code.
I appreciate any help that can be given, thank you.
You need to fix the call to odeint to be conform with its documentation, here at least
Y = odeint(Oregonator,y0,t, tfirst=True)
And you need to equalize the indentation in the Oregonator function.
Then you get an oscillating graph.
Also, increase the display resolution, 100 points in an interval with over 100 oscillations is much too few. As there are sharp peaks you want at least 20 points per oscillation, better more, thus
t = np.linspace(0, 10,5000)

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

python non linear ODE with 2 variables

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

Using dopri5 to plot a system of ODEs in matrix form

The system of equations I'm interested in plotting is the following:
I was able to plot them modifying an example someone posted by doing the following:
import scipy as sp
import pylab as plt
import numpy as np
import scipy.integrate as spi
#Constants
c13 = 4.2
c14 = 4.2
c21 = 4.3
c32 = 4.4
c34 = 4.4
c42 = 4.4
c43 = 4.4
e12 = 1.9
e23 = 2.5
e24 = 2.2
e31 = 2.0
e41 = 2.0
#Time
t_end = 700
t_start = 0
t_step = 1
t_interval = sp.arange(t_start, t_end, t_step)
#Initial Condition
r = [0.2,0.3,0.3,0.5]
def model(t,r):
Eqs= np.zeros((4))
Eqs[0] = (r[0]*(1-r[0]*r[0]-r[1]*r[1]-r[2]*r[2]-r[3]*r[3])-c21*((r[1]*r[1])*r[0])+e31*((r[2]*r[2])*r[0])+e41*((r[3]*r[3])*r[0]))
Eqs[1] = (r[1]*(1-r[0]*r[0]-r[1]*r[1]-r[2]*r[2]-r[3]*r[3])+e12*((r[0]*r[0])*r[1])-c32*((r[2]*r[2])*r[1])-c42*((r[3]*r[3])*r[1]))
Eqs[2] = (r[2]*(1-r[0]*r[0]-r[1]*r[1]-r[2]*r[2]-r[3]*r[3])-c13*((r[0]*r[0])*r[2])+e23*((r[1]*r[1])*r[2])-c43*((r[3]*r[3])*r[2]))
Eqs[3] = (r[3]*(1-r[0]*r[0]-r[1]*r[1]-r[2]*r[2]-r[3]*r[3])-c14*((r[0]*r[0])*r[3])+e24*((r[1]*r[1])*r[3])-c34*((r[2]*r[2])*r[3]))
return Eqs
ode = spi.ode(model)
ode.set_integrator('dopri5')
ode.set_initial_value(r,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(1, 1, 1)
plt.plot(t, x1, 'r', label = 'x1')
plt.plot(t, x2, 'b', label = 'x2')
plt.plot(t, x3, 'g', label = 'x3')
plt.plot(t, x4, 'purple', label = 'x4')
plt.xlim([0,t_end])
plt.legend()
plt.ylim([-0.2,1.5])
plt.show()
This certainly appears to give me the plot I want. However, I want to end up doing stochastic analysis with this set of ODEs, and for that reason, it is much easier to model this if the system of ODEs is written in matrix form (that way, I can easily change the dimension of the noise and see how that affects the ODEs). I understand how mathematically to write the equation in matrix form, but I don't understand how to modify my code so that in the "def model(t,r):" part, it's read as an array/matrix. To convert the equations to matrix form, I can define:
b = np.array([1, 1, 1, 1])
A = np.array([[1, 1+c21, 1-e31, 1-e41],
[1-e12, 1, 1+c32, 1+c42],
[c13+1, 1-e23, 1, 1+c43],
[c14+1, 1-e24, 1+c34, 1]])
And then the system of equations would be (where x is the vector (x1,x2,x3,x4)):
x(t) = diag(x)[b^{T}-Adiag(x)x]
So my question is: How do I modify where I defined my ODEs so that I can enter them as a matrix instead of writing out each equation individually? (this would also make it easier if I later look at a system with more than 4 dimensions)
Using the implemented preference for numpy.array operations to act element-wise (in contrast to numpy.matrix operations that operate in matrix fashion) the formula for the system of equations is simply
def model(t,x):
return x*(b-A.dot(x*x))
x*x produces the vector of element-wise squares, x**2 would be another option, A.dot(x2) performs the matrix-vector product for numpy.array objects, and x*(b-...) is again the vector-valued element-wise product of the two operand vectors.
Using u=x*x as variables reduces the system to
dotu = 2*u*(b-A.dot(u))
thus it has one degree less and is quadratic in u which may help in the examination of the stationary points. I suspect that they all are hyperbolic, so that there is no asymptotically stable solution.
Using the substitution u=log(x) and thus
dotu = b-A.dot(exp(2*u))
hides the stationary points at minus infinity, thus the analytical value of this substitution may be limited. However, the positivity of x=exp(u) is built-in, which may allow for more aggressive numerical methods or provide a bit more accuracy using the same cautiousness as before.

Categories