Solving a System of Ode's using scipy.integrate.ode - python

I am trying to solve a system of Odes of the form Du/Dt = F(u) in python, and I suspect I may have made a fairly dumb mistake somewhere.
Technically F(u) is actually the second derivative of u with respect to another variable y, but in practice we can consider it to be a system and some function.
#Settings#
minx = -20
h = float(1)
w = float(10)
U0 = float(10)
Nt = 10
Ny = 10
tmax = 10
v=float(1)
#Arrays#
y = np.linspace(0,h,Ny)
t = np.linspace(0,tmax,Nt)
#Variables from arrays#
dt = t[1]-t[0]
p = [0]*(Nt)
delta = y[1] - y[0]
def zo(y):
return math.cos(y/(2*math.pi))
z0 = [zo(i) for i in y]
def df(t,v1):
output = np.zeros(len(y))
it = 1
output[0] = math.cos(w*t)
output[len(y)-1] = math.cos(w*t)
while it < len(y)-1:
output[it] = ( v1[it - 1] + v1[it + 1] - 2 * v1[it] ) * ( v / ( ( delta )**2 ))
it += 1
return output
r = ode(df).set_integrator('zvode', method='bdf',order =15)
r.set_initial_value(z0, 0)
it=0
while r.successful() and r.t < tmax:
p[it] = r.integrate(r.t+dt)
it+=1
print(z0-p[0])
print(p[1])
Now the problem is twofold :
-First of all, the initial "condition" ie p[0] seems to be off.
(That may be just because of the way the ode function works though, so I don't know if that is normal)
-Second, p[1] and all p's after that are just 0.
So for some reason the ode function fails immediately... (you can check that by changing the values to 1 when initializing p)
Except that I know that this method should work.
This is the "equivalent" to ode45 in matlab after all and that definitely works.

Why did you choose a complex solver with an implicit backward differentiation formula of a rather high order if you wanted to use Dormand-Price rk45 resp. dopri5?
Please also correct the loop indentation in df. Why not a for loop over range(1, len(y)-1)?
As it currently stands p[0] contains the solution point after the first step, at t=1*dt. You would have to explicitly assign p[0]=z0 and start it=1 to get the full solution path in p. Check the length of p, it could be that you need Nt+1.

Related

python code to solve the following initial value problem ordinary differential equation using Euler method over the interval (0 10)

i have this question
Write a python code to solve the following initial value
problem ordinary differential equation using Euler method over the
interval (0 10) with 10 time steps.
A) y'= -y -y^2 ; y(0)=1 If that exact solution was y(t) = 1/(-1+2e^t)
What is the absolute error at y(10).
now i have write this code
def pow_t(x,b):
t=1;
while (b):
t*=x
b=b-1
return t
def absolute():
y=[x for x in range(1,11)]
h=0.0001
for i in range(1,10) :
y[i]=y[i-1]+(h*(-1*y[i-1]-pow_t(y[i-1],2)))
print("y",i,"=",y[i])
exact = 0.0000227
approx = y[9]
absolute = exact - approx
print("abbsolute erroe = exact - approx ")
print("abbsolute erroe = ",absolute)
print(absolute())
the expected output is this
and this is the actual result that I get
i need to set the first index of y list to 1 then fill the rest of list by the for loop, how can i code this?
Your output is essentially correct, but shifted by 1 (so e.g. your y10 is what the intended output calls y9) and not rounded to 4 decimal places.
There are 10 updates to y but you want to print 11 values. The way to do that is to either print the first y before the loop or print the final y after the loop. The following code shows the second approach:
y = 1
t = 0
h = 0.0001
iterates = [1]
for i in range(10):
print(f'y{i} = {y:.4f}')
y = y+h*(-y-y**2)
iterates.append(y)
t += h
print(f'y10 = {y:.4f}')
Note that this code simply using a scalar variable y as the variable in Euler's method (rather than an entry in an array. Ultimately a matter of taste, but the code seems cleaner that way.
The output is:
y0 = 1.0000
y1 = 0.9998
y2 = 0.9996
y3 = 0.9994
y4 = 0.9992
y5 = 0.9990
y6 = 0.9988
y7 = 0.9986
y8 = 0.9984
y9 = 0.9982
y10 = 0.9980
which matches the expected output.
It helps if you can know the closed form solution before you begin.
Equation: y' + y + y^2 = 0
Initial condition: y(0) = 1
This is a Bernoulli equation.
The closed form solution is:
y(t) = -e^C1 / (e^C1 - e^t)
Applying the initial condition to solve for the constant C1:
y(0) = -e^C1 / (e^C1 - 1) = 1
Solving for C1:
e^C1 = 1/2
C1 = ln(1/2)
Substituting gives the final solution:
y(t) = 1/(2*(e^t - 1/2))
Plot the closed form solution and use that to assess your numerical integration solution.
You can get the exact solution at y(10) to compare to your numerical result:
y(0) = 1
y(1) = 0.225399674
y(2) = 0.072578883
y(3) = 0.025529042
y(4) = 0.009242460
y(5) = 0.003380362
y(6) = 0.001240914
y(7) = 0.000456149
y(8) = 0.000671038
y(9) = 0.000061709
y(10) = 1/(2*(e^(10)) - 1/2)) = 0.000022700
I love Kotlin. I think matches Python's simplicity and succinctness but gives me more b/c of Java and the JVM. Here's what a Kotlin implementation might look like:
fun main() {
var y = 1.0
var t = 0.0
val dt = 0.005
val tmax = 10.0
do {
println("y(${t.format(5)}) = ${y.format(10)}")
y += dt*(-y-y*y)
t += dt
} while (t < tmax)
}
fun Double.format(digits: Int) = "%.${digits}f".format(this)
You can get better and better agreement with the closed form solution by taking smaller steps. Play around with the dt value to see what I mean.
You're using an explicit Euler integration scheme, which can suffer from stability limits on step sizes. You can choose more accurate and stable integration schemes like 5th order Runge Kutta.

Unexpected result when solving a system of coupled ode's implicitely

Aiming to solve this system of coupled differential equations:
$ frac{dx}{dt} = -y $
$\frac{dy}{dt} = x $
following the below implicit evolution scheme:
$$ y(t_{n+1}) = y(t_{n}) + \frac{\Delta t}{2}(x_{old}(t_{n+1}) + x(t_{n})) $$
$$ x(t_{n+1}) = x(t_{n}) - \frac{\Delta t}{2}(y_{old}(t_{n+1}) + y(t_{n})) $$
My code is as follows
# coupled ODE's to be solved
def f(x,y):
return -y
def g(x,y):
return x
#implicit evolution scheme
def Imp(f,g,x0,y0, tend,N):
t = np.linspace(0.0, tend, N+1)
dt = 0.1
x1 = np.zeros((N+1, ))
y2 = np.zeros((N+1, ))
xold = np.zeros((N+1, ))
yold = np.zeros((N+1, ))
xxold = np.zeros((N+1, ))
yyold = np.zeros((N+1, ))
x1[0] = x0
y2[0] = y0
for n in range(0,N):
xold = f(t[n+1], x1[n])
xxold = f(t[n+1], x1[n+1] + xold)
yold = g(t[n], y2[n])
yyold = g(t[n], y2[n+1]+yold)
y2[n+1] = y2[n] + (x1[n]+xxold)*0.5*dt
x1[n+1] = x1[n] - (y2[n]+ yyold)*0.5*dt
return t,x1,y2
t, y1,y2 = Imp(f,g,np.sqrt(2),0.0, 100, 1000)
plt.plot(y1,y2)
I was expecting the output (phase plot) to be a full circle as reported in the literature though I got a spiral which was not expected (I would have embedded the picture though my low reputation did not allowed it, please run to see the output).
Does anyone know how could I fix my Imp routine ? thanks
Please study again how the implicit Euler or trapezoidal method works, for scalar equations and for systems. Then think hard about the interfaces of your function, where there is an x, y and why there is no t in the declaration of f and g.
However, from what you describe you are not implementing an implicit method but the explicit 2nd order Heun method. In an implicit method you would solve the implicit equation until sufficient "numerical" convergence is reached.
The explicit Heun loop could look like
for n in range(0,N):
xold = x[n] + f(x[n],y[n])*dt
yold = y[n] + g(x[n],y[n])*dt
xnew = x[n] + f(xold, yold)*dt
ynew = x[n] + g(xold, yold)*dt
x[n+1] = 0.5*(xold+xnew)
y[n+1] = 0.5*(yold+ynew)
But as said, this is an explicit method with a fixed number of explicit steps, not an implicit method using an implicit equation solving strategy.

Using python built-in functions for coupled ODEs

THIS PART IS JUST BACKGROUND IF YOU NEED IT
I am developing a numerical solver for the Second-Order Kuramoto Model. The functions I use to find the derivatives of theta and omega are given below.
# n-dimensional change in omega
def d_theta(omega):
return omega
# n-dimensional change in omega
def d_omega(K,A,P,alpha,mask,n):
def layer1(theta,omega):
T = theta[:,None] - theta
A[mask] = K[mask] * np.sin(T[mask])
return - alpha*omega + P - A.sum(1)
return layer1
These equations return vectors.
QUESTION 1
I know how to use odeint for two dimensions, (y,t). for my research I want to use a built-in Python function that works for higher dimensions.
QUESTION 2
I do not necessarily want to stop after a predetermined amount of time. I have other stopping conditions in the code below that will indicate whether the system of equations converges to the steady state. How do I incorporate these into a built-in Python solver?
WHAT I CURRENTLY HAVE
This is the code I am currently using to solve the system. I just implemented RK4 with constant time stepping in a loop.
# This function randomly samples initial values in the domain and returns whether the solution converged
# Inputs:
# f change in theta (d_theta)
# g change in omega (d_omega)
# tol when step size is lower than tolerance, the solution is said to converge
# h size of the time step
# max_iter maximum number of steps Runge-Kutta will perform before giving up
# max_laps maximum number of laps the solution can do before giving up
# fixed_t vector of fixed points of theta
# fixed_o vector of fixed points of omega
# n number of dimensions
# theta initial theta vector
# omega initial omega vector
# Outputs:
# converges true if it nodes restabilizes, false otherwise
def kuramoto_rk4_wss(f,g,tol_ss,tol_step,h,max_iter,max_laps,fixed_o,fixed_t,n):
def layer1(theta,omega):
lap = np.zeros(n, dtype = int)
converges = False
i = 0
tau = 2 * np.pi
while(i < max_iter): # perform RK4 with constant time step
p_omega = omega
p_theta = theta
T1 = h*f(omega)
O1 = h*g(theta,omega)
T2 = h*f(omega + O1/2)
O2 = h*g(theta + T1/2,omega + O1/2)
T3 = h*f(omega + O2/2)
O3 = h*g(theta + T2/2,omega + O2/2)
T4 = h*f(omega + O3)
O4 = h*g(theta + T3,omega + O3)
theta = theta + (T1 + 2*T2 + 2*T3 + T4)/6 # take theta time step
mask2 = np.array(np.where(np.logical_or(theta > tau, theta < 0))) # find which nodes left [0, 2pi]
lap[mask2] = lap[mask2] + 1 # increment the mask
theta[mask2] = np.mod(theta[mask2], tau) # take the modulus
omega = omega + (O1 + 2*O2 + 2*O3 + O4)/6
if(max_laps in lap): # if any generator rotates this many times it probably won't converge
break
elif(np.any(omega > 12)): # if any of the generators is rotating this fast, it probably won't converge
break
elif(np.linalg.norm(omega) < tol_ss and # assert the nodes are sufficiently close to the equilibrium
np.linalg.norm(omega - p_omega) < tol_step and # assert change in omega is small
np.linalg.norm(theta - p_theta) < tol_step): # assert change in theta is small
converges = True
break
i = i + 1
return converges
return layer1
Thanks for your help!
You can wrap your existing functions into a function accepted by odeint (option tfirst=True) and solve_ivp as
def odesys(t,u):
theta,omega = u[:n],u[n:]; # or = u.reshape(2,-1);
return [ *f(omega), *g(theta,omega) ]; # or np.concatenate([f(omega), g(theta,omega)])
u0 = [*theta0, *omega0]
t = linspan(t0, tf, timesteps+1);
u = odeint(odesys, u0, t, tfirst=True);
#or
res = solve_ivp(odesys, [t0,tf], u0, t_eval=t)
The scipy methods pass numpy arrays and convert the return value into same, so that you do not have to care in the ODE function. The variant in comments is using explicit numpy functions.
While solve_ivp does have event handling, using it for a systematic collection of events is rather cumbersome. It would be easier to advance some fixed step, do the normalization and termination detection, and then repeat this.
If you want to later increase efficiency somewhat, use directly the stepper classes behind solve_ivp.

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 to create an array that can be accessed according to its indices in Numpy?

I am trying to solve the following problem via a Finite Difference Approximation in Python using NumPy:
$u_t = k \, u_{xx}$, on $0 < x < L$ and $t > 0$;
$u(0,t) = u(L,t) = 0$;
$u(x,0) = f(x)$.
I take $u(x,0) = f(x) = x^2$ for my problem.
Programming is not my forte so I need help with the implementation of my code. Here is my code (I'm sorry it is a bit messy, but not too bad I hope):
## This program is to implement a Finite Difference method approximation
## to solve the Heat Equation, u_t = k * u_xx,
## in 1D w/out sources & on a finite interval 0 < x < L. The PDE
## is subject to B.C: u(0,t) = u(L,t) = 0,
## and the I.C: u(x,0) = f(x).
import numpy as np
import matplotlib.pyplot as plt
# definition of initial condition function
def f(x):
return x^2
# parameters
L = 1
T = 10
N = 10
M = 100
s = 0.25
# uniform mesh
x_init = 0
x_end = L
dx = float(x_end - x_init) / N
#x = np.zeros(N+1)
x = np.arange(x_init, x_end, dx)
x[0] = x_init
# time discretization
t_init = 0
t_end = T
dt = float(t_end - t_init) / M
#t = np.zeros(M+1)
t = np.arange(t_init, t_end, dt)
t[0] = t_init
# Boundary Conditions
for m in xrange(0, M):
t[m] = m * dt
# Initial Conditions
for j in xrange(0, N):
x[j] = j * dx
# definition of solution to u_t = k * u_xx
u = np.zeros((N+1, M+1)) # NxM array to store values of the solution
# finite difference scheme
for j in xrange(0, N-1):
u[j][0] = x**2 #initial condition
for m in xrange(0, M):
for j in xrange(1, N-1):
if j == 1:
u[j-1][m] = 0 # Boundary condition
else:
u[j][m+1] = u[j][m] + s * ( u[j+1][m] - #FDM scheme
2 * u[j][m] + u[j-1][m] )
else:
if j == N-1:
u[j+1][m] = 0 # Boundary Condition
print u, t, x
#plt.plot(t, u)
#plt.show()
So the first issue I am having is I am trying to create an array/matrix to store values for the solution. I wanted it to be an NxM matrix, but in my code I made the matrix (N+1)x(M+1) because I kept getting an error that the index was going out of bounds. Anyways how can I make such a matrix using numpy.array so as not to needlessly take up memory by creating a (N+1)x(M+1) matrix filled with zeros?
Second, how can I "access" such an array? The real solution u(x,t) is approximated by u(x[j], t[m]) were j is the jth spatial value, and m is the mth time value. The finite difference scheme is given by:
u(x[j],t[m+1]) = u(x[j],t[m]) + s * ( u(x[j+1],t[m]) - 2 * u(x[j],t[m]) + u(x[j-1],t[m]) )
(See here for the formulation)
I want to be able to implement the Initial Condition u(x[j],t[0]) = x**2 for all values of j = 0,...,N-1. I also need to implement Boundary Conditions u(x[0],t[m]) = 0 = u(x[N],t[m]) for all values of t = 0,...,M. Is the nested loop I created the best way to do this? Originally I tried implementing the I.C. and B.C. under two different for loops which I used to calculate values of the matrices x and t (in my code I still have comments placed where I tried to do this)
I think I am just not using the right notation but I cannot find anywhere in the documentation for NumPy how to "call" such an array so at to iterate through each value in the proposed scheme. Can anyone shed some light on what I am doing wrong?
Any help is very greatly appreciated. This is not homework but rather to understand how to program FDM for Heat Equation because later I will use similar methods to solve the Black-Scholes PDE.
EDIT: So when I run my code on line 60 (the last "else" that I use) I get an error that says invalid syntax, and on line 51 (u[j][0] = x**2 #initial condition) I get an error that reads "setting an array element with a sequence." What does that mean?

Categories