Runge–Kutta RK4 nicht besser als Verlet?
Ich Teste gerade mehrere Integrationsverfahren für orbital Dynamik im Spiel.
Ich nahm RK4 mit Konstante und adaptive Schritt hier
http://www.physics.buffalo.edu/phy410-505/2011/topic2/app1/index.html
und ich verglich es mit einfachen verlet-integration (und euler, aber es hat sehr sehr schlechte Leistung). Es scheint nicht, dass RK4 mit konstantem Schritt ist besser als verlet. RK4 mit adaptive Schritt ist besser, aber nicht so viel. Ich Frage mich, ob ich was falsch mache? Oder in welchem Sinn es wird gesagt, dass RK4 ist viel besser zu verlet?
Der glaube ist die Kraft ausgewertet wird 4x pro RK4 Schritt, aber nur 1x pro verlet-Schritt. So bekommen die gleiche Leistung kann ich time_step 4x für kleinere verlet. Mit 4x kleiner Zeitschritt verlet-ist präziser als RK4 mit konstantem Schritt und fast vergleichbar mit RK4 mit addaptive Schritt.
Siehe Bild:
https://lh4.googleusercontent.com/-I4wWQYV6o4g/UW5pK93WPVI/AAAAAAAAA7I/PHSsp2nEjx0/s800/kepler.png
10T bedeutet 10 Umlaufzeiten, die folgende Anzahl 48968,7920,48966 ist die Anzahl der Kraft Auswertungen benötigt
den python-code (mit pylab) ist folgende:
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();
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich weiß nicht, ob ich werde beantworten Ihre individuellen Fragen, aber hier sind meine Gedanken.
Den Sie definiert haben, eine sehr einfache Kraft-Modell. In diesem Fall speichern einige Schritte können sich nicht verbessern die Leistung, weil die Berechnung des neuen Schritt in RK4 länger dauern kann. Wenn die Kraft-Modell ist komplexer, RK4 mit adaptive Schritt kann sparen Sie viel Zeit. Von Ihrem Grundstück, ich denke, dass die Verlet-auch abweichend von der korrekten Lösung, eine Wiederholung ellipse.
Für orbital-mechanik können Sie auch versuchen, die RK7(8) adaptive integrator, der Adams-Bashforth multistep-oder die Gauss-Jackson-Methode. Hier ist ein Papier zeigen einige dieser Methoden: http://drum.lib.umd.edu/bitstream/1903/2202/7/2004-berry-healy-jas.pdf
Und schließlich, wenn Sie Ihre Kraft-Modell wird immer eine einfache zentrale Kraft, wie in diesem Beispiel, werfen Sie einen Blick auf die Kepler-Gleichung. Die Lösung ist präzise, schnell und springen Sie zu einer beliebigen Zeit.
Ich weiß, diese Frage ist Recht alt inzwischen, aber das hat wirklich nichts mit der "überlegenheit" einer dieser Methoden über die andere, oder mit der Programmierung von Ihnen - Sie sind einfach nur gut an verschiedene Dinge. (Also Nein, diese Antwort nicht wirklich zum code. Oder auch über die Programmierung. Es ist mehr über die Mathematik, wirklich...)
Des Runge-Kutta-Familie von Löser sind ziemlich gut angreifen fast jedes problem mit sehr guter Präzision und, im Falle der adaptiven Verfahren, Leistung. Sie sind jedoch nicht symplectic, was in Kürze bedeutet, dass Sie keinen sparen von Energie in das problem.
Die Verlet-Methode auf der anderen Seite, erfordern eine viel kleinere Schrittweite als die RK-Methoden zur Minimierung von Schwingungen in der Lösung, aber die Methode ist symplectic.
Dein problem ist die Energie-Erhaltung; nach einer beliebigen Anzahl von umdrehungen, die planetarische Körper die gesamte Energie (kinetische + potentielle) sollte die gleiche sein, wie es war mit den ursprünglichen Bedingungen. Dies gilt mit einer Verlet-integrator (zumindest als Zeit-Fenster-Durchschnitt), aber es wird nicht mit einem integrator von der RK-Familie - über-Zeit -, die RK-Löser wird aufgebaut ein Fehler, dass wird nicht verringert, durch die Energie verloren und der numerischen integration.
Um dies zu überprüfen, versuchen Sie, die gesamte Energie in das system zu jeder Zeit Schritt, und zeichnen Sie es (haben Sie vielleicht zu tun, viel mehr als zehn umdrehungen den Unterschied bemerken). Die RK-Methode wird kontinuierlich den Energieverbrauch zu senken, während die Verlet-Methode gibt eine Schwingung um eine Konstante Energie.
Wenn das ist das genaue problem, das Sie lösen müssen, empfehle ich auch die Kepler-Gleichungen, die dieses problem lösen, analytisch. Auch für sehr komplexe Systeme mit vielen Planeten, Monde usw., die interplanetaren Interaktionen sind so unbedeutend, dass Sie verwenden können, die Kepler Gleichungen für jeden Körper und es ist Rotations-center individuell, ohne viel Verlust an Präzision. Allerdings, wenn du schreibst ein Spiel, das Sie vielleicht wirklich interessiert ist ein anderes problem, und das war nur ein Beispiel, um zu lernen - in diesem Fall, informieren Sie sich über die Eigenschaften der verschiedenen solver Familien, und wählen Sie eine, die geeignet ist für Ihre Probleme.
OK, endlich habe ich verwendet adaptive Runge-Kutta-Fehlberg (RKF45).
Interessanterweise ist es schneller (weniger Schritt notwendig ist), wenn ich ihn für höhere Genauigkeit ( optimal ist 1E-9 ), da bei niedrigeren Genauigkeit ( <1e-6 ) die Lösung ist instabil, und viele Iterationen sind verschwendet auslassen Schritte ( die Schritte wich, waren zu lang und ungenau ). Wenn ich Frage für eine noch bessere Präzision ( 1E-12 ) es requres weitere Schritte, weil die Zeit, die Schritte sind kürzer.
Für kreisförmige Umlaufbahnen, die Kommazahl kann decreesed bis ( 1e-5 ) mit bis zu 3x Geschwindigkeit gewinnen, allerdings brauche ich zwar simulierter sehr exzentrische (elliptische Bahnen) will ich lieber behalten sichern 1E-9 Kommazahl.
Dort ist der code falls jemand ähnliches problem lösen
http://www.openprocessing.org/sketch/96977
es zeigt auch, wie viele Kraft Auswertungen benötigt, um zu simulieren eine Zeiteinheit von trajectrory Länge