我编写了一个使用odeint来解决微分方程的程序。但它有一个问题。当我将Cosmopara设置为np.array([70.0,0.3,0,-1.0,0])
时,它会在invalid value encountered in sqrt
中发出invalid value encountered in double_scalars
和h = np.sqrt(y1 ** 2 + Omega_M * t ** (-3) + Omega_DE*y2)
的警告。但我检查了那条线并没有发现任何错误。如果Cosmopara = np.array([70.0,0.3,0.0,-1.0,0.0])
,Y不应该改变,但它会改变。此外,如果我选择Cosmopara = np.array([70.0,0.3,0.1,-1.0,0.1])
,这个程序可以给出正确的结果。
我做错了什么?
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
global CosmoPara
Cosmopara = np.array([70.0,0.3,0.0,-1.0,0.0])
def derivfun(Y,t):
Omega_M = Cosmopara[1]
Sigma_0 = Cosmopara[2]
omega = Cosmopara[3]
delta = Cosmopara[4]
Omega_DE = 1-Omega_M-Sigma_0**2
y1 = Y[0]
y2 = Y[1]
h = np.sqrt(y1**2 + Omega_M*t**(-3) + Omega_DE*y2)
dy1dt = -3.0*y1/t + (delta*Omega_DE*y2)/(t*h)
dy2dt = -(3.0*(1+omega) + 2.0*delta*y1/h)*y2/t
return np.array([dy1dt,dy2dt])
z = np.linspace(1,2.5,15001)
time = 1.0/z
Omega_M = Cosmopara[1]
Sigma_0 = Cosmopara[2]
omega = Cosmopara[3]
delta = Cosmopara[4]
Omega_DE = 1-Omega_M-Sigma_0**2
y1init = Sigma_0
y2init = 1.0
Yinit = np.array([y1init,y2init])
Y = odeint(derivfun,Yinit,time)
y1 = Y[:,0]
y2 = Y[:,1]
h = np.sqrt(y1**2 + Omega_M*time**(-3) + Omega_DE*y2)
plt.figure()
plt.plot(z,h)
plt.show()
答案 0 :(得分:0)
sqrt()
中的值小于0时由于情况导致的错误。sqrt()
中的值小于0是由时间值t
引起的突然减少到-0.0000999
。
<强>原因强>
我已经测试了几个案例以找出原因,我发现赋予函数t
的时间值derivfun
的时间间隔和全局数组{{1}的时间间隔即使函数time
工作正常,它们也是不同的。我想这是一种加速odeint
的机制。因为当导数odeint
和dydt1
不会快速变化时,结果可以被视为具有更大时间间隔的线性函数。如果导数不会快速变化,则此函数将增加下一步的时间间隔,并且该函数可以在更少的步骤中解决它。在你提供的情况下。衍生物dydt2
和dydt1
总是等于零,不会改变。这种情况会导致错误时间值。
基于这个结果,我们可以知道函数dydt2
将给出错误的时间值,该值不在数组用户在导数不变化时给出的时间范围内。如果要避免这种情况,可以使用全局时间变量而不是原始时间值。但是当你使用odeint
求解常微分方程时,它将花费更多的时间。
<强>代码强>
以下代码是您提供的代码,我添加了一些行进行测试。您可以更改参数并查看结果。
odeint
测试结果
下图显示当函数正常工作时(使用您提供的参数),函数中的时间值和全局时间值具有不同的时间间隔:
下图显示当衍生物import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
Cosmopara = np.array([70.0,0.3,0.1,-1.0,0.1])
i = 0
def derivfun(Y,t):
global i
Omega_M = Cosmopara[1]
Sigma_0 = Cosmopara[2]
omega = Cosmopara[3]
delta = Cosmopara[4]
Omega_DE = 1-Omega_M-Sigma_0**2
y1 = Y[0]
y2 = Y[1]
h = np.sqrt(y1**2 + Omega_M*t**(-3) + Omega_DE*y2)
dy1dt = -3.0*y1/t + (delta*Omega_DE*y2)/(t*h)
dy2dt = -(3.0*(1+omega) + 2.0*delta*y1/h)*y2/t
print "Time: %14.12f / Global time: %14.12f / dy1dt: %8.5f / dy2dt: %8.5f" % (t, time[i], dy1dt, dy2dt)
i += 1
return np.array([dy1dt,dy2dt])
z = np.linspace(1,2.5,15001)
time = 1.0/z
Omega_M = Cosmopara[1]
Sigma_0 = Cosmopara[2]
omega = Cosmopara[3]
delta = Cosmopara[4]
Omega_DE = 1-Omega_M-Sigma_0**2
y1init = Sigma_0
y2init = 1.0
Yinit = np.array([y1init,y2init])
Y = odeint(derivfun,Yinit,time)
y1 = Y[:,0]
y2 = Y[:,1]
h = np.sqrt(y1**2 + Omega_M*time**(-3) + Omega_DE*y2)
plt.figure()
plt.plot(z,h)
plt.show()
和dydt1
没有改变时(使用您提供的参数),并且函数中的时间值突然降低到小于{的值{1}}:
答案 1 :(得分:0)
我有一个类似的问题,它似乎引起了人们的注意,因为如前所述,odeint程序似乎想通过增加步长来捷径。我通过限制步长来自己解决此问题。您可以很容易地做到这一点:
SELECT
CASE WHEN cat_name IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN dog_name IS NOT NULL THEN 1 ELSE 0 END AS cat_dog_total
FROM table
这确实使计算时间变慢了很多,因此对于大计算和长时间范围,如果可能的话,您应该避免使用它。我发现它也可以减小时间范围,但这并不总是可能的。 (对于我来说,我想做一个2000-20000秒的时间范围,但是在值到处都是之前,我不能超过200秒)