I am a python beginner, currently using scipy's odeint to compute a coupled ODE system, however, when I run, python shell always tell me that
>>>
Excess work done on this call (perhaps wrong Dfun type).
Run with full_output = 1 to get quantitative information.
>>>
So, I have to change my time step and final time, in order to make it integratable. To do this, I need to try a different combinations, which is quite a pain. Could anyone tell me how can I ask odeint to automatically vary the time step and final time to successfully integrate this ode system?
and here is part of the code which has called odeint:
def main(t, init_pop_a, init_pop_b, *args, **kwargs):
"""
solve the obe for a given set of parameters
"""
# construct initial condition
# initially, rho_ee = 0
rho_init = zeros((16,16))*1j ########
rho_init[1,1] = init_pop_a
rho_init[2,2] = init_pop_b
rho_init[0,0] = 1 - (init_pop_a + init_pop_b)########
rho_init_ravel, params = to_1d(rho_init)
# perform the integration
result = odeint(wrapped_bloch3, rho_init_ravel, t, args=args)
# BUG: need to pass kwargs
# rewrap the result
return from_1d(result, params, prepend=(len(t),))
things = [2*pi, 20*pi, 0,0, 0,0, 0.1,100]
Omega_a, Omega_b, Delta_a, Delta_b, \
init_pop_a, init_pop_b, tstep, tfinal = things
args = ( Delta_a, Delta_b, Omega_a, Omega_b )
t = arange(0, tfinal + tstep, tstep)
data = main(t, init_pop_a, init_pop_b, *args)
plt.plot(t,abs(data[:,4,4]))
where wrapped_bloch3 is the function compute dy/dt.
EDIT: I note you already got an answer here: complex ODE systems in scipy
odeint does not work with complex-valued equations. I get
from scipy.integrate import odeint
import numpy as np
def func(t, y):
return 1 + 1j
t = np.linspace(0, 1, 200)
y = odeint(func, 0, t)
# -> This outputs:
#
# TypeError: can't convert complex to float
# odepack.error: Result from function call is not a proper array of floats.
You can solve your equation by the other ode solver:
from scipy.integrate import ode
import numpy as np
def myodeint(func, y0, t):
y0 = np.array(y0, complex)
func2 = lambda t, y: func(y, t) # odeint has these the other way :/
sol = ode(func2).set_integrator('zvode').set_initial_value(y0, t=t[0])
y = [sol.integrate(tp) for tp in t[1:]]
y.insert(0, y0)
return np.array(y)
def func(y, t, alpha):
return 1j*alpha*y
alpha = 3.3
t = np.linspace(0, 1, 200)
y = myodeint(lambda y, t: func(y, t, alpha), [1, 0, 0], t)
Related
I am solving a set of coupled ODEs using solve_bvp in python. I have solved the equations for the boundary conditions that U = 0 and B = 0 on the boundaries, however I am trying to solve them such that U' = 0 and B = 0 on the boundary. My problem is that I keep encountering a singular jacobian in the return message - my guess is that the initial guess is diverging, however I have tried a range of initial guesses and still no solution. Is there a more systematic way of figuring this out? My code is below:
import numpy as np
from scipy import integrate
from scipy.integrate import solve_bvp
import matplotlib.pyplot as plt
%matplotlib inline
x = np.linspace(-0.5, 0.5, 1000)
y = np.ones((4, x.size))
y[0] = U_0*np.tanh(Q*x)
U_0 = 0.1
Q = 30
## Then we calculate the total energy
def FullSolver(x,y,Ha, Rm):
def fun(x, y):
U, dU, B, dB = y;
d2U = -2*U_0*Q**2*(1/np.cosh(Q*x))**2*np.tanh(Q*x)-((Ha**2)/(Rm))*dB;
d2B = -Rm*dU;
return dU, d2U, dB, d2B
def bc(ya, yb):
return ya[1], yb[1], ya[2]-0, yb[2]+0
# sol will give us the solutions which are accessible if we need them
sol = solve_bvp(fun, bc, x, y, tol=1e-12)
return(sol.x, sol.yp[0], sol.y[2], sol.message)
FullSolver(x, y, 0.000005, 0.00009)
I am trying to solve the set of coupled boundary value problems such that;
U'' +aB'+ b*(cosh(lambda z))^{-2}tanh(lambda*z) = 0,
B'' + c*U' = 0,
T'' = (gamma^{-1} - 1)*(d*(U')^2 + e*(B')^2)
subject to the boundary conditions U(+/- 1/2) = +/-U_0*tanh(lambda/2), B(+/- 1/2) = 0 and T(-1/2) = 1, T(1/2) = 4. I have decomposed this set of equations into a set of first order differential equations, and used the derivative array such that [U, U', B, B', T, T']. But bvp solve is returning the error that I have a single Jacobian. When I remove the last two equations, I get a solution for U and B and that works fine. However, I am unsure why adding the other two equations results in this issue.
import numpy as np
from scipy.integrate import solve_bvp
import matplotlib.pyplot as plt
%matplotlib inline
alpha = 1E-7
zeta = 8E-3
C_k = 0.01
sigma = 0.005
Q = 30
U_0 = 0.1
gamma = 5/3
theta = 3
def fun(x, y):
return y[1], -2*U_0*Q**2*(1/np.cosh(Q*x))**2*np.tanh(Q*x)-((alpha)/(C_k*sigma))*y[3], y[3],\
-(1/(C_k*zeta))*y[1], y[4], (1/gamma - 1)*(sigma*(y[1])**2 + zeta*alpha*(y[3])**2)
def bc(ya, yb):
return ya[0]+U_0*np.tanh(Q*0.5), yb[0]-U_0*np.tanh(Q*0.5), ya[2]-0, yb[2]-0, ya[4] - 1, yb[4] - 4
x = np.linspace(-0.5, 0.5, 500)
y = np.zeros((6, x.size))
sol = solve_bvp(fun, bc, x, y)
print(sol)
However, the error that I am getting is that 'setting an array with sequence'. The first function and boundary conditions solves two coupled equations, then I use these results to evaluate the equation I have given. I have tried writing all of my equations in one function, however this seems to be returning trivial solutions i.e an array full of zeros.
Any help would be appreciated.
When the expressions become larger it is often more helpful to keep the computations human readable instead of compact.
def fun(x, y):
U, dU, B, dB, T, dT = y;
d2U = -2*U_0*Q**2*(1/np.cosh(Q*x))**2*np.tanh(Q*x)-((alpha)/(C_k*sigma))*dB;
d2B = -(1/(C_k*zeta))*dU;
d2T = (1/gamma - 1)*(sigma*dU**2 + zeta*alpha*dB**2);
return dU, d2U, dB, d2B, dT, d2T
This avoids missing an index error as there are no indices in this computation, all has names close to the original formulas.
Then the solution components (using initialization with only 5 points, resulting in a refinement with 65 points) plots as
I want to solve the equation in python over the time Interval I = [0,10] with initial condition (x_0, y_0) = (1,0) and the parameter values μ ∈ {-2, -1, 0, 1, 2} using the function
scipy.integrate.odeint
Then I want to plot the solutions (x(t;x_0,y_0), y(t;x_0,y_0)) in the xy-plane.
The originally given linear system is
dx/dt = y, x(0) = x_0
dy/dt = - x - μy, y(0) = y_0
Please see my code below:
import numpy as np
from scipy.integrate import odeint
sol = odeint(myode, y0, t , args=(mu,1)) #mu and 1 are the coefficients when set equation to 0
y0 = 0
myode(y, t, mu) = -x-mu*y
def t = np.linspace(0,10, 101) #time interval
dydt = [y[1], -y[0] - mu*y[1]]
return dydt
Could anyone check if I defined the callable function myode correctly? This function evaluates the right hand side of the ODE.
Also an syntax error message showed up for this line of code
def t = np.linspace(0,10, 101) #time interval
saying there is invalid syntax. Should I somehow use
for * in **
to get rid of the error message? If yes, how exactly?
I am very new to Python and ODE. Could anyone help me with this question? Thank you very much!
myode should be a function definition, thus
def myode(u, t, mu): x,y = u; return [ y, -x-mu*y]
The time array is a simple variable declaration/assignment, there should be no def there. As the system is two-dimensional, the initial value also needs to have dimension two
sol = odeint(myode, [x0,y0], t, args=(mu,) )
Thus a minimal modification of your script is
def myode(u, t, mu): x,y = u; return [ y, -x-mu*y]
t = np.linspace(0,10, 101) #time interval
x0,y0 = 1,0 # initial conditions
for mu in [-2,-1,0,1,2]:
sol = odeint(myode, [x0,y0], t, args=(mu,) )
x,y = sol.T
plt.plot(x,y)
a=5; plt.xlim(-a,a); plt.ylim(-a,a)
plt.grid(); plt.show()
giving the plot
Try using the solve_ivp method.
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
import numpy as np
i = 0
u = [-2,-1,0,1,2]
for x in u:
def rhs2(t,y):
return [y[1], -1*y[0] - u[x]*y[1]]
value = u[i]
res2 = solve_ivp(rhs2, [0,10], [1,0] , t_eval=[0,1,2,3,4,5,6,7,8,9,10], method = 'RK45')
t = np.array(res2.t[1:-1])
x = np.array(res2.y[0][1:-1])
y = np.array(res2.y[1][1:-1])
fig = plt.figure()
plt.plot(t, x, 'b-', label='X(t)')
plt.plot(t, y, 'g-', label='Y(t)')
plt.title("u = {}".format(value))
plt.legend(loc='lower right')
plt.show()
i = i + 1
Here is the solve_ivp method Documentation
Here is a very similar problem with a better explanation.
I'm trying to solve a pendulum-like differential equation that goes like d2(phi)/dt = -(g/R) *sin(phi) (it's the skateboard problem in Taylor's Classical Mechanics). I'm new to scipy and odeint and the like, so I'm doing this to prepare for more sophisticated numerical solutions in the future.
I've used code from here to try and navigate the coding, but all I'm coming up with is a flat line for phi(t). I think it stems from the fact that I'm trying to split a second order differential equation into two first orders, where one doesn't depend on the other (because d(phi)/dt to make an appearance); but I'm not sure how to go about fixing it.
Anyone know what's wrong with it?
# integrate skateboard problem, plot result
from numpy import *
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def skate(y, t, params):
phi, omega = y
g, R = params
derivs = [omega, -(g/R)*np.sin(phi)]
return derivs
# Parameters
g = 9.81
R = 5
params = [g, R]
#Initial values
phi0 = 20
omega0 = 0
y0 = [phi0, omega0]
t = linspace(0, 20, 5000)
solution = odeint(skate, y0, t, args=(params,))
plt.plot(t, solution[:,0])
plt.xlabel('time [s]')
plt.ylabel('angle [rad]')
plt.show()
I suspect a bug here: -(g/R)*np.sin(phi). Perhaps you forget to define the alias for numpy lib import (for example: import numpy as np). Instead you just did (from numpy import *). Try this:
def skate(y, t, params):
phi, omega = y
g, R = params
derivs = [omega, -(g/R)*sin(phi)]
return derivs
I am studying the dynamics of a damped, driven pendulum with second order ODE defined like so, and specifically I am progamming:
d^2y/dt^2 + c * dy/dt + sin(y) = a * cos(wt)
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
def pendeq(t,y,a,c,w):
y0 = -c*y[1] - np.sin(y[0]) + a*np.cos(w*t)
y1 = y[1]
z = np.array([y0, y1])
return z
a = 2.1
c = 1e-4
w = 0.666667 # driving angular frequency
t = np.array([0, 5000]) # interval of integration
y = np.array([0, 0]) # initial conditions
yp = integrate.quad(pendeq, t[0], t[1], args=(y,a,c,w))
This problem does look quite similar to Need help solving a second order non-linear ODE in python, but I am getting the error
Supplied function does not return a valid float.
What am I doing wrong??
integrate.quad requires that the function supplied (pendeq, in your case) returns only a float. Your function is returning an array.