Órbitas de herradura e integración en C

Estoy estudiando un caso particular del problema restringido de los tres cuerpos. Se ha encontrado que algunos objetos siguen un patrón de órbita en herradura, y estoy tratando de resolver algo a través de un código de integración en C. Estoy siguiendo algunos consejos en el artículo Familias de órbitas periódicas en herradura en el problema restringido de tres cuerpos , lo que me da las condiciones iniciales ideales y las ecuaciones en el sistema del centro de masa. (m es la masa de la Tierra, y la consiguiente posición del sol en el centro del sistema de referencia de masa, (x,y) son las coordenadas del tercer cuerpo, que se supone sin masa (como requiere el problema restringido).

O = ( X 2 + y 2 ) / 2 + ( 1 metro ) r 1 + metro r 2 + ( 1 metro ) metro 2
r 1 2 = ( X metro ) 2 + y 2

r 2 2 = ( X metro + 1 ) 2 + y 2
a ( X ) = d O d X + 2 v ( y )
a ( y ) = d O d y 2 v ( X )

Las posiciones del "sol" y la "tierra" están fijadas en (m,0) y (m-1,0), en el mismo sistema de referencia. (sistema de referencia giratorio, asumiendo que la tierra tiene una órbita circular).

De todo esto he calculado las ecuaciones para describir el sistema:

a ( X ) = X + ( metro 1 ) ( X metro ) ( ( X metro ) 2 + y 2 ) 1 .5 2 metro ( X metro + 1 ) ( ( X metro + 1 ) 2 + y 2 ) 1 .5 + 2 v ( y )
a ( y ) = y y ( 1 metro ) ( ( X metro ) 2 + y 2 ) 1 .5 2 y metro ( ( X metro + 1 ) 2 + y 2 ) 1 .5 2 v ( X )

He usado el algoritmo de Runge-Kutta 4 para integrar esas ecuaciones. (Sé que el código es bastante alucinante, pero simplemente no puedo usar punteros y uso estructuras en todas partes).

#include<stdio.h>
#include<stdlib.h>
#include<math.h>

#define dt 0.0001
#define N 100

typedef struct{
    long double x,y;
}vec;

typedef struct{
    vec k1,k2,k3,k4;
}runge;

typedef struct{
    runge r,v;
}big;

double dS,dE,m;

double accx(double,double,double);
double accy(double,double,double);
void rad(vec);
big rungekutta(vec,vec);
vec moto(vec,runge);
double jacobi(vec);

int main(){
  vec r,v;
    big f;
    double J,t;
    int i,Num;
    FILE* s1;
    s1=fopen("HorseShoe.dat","w");

    Num=(int)N/dt;
    scanf("%Lf",&r.x);
    scanf("%Lf",&r.y);
    scanf("%Lf",&v.x);
    scanf("%Lf",&v.y);
    scanf("%lf",&m);

    for(i=0;i<Num;i++){
        t=(i+1)*dt;
        rad(r);
        f=rungekutta(r,v);
        r=moto(r,f.r);
        v=moto(v,f.v);
        J=jacobi(r);
        fprintf(s1,"%lf\t%Lf\t%Lf\t%Lf\t%Lf\t%lf\n",t,r.x,r.y,v.x,v.y,J);
    }
return 0;
}

void rad(vec r){
    dS=pow(r.x-m,2)+pow(r.y,2);
    dE=pow(r.x-m+1,2)+pow(r.y,2);
}

double jacobi(vec r){
    return pow(r.x,2)+pow(r.y,2)+2*(1-m)/dS+2*m/dE+m*(1-m);
}

double accx(double x,double y,double v){
    return x-(x-m)*(1-m)/pow(pow(x-m,2)+pow(y,2),1.5)-m*(x-m+1)/pow(pow(x-m+1,2)+pow(y,2),1.5)+2*v;
}

double accy(double x,double y,double v){
    return y-(1-m)*y/pow(pow(y,2)+pow(x-m,2),1.5)-m*y/pow(pow(y,2)+pow(x-m+1,2),1.5)-2*v;
}

big rungekutta(vec r,vec v){
    big f;
    f.r.k1.x=v.x;
    f.r.k1.y=v.y;
    f.v.k1.x=accx(r.x,r.y,v.y);
    f.v.k1.y=accy(r.x,r.y,v.x);
    f.r.k2.x=v.x+f.v.k1.x*dt/2;
    f.r.k2.y=v.y+f.v.k1.y*dt/2;
    f.v.k2.x=accx(r.x+f.r.k1.x*dt/2,r.y+f.r.k1.y*dt/2,v.y+f.v.k1.y*dt/2);
    f.v.k2.y=accy(r.x+f.r.k1.x*dt/2,r.y+f.r.k1.y*dt/2,v.x+f.v.k1.x*dt/2);
    f.r.k3.x=v.x+f.v.k2.x*dt/2;
    f.r.k3.y=v.y+f.v.k2.y*dt/2;
    f.v.k3.x=accx(r.x+f.r.k2.x*dt/2,r.y+f.r.k2.y*dt/2,v.y+f.v.k2.y*dt/2);
    f.v.k3.y=accy(r.x+f.r.k2.x*dt/2,r.y+f.r.k2.y*dt/2,v.x+f.v.k2.x*dt/2);
    f.r.k4.x=v.x+f.v.k3.x*dt;
    f.r.k4.y=v.y+f.v.k3.y*dt;
    f.v.k4.x=accx(r.x+f.r.k3.x*dt,r.y+f.r.k3.y*dt,v.y+f.v.k3.y*dt);
    f.v.k4.y=accy(r.x+f.r.k3.x*dt,r.y+f.r.k3.y*dt,v.x+f.v.k3.x*dt);
    return f;
}

vec moto(vec r,runge rk){
    r.x+=(rk.k1.x+2*rk.k2.x+2*rk.k3.x+rk.k4.x)*dt/6;
    r.y+=(rk.k1.y+2*rk.k2.y+2*rk.k3.y+rk.k4.y)*dt/6;
    return r;
}

Trazando los resultados, solo obtengo una espiral, mientras uso las entradas dadas, debería obtener una órbita de herradura. Probé muchas entradas diferentes (m = 0.0001 y m = 0.000003, este último está en escala con los valores reales de las masas de la Tierra y el Sol (la masa del sol es 1-m)).

Simplemente no puedo ver qué está mal (probablemente todo: D), ¿alguien puede ayudarme?

Las personas podrían ayudarlo mejor si explica cuáles son sus variables (en las ecuaciones): O,x,y,m,r1/2, etc. Además, su pregunta no aclara cuál es el problema que tiene. encontrándose "No puedo ver qué está mal": ¿qué se supone que debes obtener y qué obtienes en su lugar?
Antes de continuar, me gustaría ver dos cosas: un enlace al documento de referencia que usó para ver si cometió un error en sus matemáticas y sus entradas. Sospecho que sus entradas podrían estar apagadas. Este es un sistema de unidades no estándar en el que la masa de la Tierra metro debería ser sobre 3 × 10 6 , la masa del Sol es 1 menos este pequeño número, y la magnitud del vector de posición ( X , y ) es aproximadamente 1. El tiempo también parece estar escalado. Este no es un lugar para unidades SI.
Acabo de editar la pregunta agregando los detalles que pediste (perdón si soy un poco desordenado, la primera pregunta aquí). Usé G=1, por supuesto que no podía usar unidades SI;)
Usé muchas de las entradas dadas en los artículos (al menos una por familia de órbitas), les doy un ejemplo. (x0=0,864394016091,y0=0,vx=0,vy=0,288028401448,m=0,0001)
Wow, acabo de encontrar el error, me siento tan estúpido ahora... Gracias de todos modos;)
Siéntase libre de publicar una respuesta a su propia pregunta detallando qué estaba mal y cómo lo arregló. ¡Esto puede resultar útil para otros en el futuro que lleguen a esta pregunta!
Solo una pequeña pero importante observación: Runge-Kutta de 4ᵗʰ orden no es simpléctico , lo que significa que cambia la energía orbital total con el tiempo, lento al principio pero aumentando exponencialmente en su gravedad. Dependiendo de su intervalo de integración deseado, es mejor que se deshaga de RK4 a favor de algo más adecuado para la mecánica celestial. Consulte, por ejemplo, este documento para conocer algunos antecedentes y recomendaciones.

Respuestas (1)

No revisaré sus ecuaciones con cuidado ni su código, pero dado que no mencionó las posiciones iniciales exactas que usó, supongo que no resolvió primero un vector de estado de órbita de herradura estable.

Las posiciones del Sol y la Tierra están fijas en el marco sinódico (giratorio). Pero, ¿dónde empezaste tu astroide? Para una posición inicial determinada, también debe darle al asteroide la velocidad inicial correcta (velocidad y dirección), de lo contrario, es posible que no sea estable y simplemente se desvíe o gire en espiral como mencionó.

Eche un vistazo a esta respuesta donde muestro las ecuaciones correctas y luego resuelvo las órbitas de herradura estables antes de trazarlas.

Comienzo con una posición en el eje x, frente a la Tierra, y un poco más lejos o más cerca del Sol que la Tierra. Luego lo lanzo a baja velocidad (en el marco giratorio) exactamente en la dirección del eje y. Observo que se desplaza hacia la Tierra y luego regresa como un boomerang a la región del área de inicio. Cuando vuelve a cruzar el eje x, compruebo qué tan cerca está la velocidad de y. Utilizo un solucionador de optimización cero brentqpara ajustar la velocidad inicial hasta que la velocidad de retorno está exactamente en la dirección opuesta.

Si eso es cierto, entonces la órbita será periódica y repetitiva, y podemos llamarla una órbita obsoleta bajo las restricciones del CR3BP.


De esta respuesta (hay mucho más para leer allí, ¡incluidas varias referencias!):

X ¨ = X + 2 y ˙ ( 1 m ) ( X + m ) r 1 3 m ( X 1 + m ) r 2 3
y ¨ = y 2 X ˙ ( 1 m ) y r 1 3 m y r 2 3
z ¨ = ( 1 m ) z r 1 3 m z r 2 3

ingrese la descripción de la imagen aquí

arriba: semiciclos de algunas órbitas de herradura tambaleantes

ingrese la descripción de la imagen aquí

arriba: tiempos hasta los primeros cruces del eje x de las mismas órbitas de herradura tambaleantes, utilizadas para calcular tiempos de medio ciclo.

ingrese la descripción de la imagen aquí

arriba: tiempos de ciclo de este cálculo (puntos negros) frente al método de estimación del período sinódico (puntos rojos). Buen acuerdo cualitativo. También las velocidades iniciales y en cada punto inicial en x.

a continuación: secuencia de comandos de Python para estas parcelas.

def x_acc(x, ydot):
    r1    = np.abs(x-x1)
    r2    = np.abs(x-x2)
    xddot = x + 2*ydot  -  ((1-mu)/r1**3)*(x+mu) - (mu/r2**3)*(x-(1-mu))
    return xddot

def C_calc(x, y, z, xdot, ydot, zdot):
    r1 = np.sqrt((x-x1)**2 + y**2 + z**2)
    r2 = np.sqrt((x-x2)**2 + y**2 + z**2)
    C = (x**2 + y**2 + 2.*(1-mu)/r1 + 2.*mu/r2 - (xdot**2 + ydot**2 + zdot**2))
    return C

def deriv(X, t): 
    x, y, z, xdot, ydot, zdot = X
    r1 = np.sqrt((x-x1)**2 + y**2 + z**2)
    r2 = np.sqrt((x-x2)**2 + y**2 + z**2)
    xddot = x + 2*ydot  -  ((1-mu)/r1**3)*(x+mu) - (mu/r2**3)*(x-(1-mu))
    yddot = y - 2*xdot  -  ((1-mu)/r1**3)*y      - (mu/r2**3)*y
    zddot =             -  ((1-mu)/r1**3)*z      - (mu/r2**3)*z
    return np.hstack((xdot, ydot, zdot, xddot, yddot, zddot))

# http://cosweb1.fau.edu/~jmirelesjames/hw4Notes.pdf

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint as ODEint
from scipy.optimize import brentq

halfpi, pi, twopi = [f*np.pi for f in (0.5, 1, 2)]

mu = 0.001

x1 = -mu
x2 = 1. - mu

x = np.linspace(-1.4, 1.4, 1201)
y = np.linspace(-1.4, 1.4, 1201)

Y, X = np.meshgrid(y, x, indexing='ij')
Z    = np.zeros_like(X)

xdot, ydot, zdot = [np.zeros_like(X) for i in range(3)]

C = C_calc(X, Y, Z, xdot, ydot, zdot)
C[C>8] = np.nan

if True:
    plt.figure()
    plt.imshow(C)
    plt.colorbar()
    levels = np.arange(2.9, 3.2, 0.04) 
    CS = plt.contour(C, levels,
                 origin='lower',
                 linewidths=2) 
    plt.show()

ydot0s   = np.linspace(-0.08, 0.08, 20)
x0ydot0s = []
for ydot0 in ydot0s:
    x0, infob =  brentq(x_acc, -1.5, -0.5, args=(ydot0), xtol=1E-11, rtol=1E-11,
                           maxiter=100, full_output=True, disp=True)
    x0ydot0s.append((x0, ydot0))

states = [np.array([x0, 0, 0, 0, ydot0, 0]) for (x0, ydot0) in x0ydot0s]

times  = np.arange(0, 150, 0.01)

results = []
for X0 in states:
    answer, info = ODEint(deriv, X0, times, atol = 1E-11, full_output=True)
    results.append(answer.T.copy())

resultz = []
for x0ydot0, thing in zip(x0ydot0s, results):
    y     = thing[1]
    check = y[2:]*y[1:-1] < 0
    zc    = np.argmax(y[2:]*y[1:-1] < 0) + 1
    if zc > 10:
        resultz.append((thing, zc, x0ydot0))

if True:
    plt.figure()
    hw = 1.6
    for j, (thing, zc, x0ydot0) in enumerate(resultz):
        x, y = thing[:2,:zc]
        plt.plot(x, y)
    plt.xlim(-hw, hw)
    plt.ylim(-hw, hw)
    plt.plot([x1], [0], 'ok')
    plt.plot([x2], [0], 'ok')
    plt.show()

if True:
    plt.figure()
    for j, (thing, zc, x0ydot0) in enumerate(resultz):
        x, y = thing[:2]
        plt.plot(times[:zc], y[:zc])
    plt.show()

if True:
    plt.figure()
    for j, (thing, zc, x0ydot0) in enumerate(resultz):
        x0, ydot0 = x0ydot0
        cycle_time = 2. * times[zc] / twopi
        ratio = abs(x0/x2)
        T_simple_model = twopi * abs(x0/x2)**1.5
        T_synodic_simple_model = 1. / (1. - twopi/T_simple_model) # https://astronomy.stackexchange.com/a/25002/7982
        plt.subplot(2, 1, 1)
        plt.plot(x0, cycle_time, 'ok')
        plt.plot(x0, abs(T_synodic_simple_model), 'or')
        plt.subplot(2, 1, 2)
        plt.plot(x0, ydot0, 'ok')
    plt.subplot(2, 1, 1)
    plt.xlabel('x0', fontsize=16)
    plt.ylabel('cycle times (periods)', fontsize=16)
    plt.subplot(2, 1, 2)
    plt.xlabel('x0', fontsize=16)
    plt.ylabel('ydot0', fontsize=16)
    plt.show()