OpenMDAO2限制模块化软件架构

时间:2019-05-10 23:09:37

标签: openmdao

OpenMDAO2对组连接和输入/输出变量的限制使我无法编写干净的模块化软件

from openmdao.api import Group, ExplicitComponent, IndepVarComp, Problem

class A(ExplicitComponent):
    def setup(self):
        self.add_input('x', val=0.0)
        self.add_input('y', val=0.0)
        self.add_output('Az', val=0.0)
    def compute(self, inputs, outputs):
        outputs['Az'] = inputs['x'] + inputs['y']

class B(ExplicitComponent):
    def setup(self):
        self.add_input('x', val=0.0)
        self.add_input('y', val=0.0)
        self.add_output('Bz', val=0.0)
    def compute(self, inputs, outputs):
        outputs['Bz'] = 2*inputs['x'] - inputs['y']

class AB(Group):
    def setup(self):
        self.add_subsystem('A', A(), promotes=['*'])
        self.add_subsystem('B', B(), promotes=['*'])

        indeps = IndepVarComp()
        indeps.add_output('x', 0.0)
        indeps.add_output('y', 0.0)
        self.add_subsystem('indeps', indeps, promotes=['*'])


class C(ExplicitComponent):
    def setup(self):
        self.add_input('x', val=0.0)
        self.add_input('y', val=0.0)
        self.add_output('Cz', val=0.0)
    def compute(self, inputs, outputs):
        outputs['Cz'] = 3*inputs['x'] - 2*inputs['y']

class D(ExplicitComponent):
    def setup(self):
        self.add_input('x', val=0.0)
        self.add_input('y', val=0.0)
        self.add_output('Dz', val=0.0)
    def compute(self, inputs, outputs):
        outputs['Dz'] = 4*inputs['x'] - 2.5*inputs['y']

class CD(Group):
    def setup(self):
        self.add_subsystem('C', C(), promotes=['*'])
        self.add_subsystem('D', D(), promotes=['*'])

        indeps = IndepVarComp()
        indeps.add_output('x', 0.0)
        indeps.add_output('y', 0.0)
        self.add_subsystem('indeps', indeps, promotes=['*'])

有时候,我只想与AB组一起工作(运行方案,优化等),有时我只想与CD组一起工作。我可以做到的,

prob = Problem()
prob.model = AB()
prob.setup()
prob['x'] = 10.0
prob['y'] = 20.0
prob.run_model()
print(prob['Az'],prob['Bz'])

但是,有时候我想和ABCD集团一起工作:

class ABCD(Group):
    def setup(self):
        self.add_subsystem('AB', AB())
        self.add_subsystem('CD', CD())

        indeps = IndepVarComp()
        indeps.add_output('xx', 0.0)
        indeps.add_output('yy', 0.0)
        self.add_subsystem('indeps', indeps, promotes=['*'])
        self.connect('xx', ['AB.x', 'CD.x'])
        self.connect('yy', ['AB.y', 'CD.y'])

在那种情况下,没有变量提升,连接或IndepVarComps用法的组合,不会给我带来“具有多个连接的输入”的错误。

在OpenMDAO 1.x中,我可以通过从较低级别的组(AB,CD)中删除IndepVarComps并仅在最高级别的组(see answer)中使用它们来解决此问题。但是,OpenMDAO 2.x引发错误,连接了两个输入而没有输出。例如:

class AB(Group):
    def setup(self):
        self.add_subsystem('A', A(), promotes=['*'])
        self.add_subsystem('B', B(), promotes=['*'])

现在,我坚持维护同一代码的多个副本,一个用于AB的副本,另一个用于ABCD的副本,或者只是用纯python手动连接我的模块并远离OpenMDAO。我想念什么吗?任何帮助或指导都将受到欢迎。

1 个答案:

答案 0 :(得分:3)

在您的简单示例中,我可以看到两种解决问题的方法。两者都涉及在组上使用选项。由于我不确定哪种方法会更好,因此在下面的示例中将它们都包括在内。

一种方法是,如果AB / CD拥有自己的indeps,则可以将其设为可选。然后,您可以根据需要切换所需的行为。这行得通,但我个人认为它很混乱。

第二个选项是仅创建一个组,但使用这些选项来控制mode参数创建的内容。我认为这样比较干净,因为您只有4个组件和一组组件。

from openmdao.api import Group, ExplicitComponent, IndepVarComp

class A(ExplicitComponent):
    def setup(self):
        self.add_input('x', val=0.0)
        self.add_input('y', val=0.0)
        self.add_output('Az', val=0.0)
    def compute(self, inputs, outputs):
        outputs['Az'] = inputs['x'] + inputs['y']

class B(ExplicitComponent):
    def setup(self):
        self.add_input('x', val=0.0)
        self.add_input('y', val=0.0)
        self.add_output('Bz', val=0.0)
    def compute(self, inputs, outputs):
        outputs['Bz'] = 2*inputs['x'] - inputs['y']

class AB(Group):

    def initialize(self): 
        self.options.declare('owns_indeps', types=bool, default=True)

    def setup(self):

        if self.options['owns_indeps']: 
            indeps = IndepVarComp()
            indeps.add_output('x', 0.0)
            indeps.add_output('y', 0.0)
            self.add_subsystem('indeps', indeps, promotes=['*'])

        self.add_subsystem('A', A(), promotes=['*'])
        self.add_subsystem('B', B(), promotes=['*'])


class C(ExplicitComponent):
    def setup(self):
        self.add_input('x', val=0.0)
        self.add_input('y', val=0.0)
        self.add_output('Cz', val=0.0)
    def compute(self, inputs, outputs):
        outputs['Cz'] = 3*inputs['x'] - 2*inputs['y']

class D(ExplicitComponent):
    def setup(self):
        self.add_input('x', val=0.0)
        self.add_input('y', val=0.0)
        self.add_output('Dz', val=0.0)
    def compute(self, inputs, outputs):
        outputs['Dz'] = 4*inputs['x'] - 2.5*inputs['y']

class CD(Group):

    def initialize(self): 
        self.options.declare('owns_indeps', types=bool, default=True)

    def setup(self):
        if self.options['owns_indeps']: 
            indeps = IndepVarComp()
            indeps.add_output('x', 0.0)
            indeps.add_output('y', 0.0)
            self.add_subsystem('indeps', indeps, promotes=['*'])

        self.add_subsystem('C', C(), promotes=['*'])
        self.add_subsystem('D', D(), promotes=['*'])


class ABCD(Group):
    def setup(self):
        self.add_subsystem('AB', AB(owns_indeps=False))
        self.add_subsystem('CD', CD(owns_indeps=False))

        indeps = IndepVarComp()
        indeps.add_output('xx', 0.0)
        indeps.add_output('yy', 0.0)
        self.add_subsystem('indeps', indeps, promotes=['*'])
        self.connect('xx', ['AB.x', 'CD.x'])
        self.connect('yy', ['AB.y', 'CD.y'])


class ABCD_ALT(Group): 
    """Alternate approach that would not require more than one group class at all""" 

    def initialize(self): 
        self.options.declare('mode', values=['AB', 'CD', 'ABCD'], default='AB')

    def setup(self): 
        mode = self.options['mode']

        indeps = IndepVarComp()
        indeps.add_output('xx', 0.0)
        indeps.add_output('yy', 0.0)
        self.add_subsystem('indeps', indeps, promotes=['*'])


        if 'AB' in mode: 
            self.add_subsystem('A', A(), promotes=['*'])
            self.add_subsystem('B', B(), promotes=['*'])

        if 'CD' in mode: 
            self.add_subsystem('C', C(), promotes=['*'])
            self.add_subsystem('D', D(), promotes=['*'])
        self.connect('xx', 'x')
        self.connect('yy', 'y')


if __name__ == "__main__": 

    from openmdao.api import Problem

    p = Problem()

    # p.model = AB()
    # p.model = CD()
    p.model = ABCD()


    # p.model = ABCD_ALT(mode='AB')

    p.setup()