I have been working with odeint and boundary conditions. Bassically what I am trying to do is to solve the differential equations given in this figure 1
where in my code R=R, ph = Phi, al = alpha, a = a, m = m, l = l and om = omega. The initial conditions that I am trying to implement are R(0)=O(r^l); Phi(0)=O(r^{l-1}) if l/=0 and Phi(0)=O(r) if l=0; a(0) = 1 and a(inf)=1/alpha(inf) (additionally I need that R(inf)=0). I tried to applied the shooting method in order to find initial conditions for alpha that best matches with my boundary conditions. I also need to find the omega that best matches the boundary conditions for R at infinity. The code that I wrote is the following:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import time
start = time.clock()
def system_DE(IC,p,r):
l = p[0]
m = p[1]
om = p[2]
R = IC[0]
ph = IC[1]
a = IC[2]
al = IC[3]
dR_dr = ph
da_dr = a*((2*l+1)*r/2*(om**2*a**2*R**2/al**2+ph**2+l*(l+1)*a**2*R**2/r**2+m**2*a**2*R**2)-(a**2-1)/(2*r))
dal_dr = al*(da_dr/a-l*(l+1)*(2*l+1)*a**2*R**2/r-(2*l+1)*m**2*a**2*r*R**2+(a**2-1)/r)
dph_dr = -2*ph/r-dal_dr*ph/al+da_dr*ph/a-om**2*a**2*R/al**2+l*(l+1)*a**2*R/r**2+m**2*a**2*R
return [dR_dr,da_dr,dal_dr,dph_dr]
def init(u,p,r):
if p==0:
return np.array([1,r,1,u])
else:
return np.array([r**l,l*r**(l-1),1,u])
l = 0
m = 1
ep = 0.3
n_om = 10
omega = np.linspace(m-ep,m+ep,n_om)
r = np.linspace(0.0001, 100, 1000)
niter = 100
u = 0
tol = 0.1
ustep = 0.01
p = np.zeros(3)
p[0] = l
p[1] = m
for j in range(len(omega)):
p[2] = omega[j]
for i in range(niter):
u += ustep
Y = odeint(system_DE(init(u,p[0],r[0]),p,r), init(u,p[0],r[0]), r)
print Y[-1,2]
print Y[-1,3]
if abs(Y[len(Y)-1,2]-1/Y[len(Y)-1,3]) < tol:
print(i,'times iterations')
print("a'(inf)) = ", Y[len(Y)-1,2])
print('y"(0) =',u)
break
if abs(Y[len(Y)-1,0]) < tol:
print(j,'times iterations in omega')
print("R'(inf)) = ", Y[len(Y)-1,0])
break
However, when I run it I am obtaining: error: The function and its Jacobian must be callable functions.
Could some one help me to understand what my mistake is?
Regards,
Luis Padilla.
To start with, the first argument to odeint is your derivative function system_DE. Just pass its name, no parentheses or arguments. Odeint with call it internally and supply arguments.
I fixed my code and now it is giving me some results. However, when I run it I am obtaining some warnings that I don't know how to solve it. Could some one help me to solve it? Basically my code is this:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import time
def system_DE(IC,r,l,m,om):
R = IC[0]
ph = IC[1]
a = IC[2]
al = IC[3]
dR_dr = ph
da_dr = a*((2*l+1)*r/2*(om**2*a**2*R**2/al**2+ph**2+l*(l+1)*a**2*R**2/r**2+m**2*a**2*R**2)-(a**2-1)/(2*r))
dal_dr = al*(da_dr/a-l*(l+1)*(2*l+1)*a**2*R**2/r-(2*l+1)*m**2*a**2*r*R**2+(a**2-1)/r)
dph_dr = -2*ph/r-dal_dr*ph/al+da_dr*ph/a-om**2*a**2*R/al**2+l*(l+1)*a**2*R/r**2+m**2*a**2*R
return [dR_dr,dph_dr,da_dr,dal_dr]
def init(u,p,r):
if p==0:
return np.array([1.,r,1.,u])
else:
return np.array([r**p,l*r**(p-1),1,u])
l = 0.
m = 1.
ep = 0.2
n_om = 30
omega = np.linspace(m-ep,m+ep,n_om)
r = np.linspace(0.001, 100, 1000)
niter = 1000
tol = 0.01
ustep = 0.01
for j in range(len(omega)):
print('trying with $omega =$',omega[j])
p = (l,m,omega[j])
u = 0.001
for i in range(niter):
u += ustep
ini = init(u,p[0],r[0])
Y = odeint(system_DE, ini,r,p,mxstep=500000)
if abs(Y[len(Y)-1,2]-1/Y[len(Y)-1,3]) < tol:
break
if abs(Y[len(Y)-1,0]) < tol and abs(Y[len(Y)-1,2]-1/Y[len(Y)-1,3]) < tol:
print(j,'times iterations in omega')
print(i,'times iterations')
print("R'(inf)) = ", Y[len(Y)-1,0])
print("alpha(0)) = ", Y[0,3])
print("\omega",omega[j])
break
plt.subplot(2,1,1)
plt.plot(r,Y[:,0],'r',label = '$R$')
plt.plot(r,Y[:,1],'b',label = '$d R /dr$')
plt.xlim([0,10])
plt.legend()
plt.subplot(2,1,2)
plt.plot(r,Y[:,2],'r',label = 'a')
plt.plot(r,Y[:,3],'b', label = '$alpha$')
plt.xlim([0,10])
plt.legend()
plt.show()
But when I run it I am obtaining this:
lsoda-- warning..internal t (=r1) and h (=r2) are
such that in the machine, t + h = t on the next step
(h = step size). solver will continue anyway
in above, r1 = 0.1243782486482D+01 r2 = 0.8727680448722D-16
How could I fix the problem?
Regards,
Luis Padilla.
Related
currently I am trying to think about how to speedup odeint for a simulation I am running. Actually its only a primitive second order ode with a friction term and a discontinuous force. The model I am using to describe the dynamics is defined in a seperate function. Trying to solve the ode results in an error or extremely high computation times (talking about days).
Here is my code mostly hardcoded:
import numpy as np
import pandas as pd
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def create_ref(tspan):
if tspan>2 and tspan<8:
output = np.sin(tspan)
elif tspan>=20:
output = np.sin(tspan*10)
else:
output = 0.5
return output
def model(state,t):
def Fs(x):
pT = 0
pB = 150
p0 = 200
pA = 50
kein = 0.25
kaus = 0.25
if x<0:
pA = pT
Fsres = -kaus*x*pA-kein*x*(p0-pB)
else:
pB = pT
Fsres = -kaus*x*pB-kein*x*(p0-pA)
return Fsres
x,dx = state
refnow = np.interp(t,xref.index.values,xref.values.squeeze())
refprev = np.interp(t-dt,xref.index.values,xref.values.squeeze())
drefnow = (refnow-refprev)/dt
x_meas = x
dx_meas = dx
errnow = refnow-x_meas
errprev = refprev-(x_meas-dx_meas*dt)
intrefnow = dt*(errnow-errprev)
kp = 10
kd = 0.1
ki = 100
sigma = kp*(refnow-x_meas)+kd*(drefnow-dx_meas)+ki*intrefnow
tr0 = 5
FricRed = (1.5-0.5*np.tanh(0.1*(t-tr0)))
kpu = 300
fr = 0.1
m = 0.01
d = 0.01
k = 10
u = float(kpu*np.sqrt(np.abs(sigma))*np.sign(sigma))
ddx = 1/m*(Fs(x)+FricRed*fr*np.sign(dx)-d*dx-k*x + u )
return [dx,ddx]
dt = 1e-3
tspan = np.arange(start=0, stop=50, step=dt)
steplim = tspan[-1]*0.1
reffunc = np.vectorize(create_ref)
xrefvals = reffunc(tspan)
xref = pd.DataFrame(data=xrefvals,index=tspan,columns=['xref'])
x0 = [-0.5,0]
simresult = odeint(model, x0, tspan)
plt.figure(num=1)
plt.plot(tspan,simresult[:,0],label='ispos')
plt.plot(tspan,xref['xref'].values,label='despos')
plt.legend()
plt.show()
I change the code according to Pranav Hosangadi s comments. Thanks for the hints. I didn't know that and learned something new and didn't expect dictionaries to have such a high impact on computation time. But now its much faster.
I'm trying to solve ODE with odeint.
My code is like this
from scipy.integrate import odeint
import numpy as np
import matplotlib.pyplot as plt
K = 35.94143542
S = 10
M = 30.03
Vr = 58
R = 8.3145
T = 293.15
Q = 0.4998
Vp = 0.000022
Mr = 18
Pvap = 518624.013
Cv = (40000/0.000022)*0.001
Cr = (40000/0.000022)*(1-0.001)
tr = 0.333333333
t = np.arange(0,4+1e-2,1e-2)
def Cair(C, t):
if t <= tr:
dCdt = ((K*S*M*Peq)/(Vr*R*T))-(((K*S)/Vr)+Q)*C
else:
dCdt = -(((K*S)/Vr)+Q)*C
return dCdt
Peq = (Cv*Pvap)/(Cv+Cr*(M/Mr))
C = odeint(Cair, 0 , t)
plt.plot(t, C)
def Cvt(c, t):
dcdt = -((K*S*M)/(Vp*R*T))*(Peq-((C*R*T)/M))
return dcdt
s = (K*S*M)/(Vp*R*T)
p = ((K*S*M)/(K*S*R*T+Vr*R*T*Q))*((Cv*Pvap)/(Cv+(Cr*M/Mr)))
q = ((K*S)/Vr)+Q
init = ((s*p*R*T)/M)*(1+(1/q))
c = odeint(Cvt, init, t)
plt.plot(t, c)
I successfully solve and draw first equation and graph(Cas function by t), But I failed to solve second equation and draw the graph(c as function by t)
How to do it?
I know this answer won't help you find the issue but I need to tell you that this code is particulary confusing !
https://www.python.org/dev/peps/pep-0008/#function-and-variable-names
Function names should be lowercase, with words separated by underscores. Variable names follow the same convention
So no var C and c or T and t even if it is math or physic constants and name it !
You shouldn't use variable that are not passed to the function. I don't understand the goal of using 2 functions in this code sample as it is only called once.
Also the name of the var in the function can be diffrent to the name you call the function with. So don't hesistate to write what each var is in the function, it is easier to understand.
def calculate_distance(speed, time):
return speed*time
s = 30
t = 1
calculate_distance(s, t)
SO that said :
# variables
r = 8.3145
t = 293.15
q = 0.4998
...
# list all var you need here
def calculate_dcdt(c, t, tr, k ... q):
if t <= tr:
dcdt = ((k*s*m*peq)/(vr*r*t))-(((k*s)/vr)+q)*c_1
else:
dcdt = -(((k*s)/vr)+q)*c_1
return dcdt
def calculate_cvt(c, t, k, ... peq):
dcdt = -((k*s*m)/(vp*r*t))*(peq-((c_1*r*t)/m))
return dcdt
peq = (vv*pvap)/(cv+cr*(m/mr))
c_1 = odeint(calculate_dcdt, 0 , t)
...
I am trying to use scipy.integrate.solve_ivp to calculate the solutions to newton's gravitation equation for my n body simulation, however I am confused how the function is passed into solve_ivp. I have the following code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
G = 6.67408e-11
m_sun = 1988500e24
m_jupiter = 1898.13e24
m_earth = 5.97219e24
au = 149597870.700e3
v_factor = 1731460
year = 31557600.e0
init_s = np.array([-6.534087946884256E-03*au, 6.100454846284101E-03*au, 1.019968145073305E-04*au, -6.938967653087248E-06*v_factor, -5.599052606952444E-06*v_factor, 2.173251724105919E-07*v_factor])
init_j = np.array([2.932487231769548E+00*au, -4.163444383137574E+00*au, -4.833604407653648E-02*au, 6.076788230491844E-03*v_factor, 4.702729516645153E-03*v_factor, -1.554436340872727E-04*v_factor])
variables_s = init_s
variables_j = init_j
N = 2
tStart = 0e0
t_End = 25*year
Nt = 2000
dt = t_End/Nt
temp_end = dt
t=tStart
domain = (t, temp_end)
planetsinit = np.vstack((init_s, init_j))
planetspos = planetsinit[:,0:3]
mass = np.vstack((1988500e24, 1898.13e24))
def weird_division(n, d):
return n / d if d else 0
variables_save = np.zeros((N,6,Nt))
variables_save[:,:,0] = planetsinit
pos_s = planetspos[0]
pos_j = planetspos[1]
while t < t_End:
t_index = int(weird_division(t, dt))
for index in range(len(planetspos)):
for otherindex in range(len(planetspos)):
if index != otherindex:
x1_p1, x2_p1, x3_p1 = planetsinit[index, 0:3]
x1_p2, x2_p2, x3_p2 = planetsinit[otherindex, 0:3]
m = mass[otherindex]
def f_grav(t, y):
x1_p1, x2_p1, x3_p1, v1_p1, v2_p1, v3_p1 = y
x1_diff = x1_p1 - x1_p2
x2_diff = x2_p1 - x2_p2
x3_diff = x3_p1 - x3_p2
dydt = [v1_p1,
v2_p1,
v3_p1,
-(x1_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2),
-(x2_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2),
-(x3_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2)]
return dydt
solution = solve_ivp(fun=f_grav, t_span=domain, y0=planetsinit[index])
planetsinit[index] = solution['y'][0:6, -1]
variables_save[index,:,t_index] = solution['y'][0:6, -1]
planetspos[index] = planetsinit[index][0:3]
t += dt
temp_end += dt
domain = (t,temp_end)
pos_s = variables_save[0,0:3,:]
pos_j = variables_save[1,0:3,:]
plt.plot(variables_save[0,0:3,:][0], variables_save[0,0:3,:][1])
plt.plot(variables_save[1,0:3,:][0], variables_save[1,0:3,:][1])
The code above works very nicely and produces a stable orbit. However when I calculate the acceleration outside the function and feed that through into the f_grav function, something goes wrong and produces an orbit which is no longer stable. However I am perplexed as I don't know why the data is different as to be it seems like that I have passed through the exactly same inputs. Which leads me to think that maybe its the way the the function f_grav is interpolated by the solve_ivp integrator? To calculate the acceleration outside all I do is change the following code in the loop to:
x1_p1, x2_p1, x3_p1 = planetsinit[index, 0:3]
x1_p2, x2_p2, x3_p2 = planetsinit[otherindex, 0:3]
m = mass[otherindex]
x1_diff = x1_p1 - x1_p2
x2_diff = x2_p1 - x2_p2
x3_diff = x3_p1 - x3_p2
ax = -(x1_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2)
ay = -(x2_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2)
az = -(x3_diff)*G*m/((x1_diff)**2+(x2_diff)**2+(x3_diff)**2)**(3/2)
def f_grav(t, y):
x1_p1, x2_p1, x3_p1, v1_p1, v2_p1, v3_p1 = y
dydt = [v1_p1,
v2_p1,
v3_p1,
ax,
ay,
az]
return dydt
solution = solve_ivp(fun=f_grav, t_span=domain, y0=planetsinit[index])
planetsinit[index] = solution['y'][0:6, -1]
variables_save[index,:,t_index] = solution['y'][0:6, -1]
planetspos[index] = planetsinit[index][0:3]
As I said I don't know why different orbits are produces which are shown below and any hints as to why or how to solve it would me much appreciated. To clarify why I can't use the working code as it is, as when more bodies are involved I aim to sum the accelerations contribution of all the other planets which isn't possible this way where the acceleration is calculated in the function itself.
Sorry for the large coding chunks but I did feel it was appropriate as then it could be run and the problem itself is clearer.
Both have the same time period, dt, however the orbit on the left is stable and the one on the right is not
I'm trying to solve the following system: d²i/dt² + R'(i)/L di/dt + 1/LC i(t) = 1/L dE/dt as a set of coupled first order differential equations:
di/dt = k
dk/dt = 1/L dE/dt - R'(i)/L k - 1/LC i(t)
Here is the code I'm using:
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from scipy.integrate import odeint
#Define model: x = [i , k]
def RLC(x , t):
i = sp.Symbol('i')
t = sp.Symbol('t')
#Data:
E = sp.ln(t + 1)
dE_dt = E.diff(t)
R1 = 1000 #1 kOhm
R2 = 100 #100 Ohm
R = R1 * i + R2 * i**3
dR_di = R.diff(i)
i = x[0]
k = x[1]
L = 10e-3 #10 mHy
C = 1.56e-6 #1.56 uF
#Model
di_dt = k
dk_dt = 1/L * dE_dt - dR_di/L * k - 1/(L*C) * i
dx_dt = np.array([di_dt , dk_dt])
return dx_dt
#init cond:
x0 = np.array([0 , 0])
#time points:
time = np.linspace(0, 30, 1000)
#solve ODE:
x = odeint(RLC, x0, time)
i = x[: , 0]
However, I get the following error: TypeError: Cannot cast array data from dtype('O') to dtype('float64') according to the rule 'safe'
So, I don't know if sympy and odeint don't work well together. Or maybe is it a problem because I defined t as sp.Symbol?
When you differentiate a function, you get a function back. So you need to evaluate it at a point in order to get a number. To evaluate a sympy expression, you could use .subs() but I prefer .replace() which feels more powerful (at least for me).
You must try and make every single variable have its own name in order to avoid confusion. For example, you replace the float input t with a sympy Symbol from the very beginning, thus losing the value of t. The variables x and i are also repeated in the outer scope which is not good practice if they mean different things.
The following should avoid confusion and hopefully produce something that you were expecting:
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from scipy.integrate import odeint
# Define model: x = [i , k]
def RLC(x, t):
# define constants first
i = x[0]
k = x[1]
L = 10e-3 # 10 mHy
C = 1.56e-6 # 1.56 uF
R1 = 1000 # 1 kOhm
R2 = 100 # 100 Ohm
# define symbols (used to find derivatives)
i_symbol = sp.Symbol('i')
t_symbol = sp.Symbol('t')
# Data (differentiate and evaluate)
E = sp.ln(t_symbol + 1)
dE_dt = E.diff(t_symbol).replace(t_symbol, t)
R = R1 * i_symbol + R2 * i_symbol ** 3
dR_di = R.diff(i_symbol).replace(i_symbol, i)
# nothing should contain symbols from here onwards
# variables can however contain sympy expressions
# Model (convert sympy expressions to floats)
di_dt = float(k)
dk_dt = float(1 / L * dE_dt - dR_di / L * k - 1 / (L * C) * i)
dx_dt = np.array([di_dt, dk_dt])
return dx_dt
# init cond:
x0 = np.array([0, 0])
# time points:
time = np.linspace(0, 30, 1000)
# solve ODE:
solution = odeint(RLC, x0, time)
result = solution[:, 0]
print(result)
Just something to note: the value i = x[0] seemed to sit very close to 0 throughout each iteration. This means dR_di stayed basically at 1000 the whole time. I'm not familiar with odeint or your specific ODE, but hopefully this phenomenon is expected and isn't a problem.
I Want to solve a system of three differential equation, changes the initial conditions
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
#__________________________Condiciones Problema ________________________
#Constants that are use in the problem
Yxs = 0.06
Yxp = 0.06
Umax = 0.24
Ks = 1.6
Cpm = 100
n = 2
#Function to solve de ODE
def fermentation_process(C,t):
Cx , Cs , Cp = C
U = (Umax*((Cs/(Ks+Cs))*(1-(Cp/Cpm)))**n)
rx = U*Cx
rs = -(1/Yxs)*(rx)
rp = (1/Yxp)*(rx)
dCx_dt = rx
dCs_dt = rs
dCp_dt = rp
return [dCx_dt,dCs_dt,dCp_dt]
#________________________Initial condition_____________________
Cx0 = 0.1
Cp0 = 0
#________________________Iterable intial Condition______
I save the initial conditions in a function to use this function in a bucle for
#Fuction to save the inintial conditions
def ini(Cs0):
return np.array([Cx0,Cs0,Cp0])
t_lim = (0,60)
Cs0 = 0
Bucle for evaluate the variable inintial condition Cs0
for i in range(5):
Cs0 += 10
sol = solve_ivp(fermentation_process, ini(Cs0), t_lim)
Tiempo = sol.t
concentraciones = sol.y
figure, axis = plt.subplots()
plt.plot(Tiempo, concentraciones[0])
plt.plot(Tiempo, concentraciones[2])
plt.show()
The error that I obtain is:
File "C:/Users/Paula/.spyder-py3/Sin título0.py", line 50, in
fermentation_process Cx , Cs , Cp = C
TypeError: cannot unpack non-iterable float object
Please update the below function (as we cannot assign an array to multiple variables)
def fermentation_process(C,t):
Cx , Cs , Cp = C[0], C[1], C[2]
(or)
def fermentation_process(C,t):
Cx = C[0]
Cs = C[1]
Cp = C[2]