我正在寻找一种设置固定步长的方法,以通过Python中的Runge-Kutta方法解决我的初值问题。因此,如何告诉scipy.integrate.RK45
对其集成过程进行不断的更新(步长)?
非常感谢您。
答案 0 :(得分:2)
通过查看the implementation of the step,您会发现最好的方法是通过设置属性{{1}来控制初始步长(在最小步长和最大步长设置的范围内) },然后再致电h_abs
:
RK45.step
答案 1 :(得分:1)
Scipy.integrate通常与可变步长方法一起使用,方法是在数值积分的同时控制TOL(一步误差)。 TOL通常通过使用另一种数值方法进行检查来计算。例如,RK45使用5阶Runge-Kutta来检查4阶Runge-Kutta方法的TOL以确定积分步骤。
因此,如果必须以固定步长集成ODE,只需通过将atol和rtol设置为较大的常数来关闭TOL检查。例如,如下形式:
solve_ivp(your function, t_span=[0, 10], y0=..., method="RK45", max_step=0.01, atol = 1, rtol = 1)
TOL检查设置得太大,以至于积分步骤就是您选择的max_step。
答案 2 :(得分:1)
您已经说过,您想要一个固定时间步长的行为,而不仅仅是固定的评估时间步长。因此,如果您不想自己重新实现求解器,则必须“破解”您的方法。只需将积分公差atol和rtol设置为1e90,并将max_step和first_step设置为要使用的时间步长的值dt 。这样,估计的积分误差将始终很小,从而使求解器无法动态缩小时间步长。
但是,仅将此技巧用于EXPLICIT算法(RK23,RK45,DOP853)! 来自“ solve_ivp”的隐式算法(Radau,BDF,也许还有LSODA)会根据atol和rtol调整非线性牛顿求解器的精度,因此您最终可能会得到一个没有任何意义的解决方案... >
答案 3 :(得分:0)
使用Dormand-Prince RK45方法为Butcher表编写代码非常容易。
0
1/5 | 1/5
3/10 | 3/40 9/40
4/5 | 44/45 −56/15 32/9
8/9 | 19372/6561 −25360/2187 64448/6561 −212/729
1 | 9017/3168 −355/33 46732/5247 49/176 −5103/18656
1 | 35/384 0 500/1113 125/192 −2187/6784 11/84
-----------------------------------------------------------------------------------------
| 35/384 0 500/1113 125/192 −2187/6784 11/84 0
| 5179/57600 0 7571/16695 393/640 −92097/339200 187/2100 1/40
首先进入函数
def DoPri45Step(f,t,x,h):
k1 = f(t,x) ;
k2 = f(t + 1./5*h, x + h*(1./5*k1) ) ;
k3 = f(t + 3./10*h, x + h*(3./40*k1 + 9./40*k2) ) ;
k4 = f(t + 4./5*h, x + h*(44./45*k1 - 56./15*k2 + 32./9*k3) ) ;
k5 = f(t + 8./9*h, x + h*(19372./6561*k1 - 25360./2187*k2 + 64448./6561*k3 - 212./729*k4) ) ;
k6 = f(t + h, x + h*(9017./3168*k1 - 355./33*k2 + 46732./5247*k3 + 49./176*k4 - 5103./18656*k5) )
v5 = 35./384*k1 + 500./1113*k3 + 125./192*k4 - 2187./6784*k5 + 11./84*k6;
k7 = f(t + h, x + h*v5);
v4 = 5179./57600*k1 + 7571./16695*k3 + 393./640*k4 - 92097./339200*k5 + 187./2100*k6 + 1./40*k7;
return v4,v5
,然后在标准的固定步长循环中
def DoPri45integrate(f, t, x0):
N=len(t);
x = np.asarray(N*[x0]);
for k in range(N-1):
v4, v5 = DoPri45Step(f,t[k],x[k],t[k+1]-t[k])
x[k+1] = x[k] + (t[k+1]-t[k])*v5
return x
然后使用已知的确切解决方案y(t)=sin(t)
def mms_ode(t,y): return np.array([ y[1], sin(sin(t))-sin(t)-sin(y[0]) ])
mms_x0 = [0.0, 1.0]
并绘制由h^5
缩放的误差
for h in [0.2, 0.1, 0.08, 0.05, 0.01][::-1]:
t = np.arange(0,20,h);
y = DoPri45integrate(mms_ode,t,mms_x0)
plt.plot(t, (y[:,0]-np.sin(t))/h**5, 'o', ms=3, label = "h=%.4f"%h);
plt.grid(); plt.legend(); plt.show()
确认这确实是5阶方法,因为误差系数的图形非常接近。
答案 4 :(得分:0)
如果您对基于数据的修复步长感兴趣,那么我强烈建议您使用scipy.integrate.solve_ivp
函数及其t_eval
参数。
此函数将所有scipy.integrate
的ode求解器包装到一个函数中,因此您必须通过为其method
自变量赋值来选择方法。幸运的是,默认方法是RK45,因此您不必为此烦恼。
对您来说更有趣的是t_eval
参数,您必须在其中给出一个平面数组。函数以t_eval
值采样解曲线,仅返回这些点。因此,如果要按步长大小进行均匀采样,则只需给t_eval
自变量指定以下内容:numpy.linspace(t0, tf, samplingResolution)
,其中t0是模拟的开始,tf是模拟的结束。
因此,您可以进行统一采样,而不必求助于某些ODE不稳定的固定步长。
答案 5 :(得分:0)
建议自己用py编写rk4定步程序。有许多互联网示例可以提供帮助。这保证您准确地知道每个值是如何计算的。此外,通常不会有 0/0 计算,如果有,它们将很容易跟踪并提示再次查看正在求解的 ode。