使用 odeint 求解带有阶跃函数参数的 ODE 集

Solving ODE set with a step-function parameter using odeint

我正在尝试用 Python 中的 odeint 对以下系统进行数值求解:

我的问题是 k1 取决于 p 的值。当 p<=0.01 k1=0 否则 k1=5.

我不确定如何编写具有上述约束的系统。我可以开始:

def sys(x,t,flip):
S,T,p = x

dx = [1-T-k1*T,
     0.1*(1-S)-k1*S,
     -0.2*(1-T-k1*T)+0.1*(1-S)-k1*S]
return dx

其中 flip 可以是 0.01 值,但我不确定如何为这种情况实现 if 语句,因为我需要集成过程来检查 [= 的值16=] 每次迭代后。

这是一个使用 solve_ivp 的解决方案:

import numpy as np
import matplotlib.pylab as plt

from scipy.integrate import solve_ivp

def ode(t, TS):
    T, S = TS

    p = -0.2*T + S
    k1 = 0 if p <= 0.01 else 5

    dTdt = 1 - T - k1*T
    dSdt = 0.1*(1 - S) - k1*S

    return dTdt, dSdt

# Solve
TS_start = (0.7, 0.4)
t_span = [0, 3]
sol = solve_ivp(ode, t_span, TS_start, method='RK45',
                rtol=1e-5)
print(sol.message)
print('nbr eval', sol.nfev)

# Graph
plt.plot(sol.t, sol.y[0, :], label='T')
plt.plot(sol.t, sol.y[1, :], label='S'); plt.legend();
plt.xlabel('time');

这似乎是一个很难用数值求解的问题。其他测试的求解器不收敛,使用事件也没有帮助,因为它们在稳态解附近越来越频繁

编辑:确实将翻转值更改为 -0.01 而不是 +0.01 会导致极限循环解决方案,solve_ivpevents 方法正在运行:

import numpy as np
import matplotlib.pylab as plt

from scipy.integrate import solve_ivp

def ode(t, TS):
    T, S = TS

    p = -0.2*T + S
    k1 = 0 if sign_change(t, TS) <= 0 else 5

    dTdt = 1 - T - k1*T
    dSdt = 0.1*(1 - S) - k1*S

    return dTdt, dSdt

def sign_change(t, TS):
    T, S = TS
    p = -0.2*T + S
    flip = -0.01
    return p - flip

# Solve
TS_start = [0.3, 0.1]
t_span = [0, 5]
sol = solve_ivp(ode, t_span, TS_start,
                method='RK45', events=sign_change)
print(sol.message)
print('nbr eval', sol.nfev)

# Graph
plt.plot(sol.t, sol.y[0, :], '-x', label='T')
plt.plot(sol.t, sol.y[1, :], label='S'); plt.legend();
plt.xlabel('time');

for t in sol.t_events[0]:
    plt.axvline(x=t, color='gray', linewidth=1)

现在的解决方案是:

要在积分后重建 k1p 的值,将此计算放入一个额外的函数中

def kp(T,S):
    p = -0.2*T + S
    k1lo, k1hi = 0, 5
    flip = -0.01
    k1 = 0.5*(k1lo+k1hi + (k1hi-k1lo)*np.tanh((p-flip)/1e-8))
    return k1,p

然后在 ODE 导数函数中调用它

def ode(t, TS):
    T, S = TS
    k1,_ = kp(T,S)
    dTdt = 1 - T - k1*T
    dSdt = 0.1*(1 - S) - k1*S
    return dTdt, dSdt

积分后沿解分量绘制

T,S = sol.y;
k,p = k1(T,S);

使用每个评论修正的阈值 flip=-0.01 确实获得了振荡行为。