Python solve ODE - python

How to solve a ODE,I have tried to use scipy.integrate.odeint, but what should I do if I don't know the initial value,
That's what I've defined:
alpha=0.204
beta=0.196
b=5.853
c=241.38
def Left_equation(x):
return alpha * x
def Right_equation(x):
return (b * (x ** 2)) + c
def diff_equation(x, t):
return (t ** beta) * (Right_equation(x) - Left_equation(x))
I also want to get the graph of the result,I don't know if I need to get the analytic solution first.

If the initial condition is not known, then the integration would need to be done symbolically. The Python package sympy can be used for symbolic integration of ordinary differential equations, as follows (using the function sympy.dsolve):
"""How to integrate symbolically an ordinary differential equation."""
import sympy
def main():
alpha = 0.204
beta = 0.196
b = 5.853
c = 241.38
t = sympy.symbols('t')
x = sympy.Function('x')(t)
dxdt = sympy.Derivative(x, t)
e = (t**beta) * ((b * (x**2)) + c - alpha * x)
x_eq = sympy.dsolve(dxdt - e)
if __name__ == '__main__':
main()
For this example, the function sympy.solvers.ode.ode.dsolve raises the exception PolynomialDivisionFailed. But the above code shows how to do symbolic integration.
An alternative is to numerically solve the differential equation (for specific initial condition), and compute solutions for a range of initial conditions, to explore how the solution depends on the initial condition. Using the function scipy.integrate.odepack.odeint of the Python package scipy, and the Python packages matplotlib and numpy, this can be done as follows:
"""How to integrate numerically an ordinary differential equation."""
from matplotlib import pyplot as plt
import numpy as np
import scipy.integrate
def main():
x0 = np.linspace(0, 0.2, 10) # multiple alternative initial conditions
t = np.linspace(0, 0.01, 100) # where to solve
x = scipy.integrate.odeint(deriv, x0, t)
# plot results
plt.plot(t, x)
plt.xlabel('t')
plt.ylabel('x')
plt.grid(True)
plt.show()
def deriv(x, t):
alpha = 0.204
beta = 0.196
b = 5.853
c = 241.38
return (t ** beta) * ((b * (x**2)) + c - alpha * x)
if __name__ == '__main__':
main()
As noted though in the documentation of the function scipy.integrate.odeint:
For new code, use scipy.integrate.solve_ivp to solve a differential equation.
The function scipy.integrate.solve_ivp can be used as follows:
"""How to integrate numerically an ordinary differential equation."""
from matplotlib import pyplot as plt
import numpy as np
import scipy.integrate
def main():
x0 = np.linspace(0, 0.2, 10) # multiple alternative initial conditions
t = (0.0, 0.01) # where to solve: (start, end)
res = scipy.integrate.solve_ivp(deriv, t, x0) # different order of
# arguments than for the function `scipy.integrate.odeint`
# plot results
plt.plot(res.t, res.y.T)
plt.xlabel('t')
plt.ylabel('x')
plt.grid(True)
plt.show()
def deriv(t, x): # not x, t as for the function `scipy.integrate.odeint`
alpha = 0.204
beta = 0.196
b = 5.853
c = 241.38
return (t ** beta) * ((b * (x**2)) + c - alpha * x)
if __name__ == '__main__':
main()
Please note the differences in arguments between the function scipy.integrate.odeint and the function scipy.integrate.solve_ivp.
The above code produces the following plot:

Related

Solving an Involved Set of Coupled Differential Equations

I am trying to solve a set of complicated differential equations in Python. These equations contain five functions (defined in the function 'ODEs' in the code below) that are functions of a variable n (greek name is eta--- I used n and eta interchangeably as variable names). These coupled differential equations contain a function (called a) which is a function of a parameter t. n (or eta) is also a function of t and so my first goal was to express, again numerically, the function a as a function of n (eta). Hence I had to solve a less involved pair of differential equations, which I defined in the function 'coupleODE'. I got a plot of a(t) and n(t) and used interpolation to get a model relating function a to function n. This function is a numerical estimation for a(n). I called this interpolation model 'f_quad' --- f, means function, and quad represents quadratic interpolation.
Now, the original five differential equations actually contain a'(n)/a(n), where ' is derivative with respect to n. I numerically found an interpolator for this as well and called it 'deriv_quad'.
Now in 'ODEs' I am modelling the five differential equations, and as you can see in the code, the function body contains segments that use the interpolators 'f_quad' and 'deriv_quad'; these have argument n, which represents the a(n) and a'(n) respectively. I then use odeint python function to numerically solve these differential equations, but I get the error message:
'A value in x_new is above the interpolation range.' My computer says that the error occurred in the line 'f = odeint(ODEs, initials, n_new, atol=1.0e-8, rtol=1.0e-6)'.
I am not sure why this happened; I used the same eta values that I used when finding the interpolators 'f_quad' and 'deriv_quad'. Can anyone help me get rid of this error?
Here is the code:
import numpy as np
from scipy.misc import derivative
from scipy.integrate import odeint
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
import math
def coupledODE(x,t):
#define important constants
m = 0.315
r = 0.0000926
d = 0.685
H_0 = 67.4
a = x[0]
eta = x[1]
dndt = a**(-1)
dadt = H_0 * (m*a**(-1) + r*a**(-2) + d*a**2)**(1/2)
return [dadt, dndt]
#initial condtions
x0 = [1e-7, 1e-8]
t = np.linspace(0,0.01778301154,7500)
x = odeint(coupledODE, x0, t, atol=1.0e-8, rtol=1.0e-6) #vector of the functions a(t), n(t)
a = x[:,0]
n = x[:,1] #Eta; n is the greek letter eta
plt.semilogx(t,a)
plt.xlabel('time')
plt.ylabel('a(t)')
plt.show()
plt.semilogx(t,n)
plt.xlabel('time')
plt.ylabel('Eta')
plt.show()
plt.plot(n,a)
plt.xlabel('Eta')
plt.ylabel('a(t)')
plt.show()
##############################################################################
# Calculate the Derivative a' (i.e. da/d(eta) Numerically
##############################################################################
derivative_values = []
for i in range(7499):
numerator = x[i+1,0] - x[i,0]
denominator = x[i+1,1] - x[i,1]
ratio = numerator / denominator
derivative_values.append(ratio)
x_axis = n
x_axis = np.delete(x_axis, -1)
plt.plot(x_axis,derivative_values)
plt.xlabel('Eta')
plt.ylabel('Derivative of a')
plt.show()
##############################################################################
#Interpolation
##############################################################################
#Using quadratic interpolation
f_quad = interp1d(n, a, kind = 'quadratic')
deriv_quad = interp1d(x_axis, derivative_values, kind = 'quadratic')
n_new = np.linspace(1.0e-8, 0.0504473, num = 20000, endpoint = True)
plt.plot(n_new, f_quad(n_new))
plt.xlabel('Eta')
plt.ylabel('a')
plt.show()
plt.plot(n_new, deriv_quad(n_new))
plt.xlabel('Eta')
plt.ylabel('Derivative of a')
plt.show()
#print(x[0,1])
#print(eta_new[0])
#print(deriv_quad(1e-8))
##############################################################################
# The Main Coupled Equations
##############################################################################
def ODEs(x,n):
fourPiG_3 = 1
CDM_0 = 1
Rad_0 = 1
k = 0.005
Phi = x[0]
Theta1 = x[1]
iSpeed = x[2]
DeltaC = x[3]
Theta0 = x[4]
dPhi_dn = fourPiG_3 *((CDM_0 * DeltaC)/ deriv_quad(n) + (4*Rad_0)/(f_quad(n)*deriv_quad(n))) -
(k**2*Phi*f_quad(n))/(deriv_quad(n)) - (deriv_quad(n) * Phi)/(f_quad(n))
dTheta1_dn = (k/3)*(Theta0 - Phi)
diSpeed_dn = -k*Phi - (deriv_quad(n)/f_quad(n))*iSpeed
dDeltaC_dn = -k*iSpeed - 3*dPhi_dn
dTheta0_dn = -k*Theta1 - dPhi_dn
return [dPhi_dn, dTheta1_dn, diSpeed_dn, dDeltaC_dn, dTheta0_dn]
hub_0 = deriv_quad(1e-8)/f_quad(1e-8)
#Now the initial conditions
Phi_k_0 = 1.0e-5
Theta1_k_0 = -1*Phi_k_0/hub_0 #Ask about the k
iSpeed_k_0 = 3*Theta1_k_0
DeltaC_k_0 = 1.5 * Phi_k_0
Theta0_k_0 = 0.5 * Phi_k_0
initials = [Phi_k_0, Theta1_k_0, iSpeed_k_0, DeltaC_k_0, Theta0_k_0]
####Error Happens Here ####
f = odeint(ODEs, initials, n_new, atol=1.0e-8, rtol=1.0e-6)
Phi = f[:,0]
Theta1 = f[:,1]
iSpeed = f[:,2]
DeltaC = f[:,3]
Theta0 = f[:,4]

Comparing convolutions in Mathematica and Python

I'm comparing the results of convolution in Python (using sympy's symbolic variables) and Mathematica with its Convolve function.
In Python, my MWE is
from numpy import linspace, pi
from numpy.random import randn
from scipy.signal import fftconvolve
import matplotlib.pyplot as plt
from sympy import symbols
from sympy.utilities.lambdify import lambdify
a = 0.43
b = 0.41
c = 0.65
d = 0.71
x = symbols('x')
f = 2*b / ((x-a)**2 + b**2)
g = 2*d / ((x-c)**2 + d**2)
fog = fftconvolve(f,g,mode='same')
fog_fun = lambdify(x,fog,'numpy') # returns a numpy-ready function
x = linspace(-20,20,int(1e3))
dx = x[1]-x[0]
fogS = fog_fun(x)
fogA = 4*pi*(b+d)/((x-a-c)**2+(b+d)**2) # correct analytic solution
plt.figure()
plt.plot(x,fogA,lw=2,label='analytic')
plt.plot(x,fogS,lw=2,label='sympy')
plt.grid()
plt.legend(loc='best')
plt.show()
which calculates a convolution using symbolic variable x. The resulting function (before lambdifying) is
fog = 1.1644/(((x - 0.65)**2 + 0.5041)*((x - 0.43)**2 + 0.1681))
There is no agreement between analytic (fogA, Mathematica) and sympy (fogS, Python):
My Mathematica code is:
a = 0.43; b = 0.41; c = 0.65; d = 0.71;
fogA = FullSimplify[Convolve[2*b/((t-a)^2+b^2),2*d/((t-c)^2+d^2), t, x]];
fogS = 1.1644/(((x - 0.65)^2 + 0.5041)*((x - 0.43)^2 + 0.1681));
where
fogA = (17.683+x*(-30.4006+14.0743*x))/(3.04149+x*(-7.9428+x*(8.3428+x*(-4.32+1.*x))))
and graphs for fogS and fogA are the same as for Python.
Why is there such a large disagreement between the analytic and sympy solution? I suspect the problem lies with sympy. Another Pythonic method is to convolve two arrays which seems to agree with the analytic solution.
f = 2*b / ((x-a)**2 + b**2)
g = 2*d / ((x-c)**2 + d**2)
fogN = fftconvolve(f,g,mode='same')*dx # numeric
(Note: this is a MWE. The actual f and g I want to convolve are much more complicated than the Lorentzians defined in this post.)
I do not think this is a reasonable way of using scipy + sympy.
I am actually quite surprised that you get a result from lambdify at all.
What you should be doing, instead of using scipy.signal.fftconvolve(), is to use a symbolic definition of the convolution, e.g.:
from sympy import oo, Symbol, integrate
def convolve(f, g, t, lower=-oo, upper=oo):
tau = Symbol('__very_unlikely_name__', real=True)
return integrate(
f.subs(t, tau) * g.subs(t, t - tau), (tau, lower, upper))
adapted from here.

Improving accuracy in scipy.optimize.fsolve with equations involving integration

I'm trying to solve an integral equation using the following code (irrelevant parts removed):
def _pdf(self, a, b, c, t):
pdf = some_pdf(a,b,c,t)
return pdf
def _result(self, a, b, c, flag):
return fsolve(lambda t: flag - 1 + quad(lambda tau: self._pdf(a, b, c, tau), 0, t)[0], x0)[0]
Which takes a probability density function and finds a result tau such that the integral of pdf from tau to infinity is equal to flag. Note that x0 is a (float) estimate of the root defined elsewhere in the script. Also note that flag is an extremely small number, on the order of 1e-9.
In my application fsolve only successfully finds a root about 50% of the time. It often just returns x0, significantly biasing my results. There is no closed form for the integral of pdf, so I am forced to integrate numerically and feel that this might be introducing some inaccuracy?
EDIT:
This has since been solved using a method other than that described below, but I'd like to get quadpy to work and see if the results improve at all. The specific code I'm trying to get to work is as follows:
import quadpy
import numpy as np
from scipy.optimize import *
from scipy.special import gammaln, kv, gammaincinv, gamma
from scipy.integrate import quad, simps
l = 226.02453163
mu = 0.00212571582056
nu = 4.86569872444
flag = 2.5e-09
estimate = 3 * mu
def pdf(l, mu, nu, t):
return np.exp(np.log(2) + (l + nu - 1 + 1) / 2 * np.log(l * nu / mu) + (l + nu - 1 - 1) / 2 * np.log(t) + np.log(
kv(nu - l, 2 * np.sqrt(l * nu / mu * t))) - gammaln(l) - gammaln(nu))
def tail_cdf(l, mu, nu, tau):
i, error = quadpy.line_segment.adaptive_integrate(
lambda t: pdf(l, mu, nu, t), [tau, 10000], 1.0e-10
)
return i
result = fsolve(lambda tau: flag - tail_cdf(l, mu, nu, tau[0]), estimate)
When I run this I get an assertion error from assert all(lengths > minimum_interval_length). I'm not quite sure of how to remedy this; any help would be very much appreciated!
As an example, I tried 1 / x for the integration between 1 and alpha to retrieve the target integral 2.0. This
import quadpy
from scipy.optimize import fsolve
def f(alpha):
beta, _ = quadpy.quad(lambda x: 1.0/x, 1, alpha)
return beta
target = 2.0
res = fsolve(lambda alpha: target - f(alpha), x0=2.0)
print(res)
correctly returns 7.38905611.
The failing quadpy assertion
assert all(lengths > minimum_interval_length)
you're getting means that the adaptive integration hit its limit: Either relax your tolerance a bit, or decrease the minimum_interval_length (see here).

Fitting data to numerical solution of an ode in python

I have a system of two first order ODEs, which are nonlinear, and hence difficult to solve analytically in a closed form. I want to fit the numerical solution to this system of ODEs to a data set. My data set is for only one of the two variables that are part of the ODE system. How do I go about this?
This didn't help because there's only one variable there.
My code which is currently leading to an error is:
import numpy as np
from scipy.integrate import odeint
from scipy.optimize import curve_fit
def f(y, t, a, b, g):
S, I = y # S, I are supposed to be my variables
Sdot = -a * S * I
Idot = (a - b) * S * I + (b - g - b * I) * I
dydt = [Sdot, Idot]
return dydt
def y(t, a, b, g, y0):
y = odeint(f, y0, t, args=(a, b, g))
return y.ravel()
I_data =[] # I have data only for I, not for S
file = open('./ratings_showdown.csv')
for e_raw in file.read().split('\r\n'):
try:
e=float(e_raw); I_data.append(e)
except ValueError:
continue
data_t = range(len(I_data))
popt, cov = curve_fit(y, data_t, I_data, [.05, 0.02, 0.01, [0.99,0.01]])
#want to fit I part of solution to data for variable I
#ERROR here, ValueError: setting an array element with a sequence
a_opt, b_opt, g_opt, y0_opt = popt
print("a = %g" % a_opt)
print("b = %g" % b_opt)
print("g = %g" % g_opt)
print("y0 = %g" % y0_opt)
import matplotlib.pyplot as plt
t = np.linspace(0, len(data_y), 2000)
plt.plot(data_t, data_y, '.',
t, y(t, a_opt, b_opt, g_opt, y0_opt), '-')
plt.gcf().set_size_inches(6, 4)
#plt.savefig('out.png', dpi=96) #to save the fit result
plt.show()
This type of ODE fitting becomes a lot easier in symfit, which I wrote specifically as a user friendly wrapper to scipy. I think it will be very useful for your situation because the decreased amount of boiler-plate code simplifies things a lot.
From the docs and applied roughly to your problem:
from symfit import variables, parameters, Fit, D, ODEModel
S, I, t = variables('S, I, t')
a, b, g = parameters('a, b, g')
model_dict = {
D(S, t): -a * S * I,
D(I, t): (a - b) * S * I + (b - g - b * I) * I
}
ode_model = ODEModel(model_dict, initial={t: 0.0, S: 0.99, I: 0.01})
fit = Fit(ode_model, t=tdata, I=I_data, S=None)
fit_result = fit.execute()
Check out the docs for more :)
So I figured out the problem.
The curve_fit() function apparently returns a list as it's second return value. So, instead of passing the initial conditions as a list [0.99,0.01], I passed them separately as 0.99 and 0.01.

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