Как обновить размеры соединения в реконфигурируемой модели в OpenMDAO 2.5.0? - PullRequest
0 голосов
/ 09 ноября 2018

С реконфигурируемым исполнением модели можно изменять размеры входов и выходов компонентов. Как обновляются соединения, когда переконфигурированные выходы и входы подключены?

В приведенном ниже примере размеры c2.y и c3.y изменяются при каждом запуске модели. Предполагается, что этот вход и выход подключены, как показано на диаграмме N2. Однако после реконфигурации размер соединения, кажется, не обновляется автоматически, он выдает следующую ошибку:

ValueError: The source and target shapes do not match or are ambiguous for the connection 'c2.y' to 'c3.y'. Expected (1,) but got (2,).

Я включил ниже 3 теста с продвинутым соединением, абсолютным соединением и последний с реконфигурацией, но без соединения (который работает).

Последним шансом было бы объявить соединение в родительской группе comps, что я еще не пробовал.

imagec2 and c3 are reconfigurable components">

Тесты:

  1. Повышенное соединение
  2. Абсолютное соединение
  3. Нет связи

Переконфигурируемые классы компонентов и тесты:

from __future__ import division

import logging

import numpy as np
import unittest

from openmdao.api import Problem, Group, IndepVarComp, ExplicitComponent
from openmdao.utils.assert_utils import assert_rel_error


class ReconfComp(ExplicitComponent):

    def initialize(self):
        self.size = 1
        self.counter = 0

    def reconfigure(self):
        logging.info('reconf started {}'.format(self.pathname))
        self.counter += 1
        logging.info('reconf ended {}'.format(self.pathname))

        if self.counter % 2 == 0:
            self.size += 1
            return True
        else:
            return False

    def setup(self):
        logging.info('setup started {}'.format(self.pathname))
        self.add_input('x', val=1.0)
        self.add_output('y', val=np.zeros(self.size))
        # All derivatives are defined.
        self.declare_partials(of='*', wrt='*')
        logging.info('setup ended {}'.format(self.pathname))

    def compute(self, inputs, outputs):
        logging.info('compute started {}'.format(self.pathname))
        outputs['y'] = 2 * inputs['x']
        logging.info('compute ended {}'.format(self.pathname))

    def compute_partials(self, inputs, jacobian):
        jacobian['y', 'x'] = 2 * np.ones((self.size, 1))


class ReconfComp2(ReconfComp):
    """The size of the y input changes the same as way as in ReconfComp"""

    def setup(self):
        logging.info('setup started {}'.format(self.pathname))
        self.add_input('y', val=np.zeros(self.size))
        self.add_output('f', val=np.zeros(self.size))
        # All derivatives are defined.
        self.declare_partials(of='*', wrt='*')
        logging.info('setup ended {}'.format(self.pathname))

    def compute(self, inputs, outputs):
        logging.info('compute started {}'.format(self.pathname))
        outputs['f'] = 2 * inputs['y']
        logging.info('compute ended {}'.format(self.pathname))

    def compute_partials(self, inputs, jacobian):
        jacobian['f', 'y'] = 2 * np.ones((self.size, 1))


class TestReconfConnections(unittest.TestCase):

    def test_reconf_comp_promoted_connections(self):
        p = Problem()

        p.model = Group()
        p.model.add_subsystem('c1', IndepVarComp('x', 1.0), promotes_outputs=['x'])
        p.model.add_subsystem('c2', ReconfComp(), promotes_inputs=['x'], promotes_outputs=['y'])
        p.model.add_subsystem('c3', ReconfComp2(), promotes_inputs=['y'],
                              promotes_outputs=['f'])

        p.setup()
        p['x'] = 3.

        # First run the model once; counter = 1, size of y = 1
        p.run_model()
        totals = p.compute_totals(wrt=['x'], of=['y'])
        assert_rel_error(self, p['x'], 3.0)
        assert_rel_error(self, p['y'], 6.0)
        assert_rel_error(self, totals['y', 'x'], [[2.0]])
        print(p['x'], p['y'], totals['y', 'x'].flatten())

        # Run the model again, which will trigger reconfiguration; counter = 2, size of y = 2
        p.run_model()  # FIXME Fails with ValueError

    def test_reconf_comp_connections(self):
        p = Problem()

        p.model = Group()
        p.model.add_subsystem('c1', IndepVarComp('x', 1.0), promotes_outputs=['x'])
        p.model.add_subsystem('c2', ReconfComp(), promotes_inputs=['x'])
        p.model.add_subsystem('c3', ReconfComp2(), promotes_outputs=['f'])
        p.model.connect('c2.y', 'c3.y')
        p.setup()
        p['x'] = 3.

        # First run the model once; counter = 1, size of y = 1
        p.run_model()

        # Run the model again, which will trigger reconfiguration; counter = 2, size of y = 2
        p.run_model()  # FIXME Fails with ValueError

    def test_reconf_comp_not_connected(self):
        p = Problem()

        p.model = Group()
        p.model.add_subsystem('c1', IndepVarComp('x', 1.0), promotes_outputs=['x'])
        p.model.add_subsystem('c2', ReconfComp(), promotes_inputs=['x'])
        p.model.add_subsystem('c3', ReconfComp2(), promotes_outputs=['f'])
        # c2.y not connected to c3.y
        p.setup()
        p['x'] = 3.

        # First run the model once; counter = 1, size of y = 1
        p.run_model()

        # Run the model again, which will trigger reconfiguration; counter = 2, size of y = 2
        fail, _, _ = p.run_model()
        self.assertFalse(fail)


if __name__ == '__main__':
    unittest.main()

UPDATE:

Похоже, что в Group._var_abs2meta обновляется только исходный размер, но не цель. Настройка соединений начинается до того, как будет вызвана настройка родительской группы или настройка другого компонента.

ОБНОВЛЕНИЕ 2:

Это происходит с решателем по умолчанию NonlinearRunOnce, с NewtonSolver, равным NonlinearBlockGS, ошибки нет, но размеры переменных также не меняются.

1 Ответ

0 голосов
/ 10 ноября 2018

Начиная с OpenMDAO V2.5 переменные реконфигурируемой модели официально не поддерживаются в фреймворке. Голая основа возможности была в коде с тех пор, как это исследование было сделано, но это не было тем, что было достаточно приоритетным для нас, чтобы завершить. Недавний крупный рефакторинг в V2.4 переработал работу некоторых базовых структур данных и, должно быть, нарушил эту функциональность.

Это в нашем списке приоритетов развития, чтобы это снова заработало, но оно не слишком высоко в этом списке. Мы фокусируем разработку в основном на функциях, которые имеют прямые собственные приложения, а у нас пока нет ни одной из них.

Если бы вы могли предоставить прилично полный набор тестов для этого, мы могли бы взглянуть на то, как работает функциональность.

...