【问题标题】:System of differential equations with time dependent constants in arrays, using odeint数组中具有时间相关常数的微分方程组,使用 odeint
【发布时间】:2019-07-15 10:32:48
【问题描述】:

假设我有一个微分方程组,我想用 odeint 解决它。系统的一些常数与时间有关,我将它们的值存储在数组中(a,b,c 和 d 形状为 (8000, ) )。我希望系统在每个时间步使用这些常量的不同值。见简化代码示例:

t = np.linspace(0,100,8000)

a = np.array([0, 5, 6, 12, 1.254, ..., 0.145])     # shape (8000, )
b = np.array([1.45, 5.9125, 1.367, ..., 3.1458])
c = np.array([0.124, 0.258, 0.369, ..., 0.147])
d = np.array([7.145, 5.123, 6.321, ..., 0.125])

def system(k,t):
    vcx_i = k[0]
    vcy_i = k[1]
    psi_i = k[2]
    wz_i = k[3]

    vcx_i_dot = a*np.cos(psi_i)-b*np.sin(psi_i)
    vcy_i_dot = b*np.cos(psi_i)+a*np.sin(psi_i)
    psi_i_dot = wz_i
    wz_i_dot = c*vcx_i-a*np.sin(psi_i)-d*np.sin(psi_i)-b*np.cos(psi_i)

    return [vcx_i_dot, vcy_i_dot, psi_i_dot wz_i_dot]

k0 = [0.1257, 0, 0, 0]

k = odeint(system, k0, t)

vcx_i = k[:,0]
vcy_i = k[:,1]
psi_i = k[:,2]
wz_i = k[:,3]

psi_i = [system(t_i, k_i)[2] for k_i, t_i in zip(k,t)]
wz_i = [system(t_i, k_i)[3] for k_i, t_i in zip(k,t)]

到目前为止,我发现的最相关的解决方案是:Solving a system of odes (with changing constant!) using scipy.integrate.odeint? 但是因为我只有数组中的变量值,而不是依赖于时间的变量方程(例如 a=f(t)),所以我尝试在我的数组中的值之间应用插值,如下所示:ODEINT with multiple parameters (time-dependent) 我设法使代码运行没有错误,但总时间急剧增加,系统解决的结果完全错误。我尝试了在这里找到的任何可能的插值类型:https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html,但结果仍然错误。这意味着我的插值不是最好的,或者我在数组中的点(8000 个值)太多,无法在它们之间插值并正确求解系统。解决这样的系统的正确方法是什么? 我是 python 新手,我在 Ubuntu 16.04 LTS 上使用 python 2.7.12。先感谢您。

【问题讨论】:

  • "...和系统解出来的结果完全错了。" 你怎么知道结果错了?此外,如果您不向我们展示您尝试过的代码,我们将无法帮助您找出问题所在。如果您提供minimal, complete and verifiable example,那么有人会更容易帮助您。
  • @WarrenWeckesser 我知道正确的结果是什么样的,因为我已经解决了一个更大的系统,其中包括我现在要解决的系统。结果必须相同。我在这里发布了我的完整代码:stackoverflow.com/questions/54773543/… 但是帖子有点混乱,所以我试图简化上面帖子中的问题。

标签: arrays python-2.7 scipy odeint


【解决方案1】:

插值器通常非常快,因此您的函数中可能还有其他内容。但是,您可以尝试不同的插值器(如 InterpolatedUnivariateSpline),或减少插值节点以提高速度。但我会以你的整合为目标。

最近,odeodeint 已被其他更灵活的功能取代(请参阅here

我将从显式方法开始,而不是隐式方法(solve_ivp 的默认值是 runge kutta,而odeint 的默认值是 LSODA):

interp = scipy.interpolate.interp1d(t,(a,b,c,d))

def system(t,k):
    vcx_i = k[0]
    vcy_i = k[1]
    psi_i = k[2]
    wz_i = k[3]
    a, b, c, d = interp(t)

    vcx_i_dot = a*np.cos(psi_i)-b*np.sin(psi_i)
    vcy_i_dot = b*np.cos(psi_i)+a*np.sin(psi_i)
    psi_i_dot = wz_i
    wz_i_dot = c*vcx_i-a*np.sin(psi_i)-d*np.sin(psi_i)-b*np.cos(psi_i)
    return [vcx_i_dot, vcy_i_dot, psi_i_dot, wz_i_dot]

k0 = [0.1257, 0, 0, 0]
steps = 1
method = 'RK23'
atol = 1e-3
s = solve_ivp(dydt, (0, 100), k0, method=method, t_eval=t, atol=atol, vectorized=True)

你可以增加/减少atol或者改变方法。

【讨论】:

  • 我尝试实现你提到的solve_ivp解决方案,但出现错误:'vcx_i = k[0] TypeError: 'float' object has no attribute 'getitem' '
  • 颠倒system中t和k的顺序(见编辑——我在阅读函数文档时的错误,也不同于odeint
  • 系统看起来可以正常工作,但为了查看结果,我尝试保存输出,就像我对 odeint 所做的那样(请参阅上面的更新代码),我收到错误 `vcx_i = k [:,0] TypeError: unhashable type` odeint和solve_ivp在保存系统结果方面有什么区别吗?
  • 是的。 odeint 只返回结果(y 向量),solve_ivp 返回OdeResult 对象的实例,该对象还存储其他内容(例如初始和最终时间、步骤等)。您想使用s.y 来获取结果向量。
  • 到目前为止一切顺利。我设法打印了结果。现在,假设我想重复同一系统的一部分(请参阅上面的更新代码),就像您在 odeint 中指出的那样:stackoverflow.com/questions/54365358/…。一般的问题都是一样的,不过这次我用的是solve_ivp。我该怎么做?
猜你喜欢
  • 2017-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-28
  • 1970-01-01
  • 2016-09-20
  • 1970-01-01
相关资源
最近更新 更多