OpenMDAO2 限制模块化软件架构
OpenMDAO2 restricting modular software architecture
OpenMDAO2 对组连接和 input/output 变量的限制阻碍了我编写简洁、模块化软件的愿望
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 的组合都不会给我 "inputs with multiple connections".
错误
在 OpenMDAO 1.x 中,我能够通过从较低级别组(AB、CD)中删除 IndepVarComps 并仅在最高级别组()中使用它们来解决这个问题。但是,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。我错过了什么吗?欢迎任何帮助或指导。
在您的简单示例中,我可以看到两种解决问题的方法。两者都涉及对组的选项的使用。由于我不确定哪种方式效果更好,因此我将这两种方式都包含在下面的示例中。
一种方法是,如果 AB/CD 拥有自己的独立部门,则将其设为可选。然后您可以根据需要切换所需的行为。这行得通,但我个人认为它很乱。
第二个选项是只创建一个组,但使用选项来控制 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()
OpenMDAO2 对组连接和 input/output 变量的限制阻碍了我编写简洁、模块化软件的愿望
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 的组合都不会给我 "inputs with multiple connections".
错误在 OpenMDAO 1.x 中,我能够通过从较低级别组(AB、CD)中删除 IndepVarComps 并仅在最高级别组(
class AB(Group):
def setup(self):
self.add_subsystem('A', A(), promotes=['*'])
self.add_subsystem('B', B(), promotes=['*'])
现在我坚持维护相同代码的多个副本,一个副本用于 AB,另一个副本用于 ABCD,或者只是手动连接纯 python 中的模块并远离 OpenMDAO。我错过了什么吗?欢迎任何帮助或指导。
在您的简单示例中,我可以看到两种解决问题的方法。两者都涉及对组的选项的使用。由于我不确定哪种方式效果更好,因此我将这两种方式都包含在下面的示例中。
一种方法是,如果 AB/CD 拥有自己的独立部门,则将其设为可选。然后您可以根据需要切换所需的行为。这行得通,但我个人认为它很乱。
第二个选项是只创建一个组,但使用选项来控制 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()