Need help solving numerical ODE [Python] - python

I have a physics problem and have never used odeint or any numerical solving for ODE on python and am a little confused. I tried looking at other examples but could not understand and am hoping for a little help. My ODE is:
Where α is a given angle and g is a constant.
r=sqrt(x^2+y^2)
The program will ask for x_0, dx/dt_0 and dy/dt_0.
I'm mostly unsure how to solve ODE's in python. I have seen that I should split my ODE into dr'/dt because odeint will only do a first order ODE's. Could someone help explain how to do this?
I tried using another example to do as much as possible but am stuck:
import numpy as np
import matplotlib.pylab as plt
from scipy.integrate import odeint
pi=np.pi
sin=np.sin
cos=np.cos
sqrt=np.sqrt
alpha=pi/4
g=9.80665
y0=0.0
theta0=0.0
x=[]
y=[]
sina = sin(alpha)**2
second_term = g*sin(alpha)*cos(alpha)
x0 = float(raw_input('What is the initial x in meters?'))
x_vel0 = float(raw_input('What is the initial velocity in the x direction in m/s?'))
y_vel0 = float(raw_input('what is the initial velocity in the y direction in m/s?'))
t_f = float(raw_input('What is the maximum time in seconds?'))
r0 = x0
r = sqrt(float(x)**2 + float(y)**2)
def deriv_z(z,r):
r, rdot=z
return [rdot,r*sina-second_term]
zdot0=x_vel0**2+y_vel0**2
z0 = [r0,zdot0]
times = np.linespace(0, t_f, 1000)
z = odeint(deriv_z,z0,times)

There's a great example I found to help me with my planetary orbit ODE solving.
It uses an adaptive step size solver and plots the orbit nicely. If your solution can handle it, you may also try to use the faster 'lsoda' or 'vode' options instead of 'dopri5' (which is a very solid standard).

Related

SciPy ODEINT not working with certain differential equations

I'm new to Python and trying to create basic trajectory plots of a 2D system. This is what I'm working with right now. It plots forward trajectories for a number of points.
import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
from scipy.integrate import odeint
def dx_dt(x,t):
return [x[0]*2, x[1]]
xmin = ymin = -10
xmax = ymax = 10
plt.figure()
plt.xlim(xmin, xmax)
plt.ylim(ymin, ymax)
ts = np.linspace(0,1,20)
ic = np.linspace(xmin, xmax, 11)
for r in ic:
for s in ic:
x0 = [r, s]
xs = sp.integrate.odeint(dx_dt, x0, ts)
plt.plot(xs[:,0], xs[:,1], "g")
plt.savefig("flowlines.jpg")
The issue that's arising is ODEINT is not working with certain systems, instead saying this half a dozen times
ODEintWarning: Excess work done on this call (perhaps wrong Dfun type). Run with full_output = 1 to get quantitative information.
and subsequently crashing.
I've been playing around with the specific systems of differential equations, and I think I've found the specific problem that's arising. Something like
def dx_dt(x,t):
return [x[1], x[0]**2+x[1]]
works, while
def dx_dt(x,t):
return [x[0], x[1]**2+x[0]]
does not. It seems that it does not like any specific initial conditions where x' =f(x,y) if f(x,y) contains anything involving x other than addition and scalar multiplication. It can do anything it wants with y. Hence, the first one runs just fine, but the second one fails, because y' = y^2+x involves a power of y.
I have no idea how to proceed from here.
You suspicion about the behavior of the solver depending on the algebraic form of the equations is reasonable. It is likely that the systems for which you are experiencing a problem are ones where the solution "blows up" in finite time. That is, one or both components go to infinity as t approaches a finite value. This can easily happen when the right-hand side depends on powers of the variables.
A simple one-dimensional system that exhibits this behavior is
dx/dt = x**2, x(0) = x0
which has the explicit solution
x(t) = x0/(1 - x0*t)
You can verify that it solves the differential equation and initial condition, at least in a neighborhood of t=0. As t → 1/x0 (assuming x0 ≠ 0), the solution diverges to infinity. For example, here's a plot of the solution with x(0) = 0.5:
The red vertical dashed line indicates where the solution blows up.
Equations like this violate the mathematical assumptions on which ODE solvers such as odeint are based. Solvers will generally fail when they encounter such a point, typically because they vainly try to take smaller and smaller steps to bring down the estimated local error as they approach the point where the solution blows up:
In [54]: from scipy.integrate import odeint
In [55]: def sys(x, t):
...: return x**2
...:
In [56]: odeint(sys, 0.5, [0, 2.5])
[...]/lib/python3.9/site-packages/scipy/integrate/odepack.py:247:
ODEintWarning: Excess work done on this call (perhaps wrong Dfun type).
Run with full_output = 1 to get quantitative information.
warnings.warn(warning_msg, ODEintWarning)
Out[56]:
array([[5.00000000e-01],
[4.82437777e+08]])
The solution is the same as the punch line to the old joke:
Patient: Doctor, it hurts when I do this...
Doctor: Then don't do that!

Solving two coupled second order boundary value problems

I have solved a single second order differential equation with two boundary conditions using the module solve_bvp. However, now I am trying to solve the system of two second order differential equations;
U'' + a*B' = 0
B'' + b*U' = 0
with the boundary conditions U(+/-0.5) = +/-0.01 and B(+/-0.5) = 0. I have split this into a system of first ordinary differential equations and I am trying to use solve_bvp to solve them numerically. However, I am just getting arrays full of zeros for my solution. I believe I am implementing the boundary conditions wrong. It is not clear to me how to handle more than two equations from the documentation. My attempt is below
import numpy as np
from scipy.integrate import solve_bvp
import matplotlib.pyplot as plt
%matplotlib inline
from scipy.integrate import solve_bvp
alpha = 1E-8
zeta = 8E-3
C_k = 0.05
sigma = 0.01
def fun(x, y):
return np.vstack((y[1],-((alpha)/(C_k*sigma))*y[2],y[2], -(1/(C_k*zeta))*y[1]))
def bc(ya, yb):
return np.array([ya[0]+0.001, yb[0]-0.001,ya[0]-0, yb[0]-0])
x = np.linspace(-0.5, 0.5, 5000)
y = np.zeros((4, x.size))
print(y)
sol = solve_bvp(fun, bc, x, y)
print(sol)
In my question I have just relabeled a and b, but they're just parameters that I input. I have the analytic solution for this set of equations so I know one exists that is non-trivial. Any help would be greatly appreciated.
It is most times really helpful if you state at least once in a comment or by assignment to specifically named variables how you want to compose the state vector.
By the form of the derivative return vector, I would think you intend
U, U', B, B'
which means that U=y[0], U'=y[1] and B=y[2],B'=y[3], so that your derivatives vector should correctly be
return y[1], -((alpha)/(C_k*sigma))*y[3], y[3], -(1/(C_k*zeta))*y[1]
and the boundary conditions
return ya[0]+0.001, yb[0]-0.001, ya[2]-0, yb[2]-0
Especially your boundary condition should throw the algorithm in the first step because of a singular Jacobian, always check the .success field and the .message field of the solution structure.
Note that by default the absolute and relative tolerance of the experimental solve_bvp is 1e-3, and the number of nodes is limited to 500.
Setting the initial node number to 50 (5000 is much too much, the solver refines where necessary), and the tolerance to 1-6, I get the following solution plots that visibly satisfy the boundary conditions.

Python: odeint to solve ODEs with variable coefficients (QHO)

I'm trying to solve the equation y'' + (epsilon-x^2)y = 0 numerically using odeint. I know the solutions (the wavefunctions of a QHO), but the output from odeint has no apparent relation to it. I can solve ODEs with constant coefficients just fine, but as soon as I move to variable ones, I can't solve any of the ones I tried. Here's my code:
#!/usr/bin/python2
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as spi
x = np.linspace(-5,5,1e4)
n = 0
epsilon = 2*n+1
def D(Y,x):
return np.array([Y[1], (epsilon-x**2)*Y[0]])
Y0 = [0,1]
Y = spi.odeint(D,Y0,x)
# Y is an array with the first column being y(x) and the second y'(x) for all x
plt.plot(x,Y[:,0],label='num')
#plt.plot(x,Y[:,1],label='numderiv')
plt.legend()
plt.show()
And the plot:
[not enough rep:] https://drive.google.com/file/d/0B6840LH2NhNpdUVucUxzUGFpZUk/edit?usp=sharing
Look here for plots of solution: http://hyperphysics.phy-astr.gsu.edu/hbase/quantum/hosc5.html
It looks like your equation is not correctly interpreted. You have a differential equation y'' + (epsilon-x^2)y = 0, but you forget a minus sign in your vector form. In particular it should be
y[0]' = y[1]
y[1]' = -(epsilon-x^2)y[0]
So (adding the minus sign in front of the epsilon term
def D(Y,x):
return np.array([Y[1], -(epsilon-x**2)*Y[0]])
In fact the plot you have is consistent with the DE y'' + (epsilon-x^2)y = 0. Check it out: Wolphram Alpha

Having trouble while using scipy.integrate.odeint with python

I was trying to use odeint to solve a problem. My code is as below:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
eta=1.24e-9/2
def fun(x):
f=1.05e-8*eta*x**(1.5)*np.exp(13.6/x)
return (np.sqrt(1.+4*f)-1)/2./f
x=np.arange(0,1,0.001)
y=odeint(fun,x,0)[0]
plt.plot(x,y)
plt.plot(x,x)
plt.show()
It the two curves are the same, which is obviously wrong. If I plot the function, it will looks like a step function, which is very very small before about 0.3 and exponentially goes to 1. Can you help me figure out what's wrong with it? Thank you!
There are several problems with your code, most of which you might be able to solve yourself if you read the docstring for odeint more carefully.
To get you started, the following is a simple example of solving a scalar differential equation with odeint. Instead of trying to understand (and possibly debug) your function, I'll use a very simple equation. I'll solve the equation dy/dt = a * y, with initial condition y(0) = 100. Once you have this example working, you can modify fun to solve your problem.
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def fun(y, t, a):
"""Define the right-hand side of equation dy/dt = a*y"""
f = a * y
return f
# Initial condition
y0 = 100.0
# Times at which the solution is to be computed.
t = np.linspace(0, 1, 51)
# Parameter value to use in `fun`.
a = -2.5
# Solve the equation.
y = odeint(fun, y0, t, args=(a,))
# Plot the solution. `odeint` is generally used to solve a system
# of equations, so it returns an array with shape (len(t), len(y0)).
# In this case, len(y0) is 1, so y[:,0] gives us the solution.
plt.plot(t, y[:,0])
plt.xlabel('t')
plt.ylabel('y')
plt.show()
Here's the plot:
More complicated examples of the use of odeint can be found in the SciPy Cookbook (scroll down to the bullet labeled "Ordinary differential equations").

Errors while solving ODE's python

I have a university project in which we are asked to simulate a satellite approach to Mars using ODE's and SciPy's odeint function.
I manage to simulate it in 2D by making a second-order ODE into two first-order ODE's. However I am stuck in the time limitation because my code is using SI units therefore running in seconds and Python's linspace limits does not even simulate one complete orbit.
I tried converting the variables and constants to hours and kilometers but now the code keeps giving errors.
I followed this method:
http://bulldog2.redlands.edu/facultyfolder/deweerd/tutorials/Tutorial-ODEs.pdf
And the code is:
import numpy
import scipy
from scipy.integrate import odeint
def deriv_x(x,t):
return array([ x[1], -55.3E10/(x[0])**2 ]) #55.3E10 is the value for G*M in km and hours
xinit = array([0,5251]) # this is the velocity for an orbit of period 24 hours
t=linspace(0,24.0,100)
x=odeint(deriv_x, xinit, t)
def deriv_y(y,t):
return array([ y[1], -55.3E10/(y[0])**2 ])
yinit = array([20056,0]) # this is the radius for an orbit of period 24 hours
t=linspace(0,24.0,100)
y=odeint(deriv_y, yinit, t)
I don't know how to copy/paste the error code from PyLab so I took a PrintScreen of the error:
Second error with t=linspace(0.01,24.0,100) and xinit=array([0.001,5251]):
If anyone has any suggestions on how to improve the code I will be very grateful.
Thank you very much!
odeint(deriv_x, xinit, t)
uses xinit as its initial guess for x. This value for x is used when evaluating deriv_x.
deriv_x(xinit, t)
raises a divide-by-zero error since x[0] = xinit[0] equals 0, and deriv_x divides by x[0].
It looks like you are trying to solve the second-order ODE
r'' = - C rhat
---------
|r|**2
where rhat is the unit vector in the radial direction.
You appear to be separating the x and y coordinates into separate second-order ODES:
x'' = - C y'' = - C
----- and -----
x**2 y**2
with initial conditions x0 = 0 and y0 = 20056.
This is very problematic. Among the problems is that when x0 = 0, x'' blows up. The original second-order ODE for r'' does not have this problem -- the denominator does not blow up when x0 = 0 because y0 = 20056, and so r0 = (x**2+y**2)**(1/2) is far from zero.
Conclusion: Your method of separating the r'' ODE into two ODEs for x'' and y'' is incorrect.
Try searching for a different way to solve the r'' ODE.
Hint:
What if your "state" vector is z = [x, y, x', y']?
Can you write down a first-order ODE for z' in terms of x, y,
x' and y'?
Can you solve it with one call to integrate.odeint?

Categories