如何使用scipy.integrate设置固定步长?

时间:2019-02-02 15:59:02

标签: python scipy integrate runge-kutta

我正在寻找一种设置固定步长的方法,以通过Python中的Runge-Kutta方法解决我的初值问题。因此,如何告诉scipy.integrate.RK45对其集成过程进行不断的更新(步长)?

非常感谢您。

6 个答案:

答案 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阶方法,因为误差系数的图形非常接近。

enter image description here

答案 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。