Интеграция Solve_ivp застревает, если начальное условие запускает событие с Terminal = True - PullRequest
2 голосов
/ 13 февраля 2020

Я работаю над проектированием траектории космического корабля и внедряю алгоритм для нахождения периодов c орбит, которые необходимо обнаруживать, когда траектория пересекает заданную c плоскость. Я использую scipy.integrate solve_ivp и его events функциональность.

В идеале я хотел бы подсчитать, когда инициируемые события останавливают интеграцию только после того, как сработало определенное количество событий, но поскольку я не смог найти подобную опцию в документации solve_ivp , Я просто использую цикл for для объединения этих дуг интеграции и подсчета количества событий. Однако моя проблема в том, что solve_ivp застревает, когда начальное условие вызывает событие, и event.terminal = True.

Ниже приведен пример кода:

import numpy as np
from scipy.integrate import solve_ivp

def eq_motion(t,Y):

    Ydot = np.zeros((6,1))
    r = (Y[0,0]**2 + Y[1,0]**2 + Y[2,0]**2)**0.5

    Ydot[0] = Y[3,0]
    Ydot[1] = Y[4,0] 
    Ydot[2] = Y[5,0]
    Ydot[3] = 2*Y[4,0] + 3*Y[0,0] - Y[0,0]/r**3
    Ydot[4] = -2*Y[3,0] - Y[1,0]/r**3
    Ydot[5] = -Y[2,0] - Y[2,0]/r**3

    return Ydot

def event_xz_plane(t,Y):
    return Y[1]

event_xz_plane.terminal = True
event_xz_plane.direction = 0

# initial conditions
Y0 = np.array([-0.006489114696252, 0, 0.107462057974196 + 0.0006, 0, 4.165257687202607,0])
t_span = np.array([0, 1.892845635529040*2])

n_plane_intersections = 4

for i in np.arange(n_plane_intersections):

    integrate = solve_ivp(fun=eq_motion, t_span=t_span, y0=Y0, method = 'LSODA', 
                            vectorized = True, rtol = 1e-13, atol = 1e-14,
                            events = (event_xz_plane),dense_output=True)

    print('Number of integrations before event: {}'.format(integrate.y.shape[1]-1))
    print(integrate.y_events[0])
    print('\n')

    # attempting to chain the different integration arcs
    Y0 = integrate.y[:,-1]

Выбранные начальные условия близки к периодам c орбиты и дают следующую траекторию где вы можете видеть, что есть 4 пересечения с плоскостью xz, включая начальное условие, которое находится на этой плоскости.

Тем не менее, вышеприведенный скрипт выводит следующее:

Number of integrations before event: 1
[[-0.00648911  0.          0.10806206  0.          4.16525769  0.        ]]


Number of integrations before event: 1
[[-0.00648911  0.          0.10806206  0.          4.16525769  0.        ]]


Number of integrations before event: 1
[[-0.00648911  0.          0.10806206  0.          4.16525769  0.        ]]


Number of integrations before event: 1
[[-0.00648911  0.          0.10806206  0.          4.16525769  0.        ]]

где вы можете видеть, что это в основном застревает в начальном состоянии. Это происходит, конечно, потому что начальное условие находится в терминальном условии, однако, кажется, нет никакой возможности «пройти» первую итерацию интегрирования.

Я попытался написать функцию события как

def event_xz_plane(t,Y):
    return Y[1] + 1e3*(t==0) 

, чтобы она игнорировала начальное состояние, но затем просто «зависала» на следующем пересечении плоскости, и в любом случае это не не похоже на хорошее решение.

Единственное другое решение, о котором я подумал, это установить terminate=False для функции события и проверить количество запущенных событий и вектор состояния в этих случаях. Тем не менее, это не идеально для моей реализации, так как это зависит от установленного времени t_span, которое неизвестно в алгоритме. То есть, если мне нужно получить и n количество пересечений плоскостей, мне придется итеративно корректировать t_span до тех пор, пока я не достигну этого числа, вместо того, чтобы просто позволить интегрированию работать до тех пор, пока не сработают n пересечения плоскостей.

Есть ли способ преодолеть это?

1 Ответ

1 голос
/ 13 февраля 2020

Сначала проверьте систему дифференциальных уравнений. Как линейные термины имеют смысл? Третий компонент ускорения имеет центральную часть силы, пропорциональную третьей координате Y[2]. Вполне возможно, что с этим существенным исправлением ваш оригинальный подход, использующий нетерминальное событие, будет работать.

Если вы решите реализовать функцию ODE в векторизованной версии, тогда полностью соблюдайте требования. Нельзя полагать, что входной вектор содержит только одно состояние, поэтому выходные данные Ydot должны иметь тот же формат, что и входные данные Y.

def eq_motion(t,Y):

    Ydot = np.zeros(Y.shape)
    r = (Y[0]**2 + Y[1]**2 + Y[2]**2)**0.5

    Ydot[0] = Y[3]
    Ydot[1] = Y[4] 
    Ydot[2] = Y[5]
    Ydot[3] = - Y[0]/r**3 + 2*Y[4] + 3*Y[0]
    Ydot[4] = - Y[1]/r**3 - 2*Y[3]         
    Ydot[5] = - Y[2]/r**3 - Y[2]          

    return Ydot

. Лучше также обновить t0 вместе с Y0, поэтому установите

t0, tfinal = 0, 10

Используйте направление события, чтобы запретить решателю снова обнаруживать последнее событие, в идеале вы бы из Y0 и eq_motion(t0,Y0) могли бы определить текущий знак события в положительное время направление, здесь положительное, и установите направление, противоположное ему

event_xz_plane.direction = -1

n_plane_intersections = 4

for i in np.arange(n_plane_intersections):

    integrate = solve_ivp(fun=eq_motion, t_span=[t0,tfinal], y0=Y0, method = 'LSODA', 
                            vectorized = True, rtol = 1e-13, atol = 1e-14,
                            events = (event_xz_plane),dense_output=True)

    print(integrate.message)
    print('Number of integrations before event: {}'.format(integrate.y.shape[1]-1))
    print(integrate.t_events[0],integrate.y_events[0])
    print('\n')

    # attempting to chain the different integration arcs
    Y0 = integrate.y[:,-1]
    t0 = integrate.t[-1]
    event_xz_plane.direction *= -1

Это дает вывод

A termination event occurred.
Number of integrations before event: 288
[0.96633212] [[ 3.54891748e-01 -6.67868538e-17 -8.77207053e-01  5.03973235e-02
  -7.78052855e-01 -1.76219528e-02]]


A termination event occurred.
Number of integrations before event: 263
[2.03187275] [[ 7.62458860e-03  1.66901228e-15  1.66823579e-01 -2.71527186e-01
   3.27865867e+00  1.07443749e-01]]


A termination event occurred.
Number of integrations before event: 300
[3.22281063] [[ 3.86773384e-01 -1.92554306e-16 -8.24376230e-01 -6.86117012e-02
  -8.96669320e-01  2.07695448e-01]]


A termination event occurred.
Number of integrations before event: 258
[4.10715034] [[-2.07546473e-02 -9.56266316e-16  1.42957911e-01  9.37459643e-02
   3.56344204e+00  7.24687825e-02]]

3D plot of orbit

To по сравнению с этим, метод Dormand-Prince 853 намного быстрее (а Radau намного медленнее) и дает те же результаты

A termination event occurred.
Number of integrations before event: 67
[0.96633212] [[ 3.54891748e-01 -2.46764414e-16 -8.77207053e-01  5.03973235e-02
  -7.78052855e-01 -1.76219528e-02]]


A termination event occurred.
Number of integrations before event: 52
[2.03187275] [[ 7.62458861e-03 -5.10008702e-16  1.66823579e-01 -2.71527186e-01
   3.27865867e+00  1.07443749e-01]]


A termination event occurred.
Number of integrations before event: 58
[3.22281063] [[ 3.86773384e-01  1.21430643e-17 -8.24376230e-01 -6.86117012e-02
  -8.96669320e-01  2.07695448e-01]]


A termination event occurred.
Number of integrations before event: 52
[4.10715034] [[-2.07546473e-02  9.19403442e-17  1.42957911e-01  9.37459642e-02
   3.56344204e+00  7.24687825e-02]]

...