如何将离散时间估计集成到OpenMDAO组件中?

时间:2019-04-02 07:50:45

标签: openmdao

我正在尝试使自己熟悉OpenMDAO。我发现很难理解的一件事是集成/状态变量如何在OpenMDAO的单独组件中工作。我认为这非常关键,我想了解基本知识。

因此,假设我有一枚具有恒定推力和变化质量的火箭,并且我想以离散时间和0.1秒的时间步长模拟飞行的前10秒。假设质量变化也是时间的函数,但也假装它是一个依赖于许多事物的困难变量,因此我们希望将其计算在不同的组件中,而只是作为该组件的输入。

-如何确保质量在离散的时间步长计算中以不同的分量进行更新?

下面的代码是我尝试解决该示例问题的尝试的平庸尝试,但是我相当确定现在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应与时间相关的加速度a而不是恒定加速度相结合。

PS:由于我对这一切还比较陌生,但是愿意学习,因此可以帮助像我这样的初学者使用OpenMDAO的任何资源提示都将受到赞赏。

PSPS:我已经阅读了OpenMDAO文档的《初学者和高级用户指南》,但是找不到带有集成变量的示例。旧文档中有一个engine and transmission system的示例,其引擎组件确实包含状态变量和一些离散的集成步骤,但它使用的是旧版OpenMDAO,我不知道在新版本中该如何工作(如果我什至正确理解了旧版本的话)

1 个答案:

答案 0 :(得分:4)

这是有关ODE集成的讨论,您选择了一个非常复杂的主题,因为 1)有很多完成集成的方法(例如,显式Euler,RK4,BDF ...) 2)通过时间积分携带解析导数是非常复杂的。

对于#2,困难的根源正是您所确定的问题。如果您还想从组织成组的一组不同组件中构建ODE,则不能在单个组件中使用简单的for循环结构。

好消息是,已经有一个库可以为您处理所有时间积分:Dymos。截至2019年4月,OpenMDAO团队本身正在积极开发此库,并且仍在对API进行一些模式修订和功能添加。尽管API有点流畅,但我还是建议您看一下其中的示例。对于复杂的问题,这是您最好的选择。

但是,您无需额外的库就可以实现简单的时间分步欧拉集成。诀窍在于,您需要为每个时间步标记一个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's built-in N2 viewer生成)。 n2 diagram

您会看到您获得了相对于时间的预期二次位置。

position with respect to time

您可以通过将不同的ODE方案编码到此组件(例如RK4)中来进行更复杂的集成。您还可以编写一个由多个组件组成的更复杂的组,作为您的时间步,然后标记出该组的多个副本。

我想强调一点,尽管上面的示例有助于理解,但这不是在OpenMDAO中进行时间积分的非常有效的方法。 Dymos在内部做事的方式大不相同,它们与及时矢量化的组件一起工作以提高效率。但是,如果您真的对OpenMDAO中最无效的简单时间积分方案感兴趣,就可以了。