Вот решение, использующее 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 приводит крешение с предельным циклом, для которого работает events
метод 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 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)
Теперь решение: