Python integrate.ode在我的数据范围之外设置t0值

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

我想求解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, 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
    变化?为什么它会使
    t0
    超出我的插值范围的下限(3.0)

  • 尽管有这些错误,
    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
      中,我可以将
      linear\u interpolation
      调用包含在try/except块中,当我捕获到
      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
      使
      t0
      >=4.0或
      t0
      时仍然引发异常,
      odeint
      解算器在超过上次请求时间的时间值评估函数是正常的。大多数ODE解算器都是这样工作的——它们采用内部时间步长,其大小由误差控制算法决定,然后在用户要求的时间使用自己的插值来计算解。某些解算器(例如日晷库中的CVODE解算器)允许您指定时间的硬边界,超过该时间,解算器不允许计算公式,但
      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
      类的信息。请注意,如果你自己做了一个集成循环(
      while solver.successful()和solver.t
      在这个答案中,在scipy文档中有一个类似的片段),那么解算器就不在乎了,这意味着(1)它可能会在其他点调用函数(如本答案所述),(2)它可能不会在每次迭代中调用函数一次。特别是,第(2)点意味着函数应该是引用透明的:您不能使用它来更新某些ODE参数。将该参数改为ODE状态数组的一部分。(在遇到那个问题后找到了这个答案)。
      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],))