scipy库solve_ivp无法完成tspan全区间积分问题求助
问题原因分析
- 微分方程在
t < 5区间无正实平衡点:该区间内所有初值对应的导数dudt恒为负,u会快速下跌到0以下,之后导数绝对值持续为负,解快速趋向负无穷。 solve_ivp默认使用自适应步长的RK45求解器,为了保证计算精度会持续缩小步长匹配解的变化速率,当步长低于求解器最小步长阈值时,会提前终止全区间积分,导致返回的sol.t无法覆盖0到10的完整区间。- 你编写的绘图代码手动过滤了所有
y < 0的结果,即便求解器完成了全区间积分,也只会绘制u首次变负之前的部分,进一步放大了“积分提前停止”的观感。
解决方案
方案1:修改微分方程避免非物理负值
调整导数计算逻辑,限制u<=0时导数为0,避免解爆炸触发步长过小问题,修改后的dudt如下:
def dudt(t, u): du = u*(1-u/12) - 4*np.heaviside(-(t-5), 1) # 当u小于等于0时强制导数为0,避免解趋向负无穷 du[u <= 0] = 0 return du
修改后直接运行即可完成全区间积分,绘图时无需手动过滤负值也能得到正确结果。
方案2:调整求解器参数适配带负值的解
如果业务需要保留负值的计算结果,可以更换适配刚性求解器,同时放宽精度阈值,避免步长提前收缩过小:
sol = solve_ivp( dudt, (0, 10), ic, t_eval=np.linspace(0, 10, 10000), method='Radau', # 更换为适合刚性问题的求解器 atol=1e-6, rtol=1e-3 )
绘图时删除手动过滤负值的逻辑,即可看到完整区间的解曲线。
方案3:添加事件触发终止(可选)
如果需要在u降到0时就终止积分,可以添加事件函数:
def cross_zero(t, u): return u.min() # 任意分量降到0时触发事件 cross_zero.terminal = True cross_zero.direction = -1 sol = solve_ivp(dudt, (0, 10), ic, t_eval=np.linspace(0, 10, 10000), events=cross_zero)
内容的提问来源于stack exchange,提问作者An Ignorant Wanderer
相关产品推荐
相关产品推荐

