odeint程序中的错误

时间:2015-04-25 13:18:41

标签: python scipy odeint

我编写了一个使用odeint来解决微分方程的程序。但它有一个问题。当我将Cosmopara设置为np.array([70.0,0.3,0,-1.0,0])时,它会在invalid value encountered in sqrt中发出invalid value encountered in double_scalarsh = 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()

2 个答案:

答案 0 :(得分:0)

sqrt()中的值小于0时由于情况导致的错误。sqrt()中的值小于0是由时间值t引起的突然减少到-0.0000999

  

<强>原因

我已经测试了几个案例以找出原因,我发现赋予函数t的时间值derivfun的时间间隔和全局数组{{1}的时间间隔即使函数time工作正常,它们也是不同的。我想这是一种加速odeint的机制。因为当导数odeintdydt1不会快速变化时,结果可以被视为具有更大时间间隔的线性函数。如果导数不会快速变化,则此函数将增加下一步的时间间隔,并且该函数可以在更少的步骤中解决它。在你提供的情况下。衍生物dydt2dydt1总是等于零,不会改变。这种情况会导致错误时间值。

基于这个结果,我们可以知道函数dydt2将给出错误的时间值,该值不在数组用户在导数不变化时给出的时间范围内。如果要避免这种情况,可以使用全局时间变量而不是原始时间值。但是当你使用odeint求解常微分方程时,它将花费更多的时间。

  

<强>代码

以下代码是您提供的代码,我添加了一些行进行测试。您可以更改参数并查看结果。

odeint
  

测试结果

下图显示当函数正常工作时(使用您提供的参数),函数中的时间值和全局时间值具有不同的时间间隔: enter image description here

下图显示当衍生物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}}: enter image description here

答案 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秒)