Python integrate.ode在我的数据范围之外设置t0值
我想求解ODE dy/dt=-2y+data(t),在t=0..3之间,对于y(t=0)=1 我编写了以下代码:Python integrate.ode在我的数据范围之外设置t0值,python,scipy,ode,Python,Scipy,Ode,我想求解ODE dy/dt=-2y+data(t),在t=0..3之间,对于y(t=0)=1 我编写了以下代码: import numpy as np from scipy.integrate import odeint from scipy.interpolate import interp1d t = np.linspace(0, 3, 4) data = [1, 2, 3, 4] linear_interpolation = interp1d(t, data) def func(y
import numpy as np
from scipy.integrate import odeint
from scipy.interpolate import interp1d
t = np.linspace(0, 3, 4)
data = [1, 2, 3, 4]
linear_interpolation = interp1d(t, data)
def func(y, t0):
print 't0', t0
return -2*y + linear_interpolation(t0)
soln = odeint(func, 1, t)
当我运行此代码时,会出现如下错误:
ValueError:x_new中的值高于插值范围。错误:调用名为的Python函数时出错 func 我的插值范围在0.0和3.0之间。 在func中打印
t0
的值时,我意识到t0
有时实际上高于我的插值范围:3.07634612585,3.0203768998,3.00638459329。。。这就是为什么线性插值(t0)
会引发ValueError
异常
我有几个问题:
如何使integrate.ode
变化?为什么它会使t0
超出我的插值范围的下限(3.0)t0
- 尽管有这些错误,
返回一个似乎包含正确值的数组。那么,我应该抓住并忽略这些错误吗?我是否应该忽略微分方程、integrate.ode
范围和初始条件t
- 如果我不应该忽略这些错误,那么避免它们的最佳方法是什么?2项建议:
- 在
中,我可以设置interp1d
和bounds\u error=False
,因为我的插值范围之外的fill\u value=data[-1]
似乎接近t0
:t[-1]
但首先我想确定,对于任何其他linear_interpolation = interp1d(t, data, bounds_error=False, fill_value=data[-1])
和任何其他func
,数据
将始终保持在t0
附近。例如,如果t[-1]
在我的插值范围下选择了integrate.ode
,则填充值仍然是t0
,这是不正确的。也许知道data[-1]
如何使integrate.ode
变化会帮助我确定这一点(见我的第一个问题)t0
- 在
中,我可以将func
调用包含在try/except块中,当我捕获到linear\u interpolation
时,我会调用ValueError
,但linear\u interpolation
被截断:t0
def func(y, t0): try: interpolated_value = linear_interpolation(t0) except ValueError: interpolated_value = linear_interpolation(int(t0)) # truncate t0 return -2*y + interpolated_value
至少此解决方案允许线性插值在
使integrate.ode
>=4.0或t0
时仍然引发异常,t0
解算器在超过上次请求时间的时间值评估函数是正常的。大多数ODE解算器都是这样工作的——它们采用内部时间步长,其大小由误差控制算法决定,然后在用户要求的时间使用自己的插值来计算解。某些解算器(例如日晷库中的CVODE解算器)允许您指定时间的硬边界,超过该时间,解算器不允许计算公式,但odeint
没有此选项 如果您不介意从odeint
切换到scipy.integrate.odeint
,它看起来像scipy.integrate.ode
和的“dopri5”
解算器在超出请求时间的时候不会对函数进行评估。两个警告:的“dop853”
解算器对定义微分方程的参数顺序使用不同的约定。在ode
解算器中,ode
是第一个参数。(是的,我知道,抱怨,抱怨…)t
和“dopri5”
解算器适用于非刚性系统。如果你的问题是,他们仍然应该给出正确的答案,但他们会比僵硬的解决者做更多的工作“dop853”
重命名为func
rhs
如果简单地使用最后两个数据点来线性扩展插值器太粗糙,那么您必须使用其他方法来外推一点,超出给定给import numpy as np from scipy.integrate import ode from scipy.interpolate import interp1d t = np.linspace(0, 3, 4) data = [1, 2, 3, 4] linear_interpolation = interp1d(t, data) def rhs(t, y): """The "right-hand side" of the differential equation.""" #print 't', t return -2*y + linear_interpolation(t) # Initial condition y0 = 1 solver = ode(rhs).set_integrator("dop853") solver.set_initial_value(y0) k = 0 soln = [y0] while solver.successful() and solver.t < t[-1]: k += 1 solver.integrate(t[k]) soln.append(solver.y) # Convert the list to a numpy array. soln = np.array(soln)
的最终odeint
值 另一种选择是将最后的t
值作为t
的参数,并显式处理大于func
中值的func
值。类似这样的情况,t
是您必须弄清楚的:外推
def func(y, t0, tmax): if t0 > tmax: f = -2*y + extrapolation(t0) else: f = -2*y + linear_interpolation(t0) return f soln = odeint(func, 1, t, args=(t[-1],))
@卡米莱克:我更新了我的答案,提供了一些关于使用
类的信息。请注意,如果你自己做了一个集成循环(ode
在这个答案中,在scipy文档中有一个类似的片段),那么解算器就不在乎了,这意味着(1)它可能会在其他点调用函数(如本答案所述),(2)它可能不会在每次迭代中调用函数一次。特别是,第(2)点意味着函数应该是引用透明的:您不能使用它来更新某些ODE参数。将该参数改为ODE状态数组的一部分。(在遇到那个问题后找到了这个答案)。while solver.successful()和solver.t
def func(y, t0, tmax): if t0 > tmax: f = -2*y + extrapolation(t0) else: f = -2*y + linear_interpolation(t0) return f soln = odeint(func, 1, t, args=(t[-1],))
- 在