Как интегрировать оценку дискретного времени в компоненты OpenMDAO? - PullRequest
2 голосов
/ 02 апреля 2019

Я пытаюсь ознакомиться с OpenMDAO. Трудно понять одну вещь: как интегрированные переменные / переменные состояния работают в отдельных компонентах OpenMDAO. Я думаю, что это очень важно, и я хочу понять основы.

Допустим, у меня есть ракета с постоянной тягой и изменяющейся массой, и я хочу смоделировать первые 10 секунд полета с дискретным временем и временным шагом 0,1 секунды. Допустим, изменяющаяся масса также является функцией времени, но также притворяется, что это сложная переменная, зависящая от многих вещей, поэтому мы хотим, чтобы она рассчитывалась в другом компоненте и просто была входом для этого компонента.

-Как можно гарантировать, что Масса обновляется при вычислении скорости с дискретным временным шагом, находясь в другом компоненте?

Приведенный ниже код является посредственной попыткой решить эту проблему в качестве примера, но я уверен, что теперь Mass - это простой статический ввод, хотя он должен меняться в некотором произвольном отношении ко времени. (Предположим, что тяга постоянна)

from openmdao.api import ExplicitComponent

class VelocityComponent(ExplicitComponent):

    def setup(self):
        self.add_input('T', desc='Propulsion Thrust')
        self.add_input('M', desc='Instanteneous Mass')\

        self.add_output('v', desc='Satellite Velocity')

        self.declare_partials('*','*')

    def compute(self, inputs, outputs)

        v = 10                          #some initial velocity value
        t = 0                           #initial time value
        tstep = 0.1
        tend = 10

        for i in range(0,tend/tstep):
            a = inputs['T']/inputs['M'] #calculate acceleration  
            v += a                      #update velocity
            t += tstep                  #next time step 

        outputs['v'] = v 

скорость, v, должна быть интегрирована с зависящим от времени ускорением, а не постоянным ускорением.

PS: Поскольку я довольно новичок во всем этом, но готов учиться, любые советы по ресурсам, которые могли бы помочь новичку, как я, в OpenMDAO, очень ценятся.

PSPS: я прочитал Руководство для начинающих и опытных пользователей документации OpenMDAO, но не смог найти пример со встроенными переменными. В старой документации есть пример двигателя и системы трансмиссии , а его компонент двигателя включает переменные состояния и некоторые дискретные этапы интеграции, но в нем используется более старая версия OpenMDAO, и я не знаю, как это будет работать в более новой версии (если я правильно понимаю старую)

1 Ответ

4 голосов
/ 02 апреля 2019

Это обсуждение интеграции ODE, и вы выбрали довольно сложную тему, потому что 1) существует множество различных способов выполнить интеграцию (например, явный Эйлер, RK4, BDF ...) 2) перенос аналитических производныхчерез интеграцию времени очень сложно.

для # 2, корень сложности - именно та проблема, которую вы определили.Вы не можете использовать простую структуру for-loop внутри одного компонента, если вы также хотите создать свой ODE из набора различных компонентов, организованных в группу.

Хорошей новостью является то, что уже есть библиотека, которая обрабатывает все время интеграции для вас: Dymos .По состоянию на апрель 2019 года эта библиотека активно разрабатывается самой командой OpenMDAO и все еще подвергается пересмотру некоторых режимов API и добавлению функций.Несмотря на довольно плавные API, я бы порекомендовал вам взглянуть на примеры там.Для сложных задач это ваш лучший выбор.

Тем не менее, вы можете добиться простой интеграции euler с пошаговым временем без дополнительной библиотеки.Хитрость в том, что вы выделяете один экземпляр вашего ODE для каждого временного шага и передаете новое состояние по линии от одного экземпляра к следующему.Вот мертвый простой пример падающего объекта в условиях постоянной силы тяжести:

from openmdao.api import IndepVarComp, Problem, ExplicitComponent


class Cannonball(ExplicitComponent): 

    def initialize(self): 

        self.options.declare('delta_t', default=0.1)

    def setup(self): 

        self.add_input('Yi', units='m',   desc='position at the start of the time-step')
        self.add_input('Vi', units='m/s', desc='velocity at the start of the time-step')

        self.add_output('Ye', units='m',   desc='position at the end of the time-step')
        self.add_output('Ve', units='m/s', desc='velocity at the end of the time-step')

        self.declare_partials(of='*', wrt='*', method='cs')


    def compute(self, inputs, outputs): 

        dt = self.options['delta_t']

        outputs['Ve'] = 9.81 * dt + inputs['Vi']
        outputs['Ye'] = 0.5 * 9.81 * dt**2 + inputs['Vi'] * dt + inputs['Yi']


if __name__ == "__main__": 
    import numpy as np
    import matplotlib.pylab as plt

    N_TIMES = 10

    p = Problem()

    ivc = p.model.add_subsystem('init_conditions', IndepVarComp(), promotes=['*'])
    ivc.add_output('Y0', 100., units='m')
    ivc.add_output('V0', 0, units='m/s')

    p.model.connect('Y0', 't_0.Yi')
    p.model.connect('V0', 't_0.Vi')

    for i in range(N_TIMES): 
        p.model.add_subsystem(f't_{i}', Cannonball())


    for i in range(N_TIMES-1): 
        p.model.connect(f't_{i}.Ye', f't_{i+1}.Yi')
        p.model.connect(f't_{i}.Ve', f't_{i+1}.Vi')

    p.setup()

    p.run_model()


    # collect the data into an array for plotting
    Y = [p['Y0'],]
    V = [p['V0'],]
    for i in range(N_TIMES): 
        Y.append(p[f't_{i}.Ye'])
        V.append(p[f't_{i}.Ve'])

    times = np.arange(N_TIMES+1) * .01 # delta_t

    fig, ax = plt.subplots()
    ax.plot(times, Y)
    ax.set_ylabel('velocity (m/s')
    ax.set_xlabel('time (s)')

    plt.show()

Это придаст структуре вашей модели временную подачу (сгенерированную с помощью встроенного в OpenMDAO средства просмотра N2 ).n2 diagram

И вы можете видеть, что вы получаете ожидаемую квадратичную позицию по времени.

position with respect to time

Вы можете сделать более сложную интеграцию, кодируя другую схему ODE в этот компонент (например, RK4).Вы также можете написать более сложную группу, состоящую из нескольких компонентов, в качестве временного шага, а затем удалить несколько копий этой группы.

Хочу подчеркнуть, что, хотя приведенный выше пример хорош для формирования понимания, это не очень эффективный способ на самом деле интегрировать время в OpenMDAO. Dymos внутренне работает по-разному, работая с компонентами, которые векторизованы во времени для гораздо большей эффективности.Тем не менее, если вы действительно заинтересованы в самой мертвой простой схеме временной интеграции в OpenMDAO ... вот оно.

...