我正在寻找一种方法,在Python中使用Runge-Kutta方法解决初始值问题时设置固定的步长。因此,我该如何告诉
scipy.integrate.RK45
保持其积分过程的恒定更新(步长)?非常感谢。scipy.integrate.RK45
保持其积分过程的恒定更新(步长)?非常感谢。Scipy.integrate通常通过控制误差TOL(一步误差)来使用可变步长方法进行数值积分。通常通过与另一种数值方法进行比较来计算TOL。例如,RK45使用第5阶Runge-Kutta方法来检查第4阶Runge-Kutta方法的TOL以确定积分步骤。
因此,如果必须使用固定步长积分ODEs,则只需通过将atol,rtol设置为相当大的常量来关闭TOL检查。例如,如下形式:
solve_ivp(您的函数,t_span = [0, 10],y0 = ...,method =“RK45”,max_step = 0.01,atol = 1,rtol = 1)
TOL检查被设置为非常大,以便积分步长将是您选择的max_step。
编写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
首先,在单步函数中导入numpy库。
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 = [x0]
for k in range(N-1):
v4, v5 = DoPri45Step(f,t[k],x[k],t[k+1]-t[k])
x.append(x[k] + (t[k+1]-t[k])*v5)
return np.array(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()
DoPri45integrate
中设置数组切片似乎不可能,并且会产生非常恶心的错误。我将代码编辑为更健壮的列表附加。 - AlexNex''-mu*(1-x^2)x'+x=0
吗?你实现了方程eps*x'' - x'(1-x')^2+x=0
。在它的导数y=x'
中,它变成了eps*y''-y'(1-4y+3y^2)+y=0
,经过时间重新调整后,它与VdP有些相似。 - Lutz Lehmannscipy.integrate.solve_ivp
函数及其t_eval
参数。scipy.integrate
ode求解器的功能,因此您必须通过给其method
参数赋值来选择方法。幸运的是,默认方法是RK45,所以您不必担心这个问题。t_eval
参数,在其中您必须提供一个平坦的数组。该函数在t_eval
值处采样解曲线并仅返回这些点。因此,如果您想通过步长进行均匀采样,只需将t_eval
参数设置为:numpy.linspace(t0,tf,samplingResolution)
,其中t0是模拟开始时间,tf是结束时间。
这样,您就可以获得均匀的采样,而无需使用会导致某些ODE不稳定的固定步长。通过查看步骤的实现,您会发现最好的方法就是通过在调用RK45.step
之前设置属性h_abs
来控制初始步长(在最小和最大步长范围内):
In [27]: rk = RK45(lambda t, y: t, 0, [0], 1e6)
In [28]: rk.h_abs = 30
In [29]: rk.step()
In [30]: rk.step_size
Out[30]: 30.0
然而,只有在显式算法(RK23、RK45、DOP853)中才使用这个技巧!"solve_ivp"中的隐式算法(Radau、BDF,可能还包括LSODA)根据atol和rtol调整非线性牛顿求解器的精度,因此您可能会得到一个毫无意义的解...