Differential Equations - ODEINT - python

I have to solve two differential equations by ODEINT in Python, the equations:
y''(t) = (l*q)/a * (1/y(p) * [1 - z'(p)*u]
z''(t) = a * (1/y(p) * y'(p)*u
So I was told to make:
y1=y
y2=y'
z1=z
z2=z'
and
y1' = y2
y2' = y'' = (l*q)/a * (1/y(p) * [1 - z'(p)*u]
z1' = z2
z2' = z''(t) = a * (1/y(p) * y'(p)*u
and now I have to solve these 4 equations. l, q, a, u are known.
I tried something like this:
import math
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
def rownanie(y, t, l, q, a, u):
y1, y2, z1, z2 = y
dydt = [y2, ((l*q)/a)*(1/y1)*(1-z2*u), z2, (a*y2*u)/y1]
return dydt
l = 1
q = 1
a = 10
u = 0.25
y0 = 0
z0 = 0
t = np.linspace(0, 10, 101)
sol = odeint(rownanie, y0, z0, t, args=(l,q,a,u))
print(sol)
Need help with this

If you read the docs, you'll see odeint
Solves the initial value problem for stiff or non-stiff systems of first order ode-s:
dy/dt = func(y, t, ...) [or func(t, y, ...)]
where y can be a vector
This conversion is a standard mathematical way of transforming a second order ODE into a first order vector ODE.
You therefore create a new vector variable (I'll call it Y to avoid confusion), consisting of the vector Y = [y, y_prime, z, z_prime]: Your implementation of the function is correct.
Also note that in order to be solved numerically you need to specify the initial conditions of all the vector, in this case y0, z0, y'0 and z'0. As Thomas pointed out, you need to specify these values as the initial value of the vector when you call odeint.
import math
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
def rownanie(Y, t, l, q, a, u):
y1, y2, z1, z2 = Y
dydt = [y2, ((l*q)/a)*(1/y1)*(1-z2*u), z2, (a*y2*u)/y1]
return dydt
l = 1
q = 1
a = 10
u = 0.25
y0 = 0
z0 = 0
y0_prime, z0_prime = 0, 0 # you need to specify a value for these too
t = np.linspace(0, 10, 101)
sol = odeint(rownanie, [y0, y0_prime, z0, z0_prime], t, args=(l,q,a,u))
print(sol)

Related

Python / Matplotlib - How to compute/plot derivative without hard-coding it?

I am plotting a famous function and its derivative here.
The famous function is the one which arises from the Bernoulli's inequality.
I wonder if there's some way to calculate the derivative without "hard-coding it"
i.e. by just using some library and calling derivative(f) or something like that.
import numpy as np
import matplotlib.pyplot as plt
# a,b - the two ends of our interval
a = -2.2
b = +0.25
n = 10
# function f(t) = (1+t)^n - n*t - 1
def f(t):
'''
s = 1
for i in range(n):
s = s * (1 + 1 * t)
return s - n * t - 1
'''
return np.power(1 + t, n) - n * t - 1
# derivative f'(t) = n*(1+t)^(n-1) - n
def f1(t):
'''
s = 1
for i in range(n-1):
s = s * (1 + 1 * t)
return n * s - n
'''
return n * np.power(1 + t, n-1) - n
t = np.linspace(a, b, 4000)
g = f(t)
g1 = f1(t)
plt.plot(t, g, 'r') # plotting t, g separately
plt.plot(t, g1, 'g') # plotting t, g1 separately
plt.axhline(0, color='k')
plt.axvline(0, color='k')
print("=====================")
print(f(-2))
print(f(-1.5))
print(f(-1))
print(f(-0.5))
print(f(0))
print("=====================")
print(f1(-2))
print(f1(-1.5))
print(f1(-1))
print(f1(-0.5))
print(f1(0))
plt.grid()
plt.show()
Among many others, there are two following methods:
Method 1
You can use derivative from scipy that takes a function f and returns its derivative w.r.t t. So you don't have to define the derivative function f1(t) explicitly.
from scipy.misc import derivative
def f(t):
return np.power(1 + t, n) - n * t - 1
# Rest of the code
t = np.linspace(a, b, 4000)
g = f(t)
plt.plot(t, g, 'r') # plotting t, g separately
plt.plot(t, derivative(f, t, dx=0.001), 'g')
Method 2
You can use gradient function of NumPy which uses central differences and returns the same shape as the input array.
t, dt = np.linspace(a, b, 4000, retstep=True)
g1 = np.gradient(f(t), dt)
plt.plot(t, g1, 'g')
You can use sympy to calculate the derivative symbolically. If you have a nice mathematical expression, this gives a better accuracy than numerical methods.
Sympy has its own plot functions, but they can be cumbersome if you want to combine many elements. In those cases, it can be easier to use lambdify to convert them to numpy functions.
from sympy import Pow, lambdify
from sympy.abc import t, n
f = Pow(1 + t, n) - n * t - 1
f1 = f.diff(t) # result: -n + n*(t + 1)**n/(t + 1)
f_np = lambdify(t, f.subs(n, 10))
f1_np = lambdify(t, f1.subs(n, 10))
import numpy as np
from matplotlib import pyplot as plt
a = -2.2
b = +0.25
x = np.linspace(a, b, 1000)
plt.plot(x, f_np(x), 'r')
plt.plot(x, f1_np(x), 'g')
plt.axhline(0, color='k')
plt.axvline(0, color='k')
plt.show()
PS: Purely staying within sympy, plotting can happen as follows:
from sympy import Pow, plot
from sympy.abc import t, n
a = -2.2
b = +0.25
f = Pow(1 + t, n) - n * t - 1
f1 = f.diff(t)
p1 = plot(f.subs(n, 10), (t, a, b), line_color='r', show=False)
p2 = plot(f1.subs(n, 10), (t, a, b), line_color='g', show=False)
p1.append(p2[0])
p1.show()
Automatic differentiation is a great tool to do this. Check out https://github.com/HIPS/autograd.
import autograd.numpy as np
import matplotlib.pyplot as plt
from autograd import elementwise_grad as egrad
# a,b - the two ends of our interval
a = -2.2
b = +0.25
n = 10
# function f(t) = (1+t)^n - n*t - 1
def f(t):
'''
s = 1
for i in range(n):
s = s * (1 + 1 * t)
return s - n * t - 1
'''
return np.power(1 + t, n) - n * t - 1
# derivative f'(t) = n*(1+t)^(n-1) - n
f1 = egrad(f)
t = np.linspace(a, b, 4000)
g = f(t)
g1 = f1(t)
plt.plot(t, g, 'r') # plotting t, g separately
plt.plot(t, g1, 'g') # plotting t, g1 separately
plt.axhline(0, color='k')
plt.axvline(0, color='k')
print("=====================")
print(f(-2))
print(f(-1.5))
print(f(-1))
print(f(-0.5))
print(f(0))
print("=====================")
print(f1(-2.0))
print(f1(-1.5))
print(f1(-1.0))
print(f1(-0.5))
print(f1(0.0))
Note that I had to change the arguments passed to f1 to be floats, but otherwise it generates the same plot. This is in general how all of the "deep learning" frameworks such as tensorflow, Torch, etc. compute gradients.
This avoids having to analytically compute the derivative yourself and also avoids issues with numerical differentiation.

Solve Non linear ODE of predefined functions with scipy.odeint

I want to solve a non linear ordinary differential equation of the form
Theta2 = (C + j(Theta2))**-1 * (f(t) – g(Theta1) -h(Theta0))
Where f(), g(), h(), and j() are functions already defined that take Theta2, Theta1, Theta0 or t as an input. Theta2 and Theta1 are the second and first derivative of Theta0 with time t.
I have been solving the equation without the j(Theta2) term using the SciPy.odeint function using the following code:
from scipy.integrate import odeint
def ODE():
def g(Theta, t):
Theta0 = Theta[0]
Theta1 = Theta[1]
Theta2 = (1/C)*( f(t) - g(Theta1) - h(Theta0))
return Theta1, Theta2
init = 0, 0 # Initial conditions on theta0 and theta1 (velocity) at t=0
sol=odeint(g, init, t)
A = sol[:,1]
B = sol[:,0]
return(A, B)
The equation could be re-written as:
F(t, theta, theta')
theta'' = -------------------
a + b*theta''
where a and b are constants, and F corresponds to (f(t) – g(Theta1) -h(Theta0)).
It is a second order polynomial function of theta'', with 2 solutions (considering b!=0 and a^2 + 4*b*F>0) :
theta'' = -( sqrt(a^2 + 4*b*F) +/- a )/(2*b)
This new equation is of the form y' = f(t, y) which could be solved using regular ODE solver.
Here is an example using solve_ivp which is the replacement for odeint:
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
a = 20
b = 1
def f(t, y, dydt):
return t + y**2
def ode_function_plus(t, Y):
y = Y[0]
dydt = Y[1]
d2y_dt2 = -(np.sqrt(a**2 + 4*b*f(t, y, dydt)) + a )/(2*b)
return [dydt, d2y_dt2]
def ode_function_minus(t, Y):
y = Y[0]
dydt = Y[1]
d2y_dt2 = -(np.sqrt(a**2 + 4*b*f(t, y, dydt)) - a )/(2*b)
return [dydt, d2y_dt2]
# Solve
t_span = [0, 4]
Y0 = [10, 1]
sol_plus = solve_ivp(ode_function_plus, t_span, Y0)
sol_minus = solve_ivp(ode_function_minus, t_span, Y0)
print(sol_plus.message)
# Graph
plt.plot(sol_plus.t, sol_plus.y[0, :], label='solution +a');
plt.plot(sol_minus.t, sol_minus.y[0, :], label='solution -a');
plt.xlabel('time'); plt.ylabel('y'); plt.legend();

Using solve_ivp instead of odeint to solve initial problem value

Currently, I solve the following ODE system of equations using odeint
dx/dt = (-x + u)/2.0
dy/dt = (-y + x)/5.0
initial conditions: x = 0, y = 0
However, I would like to use solve_ivp which seems to be the recommended option for this type of problems, but honestly I don't know how to adapt the code...
Here is the code I'm using with odeint:
import numpy as np
from scipy.integrate import odeint, solve_ivp
import matplotlib.pyplot as plt
def model(z, t, u):
x = z[0]
y = z[1]
dxdt = (-x + u)/2.0
dydt = (-y + x)/5.0
dzdt = [dxdt, dydt]
return dzdt
def main():
# initial condition
z0 = [0, 0]
# number of time points
n = 401
# time points
t = np.linspace(0, 40, n)
# step input
u = np.zeros(n)
# change to 2.0 at time = 5.0
u[51:] = 2.0
# store solution
x = np.empty_like(t)
y = np.empty_like(t)
# record initial conditions
x[0] = z0[0]
y[0] = z0[1]
# solve ODE
for i in range(1, n):
# span for next time step
tspan = [t[i-1], t[i]]
# solve for next step
z = odeint(model, z0, tspan, args=(u[i],))
# store solution for plotting
x[i] = z[1][0]
y[i] = z[1][1]
# next initial condition
z0 = z[1]
# plot results
plt.plot(t,u,'g:',label='u(t)')
plt.plot(t,x,'b-',label='x(t)')
plt.plot(t,y,'r--',label='y(t)')
plt.ylabel('values')
plt.xlabel('time')
plt.legend(loc='best')
plt.show()
main()
It's important that solve_ivp expects f(t, z) as right-hand side of the ODE. If you don't want to change your ode function and also want to pass your parameter u, I recommend to define a wrapper function:
def model(z, t, u):
x = z[0]
y = z[1]
dxdt = (-x + u)/2.0
dydt = (-y + x)/5.0
dzdt = [dxdt, dydt]
return dzdt
def odefun(t, z):
if t < 5:
return model(z, t, 0)
else:
return model(z, t, 2)
Now it's easy to call solve_ivp:
def main():
# initial condition
z0 = [0, 0]
# number of time points
n = 401
# time points
t = np.linspace(0, 40, n)
# step input
u = np.zeros(n)
# change to 2.0 at time = 5.0
u[51:] = 2.0
res = solve_ivp(fun=odefun, t_span=[0, 40], y0=z0, t_eval=t)
x = res.y[0, :]
y = res.y[1, :]
# plot results
plt.plot(t,u,'g:',label='u(t)')
plt.plot(t,x,'b-',label='x(t)')
plt.plot(t,y,'r--',label='y(t)')
plt.ylabel('values')
plt.xlabel('time')
plt.legend(loc='best')
plt.show()
main()
Note that without passing t_eval=t, the solver will automatically choose the time points inside tspan at which the solution will be stored.

Solving ODE in complex domain with Python (or Matlab)

As a test for a more complicated system, I want to solve a differential equation dw/dz = w where the function w = w(z) is complex valued and z = x+iy as usual. The boundary conditions are w = i when z = i. The solution is of course complex and defined on the argand plane. I was hoping to solve this with some standard ODE solvers in python. My method is to first define a grid in the argand plane (lines of constant x and y) and then loop through each grid line and call an ODE solver on each iteration. In the below code I am attempting to integrate my differential equation between 1j and 2j, but the resulting vector of w is just 1j! Can anyone advise me what to do? Thanks
from scipy.integrate import ode
import numpy as np
from matplotlib.pylab import *
def myodeint(func, w0, z):
w0 = np.array(w0, complex)
func2 = lambda z, w: func(w, z) # odeint has these the other way :/
z0 = z[0]
solver = ode(func2).set_integrator('zvode').set_initial_value(w0, z0)
w = [solver.integrate(zp) for zp in z[1:]]
w.insert(0, w0)
return np.array(w)
def func2(w, z, alpha):
return alpha*w
if __name__ == '__main__':
# Set grid size in z plane
x_max = 3
x_min = 0
y_max = 3
y_min = 0
# Set grid resolution
dx = 0.1
dy = 0.1
# Number of nodes
x_nodes = int(np.floor((x_max-x_min)/dx)+1)
y_nodes = int(np.floor((y_max-y_min)/dy)+1)
# Create array to store value of w(z) at each node
ww = np.zeros((y_nodes,x_nodes), complex)
# Set boundary condition: w = w0 at x = x0, y = y0
x0 = 0
y0 = 1
i0 = (x0-x_min)/dx
j0 = (y_max-y0)/dy
w0 = 1j
ww[j0,i0] = w0
z0 = 1j
alpha = 1
z = np.linspace(z0, z0+1j, 200)
w = myodeint(lambda w, z: func2(w, z, alpha), [w0, 0, 0], z)

Triple integration using using Python

I've been trying to solve an equation using scipy.integrate.tplquadrature but don't fully understand the notation and so don't really know how to solve the following equation. Any help would be much appreciated.
Thanks,
In your example it gave a zero integral result. I used a high value 1.e22 for inf:
from scipy import exp, pi
inf = 1.e22
from scipy.integrate import tplquad
func = lambda x,y,z: x**2 * exp(-x**2) * exp(-0.5*y*z/x)
x1,x2 = 0, pi
y1,y2 = lambda x: 0, lambda x: inf
z1,z2 = lambda x,y: 0, lambda x,y: inf
print tplquad( func, x1, x2, y1, y2, z1, z2 )
#(0.0, 0.0)
This is an example to calculate the volume of a sphere:
import scipy
from scipy.integrate import quad, dblquad, tplquad
from numpy import *
# limits for radius
r1 = 0.
r2 = 1.
# limits for theta
t1 = 0
t2 = 2*pi
# limits for phi
p1 = 0
p2 = pi
def diff_volume(p,t,r):
return r**2*sin(p)
volume = tplquad(diff_volume, r1, r2, lambda r: t1, lambda r: t2,
lambda r,t: p1, lambda r,t: p2)[0]

Categories

Resources