结果达到阈值时,如何控制odeint停止积分?

时间:2019-06-24 04:40:42

标签: python python-3.x optimization scipy

这是我的代码。

import numpy as np
from scipy.integrate import odeint

#Constant
R0=1.475
gamma=2.
ScaleMeVfm3toEskm3 = 8.92*np.power(10.,-7.)

def EOSe(p):
    return np.power((p/450.785),(1./gamma))

def M(m,r):
    return (4./3.)*np.pi*np.power(r,3.)*p

# function that returns dz/dt
def model(z,r):
    p, m = z
    dpdr = -((R0*EOSe(p)*m)/(np.power(r,2.)))*(1+(p/EOSe(p)))*(1+((4*math.pi*(np.power(r,3))*p)/(m)))*((1-((2*R0)*m)/(r))**(-1.))
    dmdr = 4.*math.pi*(r**2.)*EOSe(p)
    dzdr = [dpdr,dmdr]
    return dzdr

# initial condition
r0=10.**-12.
p0=10**-6.
z0 = [p0, M(r0, p0)]

# radius
r = np.linspace(r0, 15, 100000)

# solve ODE
z = odeint(model,z0,r)

z[:,0]的结果如我所料,一直在下降。但是我想要的只是积极的价值观。可以运行该代码并尝试print(z[69306]),它将显示[2.89636405e-11 5.46983202e-01]。那是我希望odeint停止集成的最后一点。

当然,所提供的代码会显示

RuntimeWarning: invalid value encountered in power
  return np.power((p/450.785),(1./gamma))

因为p的结果开始为负。对于其他点,odeint产生结果[nan nan]

但是,我可以使用np.nanmin()来找到z {:,0]的最小值,而不是nan。但是我的工作有一组p0值。我将需要像这样的循环调用odeint

P=np.linspace(10**-8.,10**-2.,10000)
for p0 in P:
#the code for solving ode provided above.

这需要更多时间。

如果我可以在z [:,0]变为负值之前停下来,那会减少执行时间?

1 个答案:

答案 0 :(得分:0)

这是使用solve_ivp修改的代码:

import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pylab as plt

# Constants
R0 = 1.475
gamma = 2.

def EOSe(p):
    return np.power(np.abs(p)/450.785, 1./gamma)

def M(m, r):
    return (4./3.)*np.pi*np.power(r,3.)*p

# function that returns dz/dt
# note: the argument order is reversed compared to `odeint`
def model(r, z):
    p, m = z
    dpdr = -R0*EOSe(p)*m/r**2*(1 + p/EOSe(p))*(1 + 4*np.pi*r**3*p/m)*(1 - 2*R0*m/r)**(-1)
    dmdr = 4*np.pi * r**2 * EOSe(p)
    dzdr = [dpdr, dmdr]
    return dzdr

# initial condition
r0 = 1e-3
r_max = 50
p0 = 1e-6
z0 = [p0, M(r0, p0)]

# Define the event function
# from the doc: "The solver will find an accurate value
# of t at which event(t, y(t)) = 0 using a root-finding algorithm. "
def stop_condition(r, z):
    return z[0]

stop_condition.terminal = True

# solve ODE
r_span = (r0, r_max)
sol = solve_ivp(model, r_span, z0,
                events=stop_condition)


print(sol.message)
print('last p, m = ', sol.y[:, -1], 'for r_event=', sol.t_events[0][0])

r_sol = sol.t
p_sol = sol.y[0, :]
m_sol = sol.y[1, :]

# Graph
plt.subplot(2, 1, 1);
plt.plot(r_sol, p_sol, '.-b')
plt.xlabel('r'); plt.ylabel('p');

plt.subplot(2, 1, 2);
plt.plot(r_sol, m_sol, '.-r')
plt.xlabel('r'); plt.ylabel('m');

实际上,由于否定p,在这种情况下使用事件不会阻止警告。原因是求解器无论如何都会为p<O评估模型。一种解决方案是取平方根中的p的绝对值(如上面的代码所示)。使用np.sign(p)*np.power(np.abs(p)/450.785, 1./gamma)也会产生有趣的结果。