Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 龙格–;Kutta RK4不比Verlet好吗?_Python_Matplotlib_Runge Kutta_Verlet Integration - Fatal编程技术网

Python 龙格–;Kutta RK4不比Verlet好吗?

Python 龙格–;Kutta RK4不比Verlet好吗?,python,matplotlib,runge-kutta,verlet-integration,Python,Matplotlib,Runge Kutta,Verlet Integration,我只是在游戏中测试几个轨道动力学的积分方案。 我在这里采用了RK4的恒定和自适应步长 我将它与简单的verlet积分(和euler积分)进行了比较,但它的性能非常差。似乎RK4的恒定步长并不比verlet好。带有自适应步长的RK4更好,但不是很多。我想知道我是否做错了什么?或者从哪个意义上说RK4比verlet优越得多 我们的想法是,每RK4步评估4倍的力,但每verlet步仅评估1倍。因此,为了获得相同的性能,我可以为verlet设置小4倍的时间步长。verlet的时间步长比RK4的恒定步长

我只是在游戏中测试几个轨道动力学的积分方案。 我在这里采用了RK4的恒定和自适应步长

我将它与简单的verlet积分(和euler积分)进行了比较,但它的性能非常差。似乎RK4的恒定步长并不比verlet好。带有自适应步长的RK4更好,但不是很多。我想知道我是否做错了什么?或者从哪个意义上说RK4比verlet优越得多

我们的想法是,每RK4步评估4倍的力,但每verlet步仅评估1倍。因此,为了获得相同的性能,我可以为verlet设置小4倍的时间步长。verlet的时间步长比RK4的恒定步长精确4倍,几乎与RK4的addaptive步长相当

如图所示:

10T表示10个轨道周期,以下数字48968792048966是所需的力评估数

python代码(使用pylab)如下所示:

from pylab import * 
import math

G_m1_plus_m2 = 4 * math.pi**2

ForceEvals = 0

def getForce(x,y):
    global ForceEvals
    ForceEvals += 1
    r = math.sqrt( x**2 + y**2 )
    A = - G_m1_plus_m2 / r**3
    return x*A,y*A

def equations(trv):
    x  = trv[0]; y  = trv[1]; vx = trv[2]; vy = trv[3];
    ax,ay = getForce(x,y)
    flow = array([ vx, vy, ax, ay ])
    return flow

def SimpleStep( x, dt, flow ):
    x += dt*flow(x)

def verletStep1( x, dt, flow ):
    ax,ay = getForce(x[0],x[1])
    vx   = x[2] + dt*ax; vy   = x[3] + dt*ay; 
    x[0]+= vx*dt;        x[1]+= vy*dt;
    x[2] = vx;        x[3] = vy;

def RK4_step(x, dt, flow):    # replaces x(t) by x(t + dt)
    k1 = dt * flow(x);     
    x_temp = x + k1 / 2;   k2 = dt * flow(x_temp)
    x_temp = x + k2 / 2;   k3 = dt * flow(x_temp)
    x_temp = x + k3    ;   k4 = dt * flow(x_temp)
    x += (k1 + 2*k2 + 2*k3 + k4) / 6

def RK4_adaptive_step(x, dt, flow, accuracy=1e-6):  # from Numerical Recipes
    SAFETY = 0.9; PGROW = -0.2; PSHRINK = -0.25;  ERRCON = 1.89E-4; TINY = 1.0E-30
    scale = flow(x)
    scale = abs(x) + abs(scale * dt) + TINY
    while True:
        x_half = x.copy();  RK4_step(x_half, dt/2, flow); RK4_step(x_half, dt/2, flow)
        x_full = x.copy();  RK4_step(x_full, dt  , flow)
        Delta = (x_half - x_full)
        error = max( abs(Delta[:] / scale[:]) ) / accuracy
        if error <= 1:
            break;
        dt_temp = SAFETY * dt * error**PSHRINK
        if dt >= 0:
            dt = max(dt_temp, 0.1 * dt)
        else:
            dt = min(dt_temp, 0.1 * dt)
        if abs(dt) == 0.0:
            raise OverflowError("step size underflow")
    if error > ERRCON:
        dt *= SAFETY * error**PGROW
    else:
        dt *= 5
    x[:] = x_half[:] + Delta[:] / 15
    return dt    

def integrate( trv0, dt, F, t_max, method='RK4', accuracy=1e-6 ):
    global ForceEvals
    ForceEvals = 0
    trv = trv0.copy()
    step = 0
    t = 0
    print "integrating with method: ",method," ... "
    while True:
        if method=='RK4adaptive':
            dt = RK4_adaptive_step(trv, dt, equations, accuracy)
        elif method=='RK4':
            RK4_step(trv, dt, equations)
        elif method=='Euler':
            SimpleStep(trv, dt, equations)
        elif method=='Verlet':
            verletStep1(trv, dt, equations)
        step += 1
        t+=dt
        F[:,step] = trv[:]
        if t > t_max:
            break
    print " step = ", step


# ============ MAIN PROGRAM BODY =========================

r_aphelion   = 1
eccentricity = 0.95
a = r_aphelion / (1 + eccentricity)
T = a**1.5
vy0 = math.sqrt(G_m1_plus_m2 * (2 / r_aphelion - 1 / a))
print " Semimajor axis a = ", a, " AU"
print " Period T = ", T, " yr"
print " v_y(0) = ", vy0, " AU/yr"
dt       = 0.0003
accuracy = 0.0001

#                 x        y     vx  vy
trv0 = array([ r_aphelion, 0,    0, vy0 ])             

def testMethod( trv0, dt, fT, n, method, style ):
    print " "
    F = zeros((4,n));
    integrate(trv0, dt, F, T*fT, method, accuracy);
    print "Periods ",fT," ForceEvals ",  ForceEvals
    plot(F[0],F[1], style ,label=method+" "+str(fT)+"T "+str(  ForceEvals ) ); 

testMethod( trv0, dt, 10, 20000  , 'RK4', '-' )
testMethod( trv0, dt, 10, 10000  , 'RK4adaptive', 'o-' )
testMethod( trv0, dt/4, 10, 100000, 'Verlet', '-' )
#testMethod( trv0, dt/160, 2, 1000000, 'Euler', '-' )

legend();
axis("equal")
savefig("kepler.png")
show();
从pylab导入*
输入数学
G_m1_加上_m2=4*math.pi**2
forcevals=0
def getForce(x,y):
全球强制评估
力值+=1
r=数学sqrt(x**2+y**2)
A=-G\u m1\u加上\u m2/r**3
返回x*A,y*A
def方程式(trv):
x=trv[0];y=trv[1];vx=trv[2];vy=trv[3];
ax,ay=getForce(x,y)
流量=阵列([vx、vy、ax、ay])
回流
def SimpleStep(x,dt,流量):
x+=dt*流量(x)
def verletStep1(x、dt、流量):
ax,ay=getForce(x[0],x[1])
vx=x[2]+dt*ax;vy=x[3]+dt*ay;
x[0]+=vx*dt;x[1]+=vy*dt;
x[2]=vx;x[3]=vy;
def RK4_步骤(x,dt,flow):#用x(t+dt)替换x(t)
k1=dt*流量(x);
x_温度=x+k1/2;k2=dt*流量(x_温度)
x_温度=x+k2/2;k3=dt*流量(x_温度)
x_温度=x+k3;k4=dt*流量(x_温度)
x+=(k1+2*k2+2*k3+k4)/6
def RK4_自适应步骤(x,dt,流量,精度=1e-6):#来自数值配方
安全系数=0.9;PGROW=-0.2;PSHRINK=-0.25;ERRCON=1.89E-4;微小=1.0E-30
比例=流量(x)
刻度=绝对值(x)+绝对值(刻度*dt)+极小值
尽管如此:
x_half=x.copy();RK4_阶跃(x_半,dt/2,流量);RK4_阶跃(x_半,dt/2,流量)
x_full=x.copy();RK4_阶跃(x_满、dt、流量)
增量=(x_半-x_全)
误差=最大值(绝对值(增量[:])/刻度[:])/精度
如果错误=0:
dt=最大值(dt_温度,0.1*dt)
其他:
dt=最小值(dt_温度,0.1*dt)
如果abs(dt)==0.0:
提升溢流阀错误(“步长下溢”)
如果错误>ERRCON:
dt*=安全*错误**PGROW
其他:
dt*=5
x[:]=x_半[:]+δ[:]/15
返回dt
def积分(trv0,dt,F,t_max,方法='RK4',精度=1e-6):
全球强制评估
forcevals=0
trv=trv0.copy()
步长=0
t=0
打印“与方法集成:,方法,…”
尽管如此:
如果方法=='RK4adaptive':
dt=RK4_自适应_步长(trv,dt,方程,精度)
elif方法=='RK4':
RK4_阶跃(trv,dt,方程)
elif方法=='Euler':
SimpleStep(trv,dt,方程)
elif方法=='Verlet':
verletStep1(trv、dt、方程式)
步骤+=1
t+=dt
F[:,步长]=trv[:]
如果t>t_max:
打破
打印“步骤=”,步骤
#================主程序体=========================
r_aphelion=1
偏心率=0.95
a=r_aphelion/(1+偏心率)
T=a**1.5
vy0=数学sqrt(G_m1_加上_m2*(2/r_aphelion-1/a))
打印“半长轴a=”,a,“AU”
打印“期间T=,T,“年”
打印“v_y(0)=”,vy0,“AU/yr”
dt=0.0003
精度=0.0001
#x y vx vy
trv0=数组([r\u aphelion,0,0,vy0])
def测试方法(trv0、dt、fT、n、方法、样式):
打印“”
F=零((4,n));
积分(trv0、dt、F、T*fT、方法、精度);
打印“期间”,英尺,“强制评估”,强制评估
绘图(F[0],F[1],样式,标签=方法+“”+str(fT)+“T”+str(forcevals));
测试方法(trv0,dt,10,20000,'RK4','-')
测试方法(trv0,dt,10,10000,'RK4adaptive','o-')
测试方法(trv0,dt/4,10,100000,'Verlet','-')
#试验方法(trv0,dt/160,210000,'欧拉','-')
图例();
轴(“相等”)
savefig(“kepler.png”)
show();

我不知道是否要回答您的具体问题,但以下是我的想法

您已经定义了一个非常简单的力模型。在这种情况下,保存某些步骤可能不会提高性能,因为在RK4中计算新步骤可能需要更长的时间。如果力模型更复杂,带有自适应步长的RK4可能会为您节省大量时间。从你的情节来看,我认为Verlet也偏离了正确的解决方案,一个重复的椭圆

对于轨道力学,您也可以尝试RK7(8)自适应积分器、Adams-Bashforth多步或Gauss-Jackson方法。以下是一篇文章,展示了其中的一些方法:


最后,如果你的力模型总是一个简单的中心力,就像这个例子,看看开普勒方程。解决它是精确的,快速的,你可以跳到任意时间

好的,最后,我使用了自适应Runge-Kutta-Fehlberg(RKF45)。
有趣的是,当我要求更高的精度(最佳值为1E-9)时,它会更快(需要更少的步长),因为在较低的精度下(我知道这个问题现在已经很老了,但这实际上与其中一种方法相对于另一种方法的“优势”无关,或者与你对它们的编程无关——它们只是擅长不同的事情。(所以不,这个答案不是关于代码,甚至不是关于编程。更多的是关于数学,真的…)

Runge-Kutta解算器家族非常擅长以相当高的精度处理几乎任何问题,对于自适应方法而言,其性能也非常好。然而,它们并非如此,这意味着它们不会在问题中节约能量

另一方面,Verlet方法可能是r