OpenMDAO2 ограничивает модульную программную архитектуру - PullRequest
0 голосов
/ 11 мая 2019

Ограничения 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=['*'])

Иногда мне бы хотелось работать только с Group AB (запускать сценарии, оптимизировать и т. Д.), А иногда я хотел бы работать только с Group 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 я смог обойти это, удалив IndepVarComps из групп нижнего уровня (AB, CD) и используя их только в группе самого высокого уровня ( см. Ответ ). Однако 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 Ответ

3 голосов
/ 11 мая 2019

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

Один из способов - сделать его необязательным, если у 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()
...