Я работаю над калькулятором траектории для проблемы с двумя телами, и я пытаюсь использовать Scipy RK45 или LSODA для решения ODE и возврата траектории. (Пожалуйста, предложите другой метод, если вы считаете, что это лучший/более простой способ сделать это)
Я использую IDE Spyder с Anaconda. Scipy версия 1.1.0
ПРОБЛЕМЫ:
RK45: При использовании RK45 первый шаг, похоже, работает. При переходе через код в отладчике вводится значение twoBody() и работает точно так, как ожидалось, первый прогон. Однако, после первого return ydot
, все начинает идти не так. С точкой останова на линии ydot[0] = y[3]
мы начинаем видеть проблему. Массив y
(который я ожидал быть 6x1) теперь представляет собой массив 6x6. При попытке оценить эту строку numpy возвращает ошибку ValueError: could not broadcast input array from shape (6) into shape (1)
. Есть ли ошибка в моем коде, которая заставит y
перейти от 6x1 до 6x6? Ниже представлен массив y перед ошибкой вещания.
y =
-5.61494e+06 -2.01406e+06 2.47104e+06 -683.979 571.469 1236.76
-5.61492e+06 -2.01404e+06 2.47106e+06 -663.568 591.88 1257.17
-5.6149e+06 -2.01403e+06 2.47107e+06 -652.751 602.697 1267.99
-5.61492e+06 -2.01405e+06 2.47105e+06 -672.901 582.547 1247.84
-5.61492e+06 -2.01405e+06 2.47105e+06 -672.988 582.46 1247.75
-5.61492e+06 -2.01405e+06 2.47105e+06 -673.096 582.352 1247.64
Может ли мое начальное условие Y0
заставлять его достигнуть слишком малого шага и, следовательно, выйти из строя?
LSODA: Я также пытался использовать решатель LSODA. Однако он никогда не входит в twoBody()
! Точка прерывания внутри вершины twoBody()
никогда не достигается, и программа возвращает время выполнения. Я понятия не имею, что здесь происходит. Угадав, я настроил его неправильно.
EDIT: То же самое происходит при использовании Scipy solve_ivp. Все другие методы интеграции возвращают широковещательную ошибку.
import numpy as np
import scipy.integrate as ode
from time import time
startTime = time()
def twoBody(t, y):
"""
Two Body function returns the derivative of the state space variables.
INPUTS:
--- t ---
A scalar time value.
--- y ---
A 6x1 array of the state space of a particle in 3D space
OUTPUTS:
--- ydot ---
The derivative of y for the two-body problem
"""
mu = 3.986004418 * 10**14
r = np.sqrt(y[0]**2 + y[1]**2 + y[2]**2)
ydot = np.empty((6,1))
ydot[:] = np.nan
ydot[0] = y[3]
ydot[1] = y[4]
ydot[2] = y[5]
ydot[3] = (-mu/(r**3))*y[0]
ydot[4] = (-mu/(r**3))*y[1]
ydot[5] = (-mu/(r**3))*y[2]
return ydot
# In m and m/s
# first three are the (x, y, z) position
# second three are the velocities in those same directions respectively
Y0 = np.array([-5614924.5443320004,
-2014046.755686,
2471050.0114869997,
-673.03650300000004,
582.41158099999996,
1247.7034980000001])
solution = ode.LSODA(twoBody, t0 = 0.0, y0 = Y0, t_bound = 351.0)
#solution = ode.RK45(twoBody, t0 = 0.0, y0 = Y0, t_bound = 351.0)
runTime = round(time() - startTime,6)
print('Program runtime was {} s'.format(runTime))
Вы также можете использовать scipy.integrate.odeint
для такого рода задач, которые могут быть проще настроить:
import numpy as np
from scipy.integrate import odeint
def twoBody(y, t, mu):
"""
Two Body function returns the derivative of the state space variables.
INPUTS:
--- t ---
A scalar time value.
--- y ---
A 6x1 array of the state space of a particle in 3D space
OUTPUTS:
--- ydot ---
The derivative of y for the two-body problem
"""
r = np.sqrt(y[0]**2 + y[1]**2 + y[2]**2)
ydot = np.empty((6,))
ydot[0] = y[3]
ydot[1] = y[4]
ydot[2] = y[5]
ydot[3] = (-mu/(r**3))*y[0]
ydot[4] = (-mu/(r**3))*y[1]
ydot[5] = (-mu/(r**3))*y[2]
return ydot
# In m and m/s
# first three are the (x, y, z) position
# second three are the velocities in those same directions respectively
Y0 = np.array([-5614924.5443320004,
-2014046.755686,
2471050.0114869997,
-673.03650300000004,
582.41158099999996,
1247.7034980000001])
mu = 3.986004418 * 10**14
solution = odeint(twoBody, Y0, np.linspace(0., 351., 100), args=(mu, ))
Я не могу судить о правильности вывода, но, похоже, он хорошо интегрируется.
Проблема в том, что вы определяете ydot
как матрицу, то есть 2-мерный массив, даже если второе измерение имеет ширину только 1
. Начальное значение представляет собой простой массив длины 6. При автоматическом преобразовании после модели идиосинкразий Matlab y0
интерпретируется как вектор строки, а массив (6,1), как обычно, как вектор столбца, сумма обоих равна ( 6,6) матрица.
a = np.zeros((3,1))
b = np.array([1,2,3.0])
a+b
array([[1., 2., 3.],
[1., 2., 3.],
[1., 2., 3.]])
Таким образом, на следующем шаге вы пытаетесь заполнить записи вектора ydot
столбца (6,1) строками матрицы y
(6,6), что дает сообщаемую ошибку.
Таким образом, избегайте смешивания разных типов векторов. определяющий
ydot = np.empty((6,))
делает это с минимальным изменением.
PS: Это может не иметь значения, но я бы взял время начала после интерпретации функции ODE, непосредственно перед вызовом решателя.