Python - 将 Kronecker Delta 与 ODEINT 结合使用

Python - Using a Kronecker Delta with ODEINT

我正在尝试使用 Kronecker delta 函数绘制 ODE 的输出,该函数应该只在特定时间 = t1 变为 'active'。 这应该给出类似锯齿的响应,其中初始值呈指数衰减直到 t=t1 ,它在再次衰减之前立即再次上升。 然而,当我绘制它时,看起来求解器将 Kronecker delta 函数视为所有时间 t 的零。在 Python 中有没有办法做到这一点?

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

def dy_dt(y,t):

    dy_dt = 500*KroneckerDelta(t,t1) - 2y

return dy_dt

t1 = 4
y0 = 500
t = np.arrange(0,10,0.1)

y = sp.odeint(dy_dt,y0,t)

plt.plot(t,y)

我认为问题可能是内部舍入错误,因为 0.1 不能精确表示为 python float。我会尝试

import math

def dy_dt(y,t): 
    if math.isclose(t, t1):
        return 500 - 2*y
    else:
        return -2y

odeint 的文档还建议使用 args 参数而不是全局变量来让您的导数函数访问其他参数,并将 np.arange 替换为 np.linspace

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

def dy_dt(y, t, t1): 
    if math.isclose(t, t1):
        return 500 - 2*y
    else:
        return -2*y

t1 = 4 
y0 = 500 
t = np.linspace(0, 10, num=101) 
y = sp.odeint(dy_dt, y0, t, args=(t1,)) 
plt.plot(t, y)

我没有测试代码,如果有什么问题请告诉我。


编辑:

在测试我的代码时,我查看了评估 dy_dtt 值。我注意到 odeint 不仅使用指定的 t 值,而且还稍微改变了它们:

...
3.6636447422787928
3.743098503914526
3.822552265550259
3.902006027185992
3.991829287543431
4.08165254790087
4.171475808258308
...

现在使用我的方法,我们得到

math.isclose(3.991829287543431, 4) # False

因为默认公差设置为最多 10^(-9) 的相对误差,所以 odeint 函数 "misses" 导数的凸起为 4。幸运的是,我们可以通过指定更高的错误阈值来解决这个问题:

def dy_dt(y, t, t1): 
    if math.isclose(t, t1, abs_tol=0.01):
        return 500 - 2*y
    else:
        return -2*y

现在 dy_dt 对于 3.99 到 4.01 之间的所有值都非常高。如果增加 linspacenum 参数,则可以缩小此范围。


TL;DR

你的问题不是python的问题,而是微分方程的数值求解问题:你需要改变你的导数足够长的间隔,否则求解器可能会错过有趣的点。 kronecker delta 不适用于求解 ODE 的数值方法。

在使用时间的简单 Kronecker delta 的情况下,您可以 运行 像这样分段的颂歌:

from scipy.integrate import odeint
import matplotlib.pyplot as plt
import numpy as np

def dy_dt(y,t):
    return -2*y

t_delta = 4
tend = 10
y0 = [500]

t1 = np.linspace(0,t_delta,50)
y1 = odeint(dy_dt,y0,t1)

y0 = y1[-1] + 500  # execute Kronecker delta
t2 = np.linspace(t_delta,tend,50)
y2 = odeint(dy_dt,y0,t2)

t = np.append(t1, t2)
y = np.append(y1, y2)
plt.plot(t,y)

复杂情况下的另一个选择是 solve_ivpevents 功能。