使用 scipy.integrate.odeint 解决颂歌系统(不断变化的常数!)?

Posted

技术标签:

【中文标题】使用 scipy.integrate.odeint 解决颂歌系统(不断变化的常数!)?【英文标题】:Solving a system of odes (with changing constant!) using scipy.integrate.odeint? 【发布时间】:2016-01-04 09:23:18 【问题描述】:

我目前有一个带有时间相关常数的颂歌系统。例如

def fun(u, t, a, b, c):
    x = u[0]
    y = u[1]
    z = u[2]
    dx_dt = a * x + y * z
    dy_dt = b * (y-z)
    dz_dt = -x*y+c*y-z
    return [dx_dt, dy_dt, dz_dt]

常量是“a”、“b”和“c”。我目前有每个时间步长的“a”列表,我想在每个时间步长插入,当使用 scipy ode 求解器时……这可能吗?

谢谢!

【问题讨论】:

【参考方案1】:

是的,这是可能的。在a 是常量的情况下,我猜你叫scipy.integrate.odeint(fun, u0, t, args) 其中fun 在你的问题中被定义,u0 = [x0, y0, z0] 是初始条件,t 是要解决的时间点序列ODE 和args = (a, b, c) 是要传递给fun 的额外参数。

a 取决于时间的情况下,您只需将a 重新考虑为一个函数,例如(给定一个常量a0):

def a(t):
    return a0 * t

然后您将不得不修改 fun,它计算每个时间步的导数,以将先前的更改考虑在内:

def fun(u, t, a, b, c):
    x = u[0]
    y = u[1]
    z = u[2]
    dx_dt = a(t) * x + y * z # A change on this line: a -> a(t)
    dy_dt = b * (y - z)
    dz_dt = - x * y + c * y - z
    return [dx_dt, dy_dt, dz_dt]

最后,请注意u0targs 保持不变,您可以再次调用scipy.integrate.odeint(fun, u0, t, args)

关于这种方法的正确性的一句话。数值积分近似的性能受到影响,我不知道具体如何(没有理论上的保证),但这是一个有效的简单示例:

import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
import scipy.integrate

tmax = 10.0

def a(t):
    if t < tmax / 2.0:
        return ((tmax / 2.0) - t) / (tmax / 2.0)
    else:
        return 1.0

def func(x, t, a):
    return - (x - a(t))

x0 = 0.8
t = np.linspace(0.0, tmax, 1000)
args = (a,)
y = sp.integrate.odeint(func, x0, t, args)

fig = plt.figure()
ax = fig.add_subplot(111)
h1, = ax.plot(t, y)
h2, = ax.plot(t, [a(s) for s in t])
ax.legend([h1, h2], ["y", "a"])
ax.set_xlabel("t")
ax.grid()
plt.show()

我希望这会对你有所帮助。

【讨论】:

在扭结的情况下,平均结果(对于固定步长的积分器)将具有全局误差 O(dt),与欧拉方法相同。使用自适应步长,扭结是一个无限曲率的点,将步长减小到救助点(从而增加步数,从而增加浮点错误的累积)。一个扭结不会产生明显的影响。【参考方案2】:

不,这在字面意义上是不可能的

“我目前有一个每个时间步的“a”列表,我想在每个时间步插入”

由于求解器具有自适应步长控制,也就是说,它将使用您无法控制的内部时间步长,并且每个时间步长使用函数的多个评估。因此求解器时间步长和数据时间步长之间没有联系。

然而,在给定数据定义分段常数阶跃函数的扩展意义上,有几种方法可以得到解决方案。

    您可以使用 ODE 函数和该时间段的常量参数从一个跳转点到另一个跳转点进行积分。之后使用像 concatenate 这样的 numpy 数组操作来组装完整的解决方案。

    您可以使用numpy.interpscipy.interpolate.interp1d 等插值函数。第一个给出分段线性插值,这里可能不需要。第二个返回一个函数对象,可以配置为“零阶保持”,即分段常数阶跃函数。

    您可以实现自己的逻辑,从时间t 到这些参数的正确值。这主要适用于数据具有某种结构的情况,例如,如果它们具有f(int(t/h)) 的形式。

请注意,数值积分的近似阶不仅受限于 RK (solve_ivp) 或多步 (odeint) 方法的阶,还受限于微分方程(部分)的可微阶。如果 ODE 的平滑度远低于方法的阶数,则违反了步长控制机制的隐含假设,这可能导致步长非常小,需要大量的积分步骤。

【讨论】:

实际上,这是可能的(参见我的回答)。顺便说一句,我不确定scipy.integrate.odeint 在内部使用 Runge-Kutta。我认为它更像是线性多步方法(Adams 方法和后向微分公式方法)等方法的混合,它们比 Runge-Kutta 方法更强大。 这似乎对应于分段常数的情况。 感谢您的回答,实际上我有 sae 问题,并且我对时间有分段线性依赖性。你提到它可以被插值。您能否提供有关该案例的更多详细信息? @Ibrahimzawra :您有函数 numpy.interp 进行立即分段插值,scipy.interpolation.interp1d 返回可以在各种模式下生成的插值函数,包括零阶保持,即,基于提供的时间值数据的分段常数函数。【参考方案3】:

我也遇到过类似的问题。就我而言,参数a, b,c 与时间没有直接关系,而是由当时的x, y,z 确定。所以我必须在时间t 得到x, y, z,并计算a, b, c 用于x, y, zt+dt 的积分计算。事实证明,如果我更改dt 的值,整个集成结果会发生巨大变化,甚至会变得不合理。

【讨论】:

这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review 或者至少,就@camille 而言,如果这是一个答案,我很难理解它。听起来您更像是遇到了类似的问题,尝试了一些事情,并提供了有关您遇到问题的其他信息。我是不是误会了?如果是这样,编辑您的答案以增加清晰度会很有用。

以上是关于使用 scipy.integrate.odeint 解决颂歌系统(不断变化的常数!)?的主要内容,如果未能解决你的问题,请参考以下文章

使用 scipy.integrate.odeint 解决颂歌系统(不断变化的常数!)?

如何使用 scipy.integrate.odeint 求解具有时间相关变量的 ODE 系统

scipy.integrate.odeint可以计算矩阵的微分方程吗

多个 scipy.integrate.ode 实例

python 的scipy 里的 odeint 这个求微分方程的函数怎么用啊

为solve_ivp 传递参数(新的SciPy ODE API)