Jak mogę zastąpić metodę Eulera czwartym rzędem Runge-Kutty, aby określić ruch swobodnego spadku o nie stałej wielkości grawitacyjnej (np. Swobodny spadek z 10 000 km nad ziemią)?
Do tej pory napisałem prostą integrację metodą Eulera:
while()
{
v += getMagnitude(x) * dt;
x += v * dt;
time += dt;
}
x zmienna oznacza aktualną pozycję, v oznacza prędkość, getMagnitude (x) zwraca przyspieszenie na pozycji x.
Próbowałem zaimplementować RK4:
while()
{
v += rk4(x, dt) * dt; // rk4() instead of getMagintude()
x += v * dt;
time += dt;
}
gdzie ciało funkcji rk4 () to:
inline double rk4(double tx, double tdt)
{
double k1 = getMagnitude(tx);
double k2 = getMagnitude(tx + 0.5 * tdt * k1);
double k3 = getMagnitude(tx + 0.5 * tdt * k2);
double k4 = getMagnitude(tx + tdt * k3);
return (k1 + 2*k2 + 2*k3 + k4)/6.0;
}
Ale coś jest nie tak, ponieważ integruję tylko raz używając RK4 (przyspieszenie). Całkowanie prędkości za pomocą RK4 nie ma sensu, ponieważ jest takie samo jak v * dt.
Czy możesz mi powiedzieć, jak rozwiązać równania różniczkowe drugiego rzędu za pomocą integracji Runge-Kutta? Czy powinienem zaimplementować RK4 obliczając współczynniki k1, l1, k2, l2 ... l4? Jak mogę to zrobić?
źródło
Odpowiedzi:
Wydaje się, że istnieje sporo zamieszania co do sposobu stosowania metod wieloetapowych (np. Runge-Kutta) do ODE drugiego lub wyższego rzędu lub systemów ODE. Proces jest bardzo prosty, gdy go zrozumiesz, ale być może nie jest oczywisty bez dobrego wyjaśnienia. Poniższa metoda jest najprostsza.
W twoim przypadku równanie różniczkowe, które chciałbyś rozwiązać, to . Pierwszym krokiem jest napisanie ODE drugiego rzędu jako systemu ODE pierwszego rzędu. Odbywa się to jakofa= m x¨
Wszystkie równania w tym systemie musi być rozwiązany równocześnie, co znaczy, że należy nie advance , a następnie postęp , powinny być rozszerzone zarówno w tym samym czasie. W językach, które obsługują operacje wektorowe bez pętli, można to łatwo zrobić, tworząc wszystkie niezbędne terminy w wektorach kodu o długości 2. Funkcja obliczająca prawą stronę (szybkość zmian) ODE powinna zwrócić wektor o długości 2 , aby powinny być wektory o długości 2, a zmienna stanu powinien być wektor o długości 2. W MATLAB kod niezbędny do intensyfikacji czasu można zapisać jako:x ( x , v )v x ( x , v )
k1
k4
gdzie i zwraca wektor zawierający . Jak widać, poprzez wektoryzację rzeczy nie musisz nawet zmieniać składni kodu RK4 bez względu na to, ile równań znajduje się w twoim systemie ODE.= ( x , v ) ( x˙( t ) , v˙( t ) )
X
( ˙ x ( t ) , ˙ v ( t ) )RHS( t, X )
Niestety C ++ nie obsługuje natywnie takich operacji wektorowych, więc musisz albo użyć biblioteki wektorów, użyć pętli, albo ręcznie napisać osobne części.W C ++ możesz użyćstd::valarray
tego samego efektu. Oto prosty działający przykład ze stałym przyspieszeniem.źródło
typedef std::valarray<double> Vector
dla często używanych typów. 3) Użyjconst int NDIM = 2
zamiast#define
dla bezpieczeństwa typu i poprawności. 4) Od wersji C ++ 11 można po prostu zastąpić korpus RHSreturn {X[1], 1}
. 5) W C ++ (w przeciwieństwie do C) naprawdę rzadko zdarza się, aby najpierw deklarować zmienne, a następnie inicjować je, preferować deklarowanie zmiennych w tym samym miejscu, w którym je inicjujesz (double t = 0.
itp.)RHS()
oblicza prawą stronę równania różniczkowego. Wektor stanu X to (x, v), więc dX / dt = (dx / dt, dv / dt) = (v, a). Dla twojego problemu (jeśli a = G * M / x ^ 2) RHS powinien powrócić{ X[1], G*M/(X[0]*X[0]) }
.