(Openmdao 2.4.0) Функция 'compute_partials' Компонента, кажется, запускается, даже когда вынуждаете 'Declare_partials' к FD для этого компонента - PullRequest
0 голосов
/ 28 января 2019

Я хочу решить MDA для Селлара, используя нелинейный решатель Ньютона для Группы.Я определил Дисциплины с производными (используя 'compute_partials'), но я хочу проверить количество обращений к Дисциплине 'compute' и 'compute_partials', когда вынуждаем или нет дисциплины не использовать свои аналитические производные (используя 'Declare_partials' в Задачеопределение).Проблема в том, что кажется, что функция 'compute_partials' все еще вызывается, хотя я заставляю ее не использовать.Вот пример (Sellar)

Итак, для Дисциплины 2 я добавляю счетчик и у меня есть

from openmdao.test_suite.components.sellar import SellarDis1, SellarDis2 

class SellarDis2withDerivatives(SellarDis2):
    """
    Component containing Discipline 2 -- derivatives version.
    """

    def _do_declares(self):
        # Analytic Derivs
        self.declare_partials(of='*', wrt='*')
        self.exec_count_d = 0

    def compute_partials(self, inputs, J):
        """
        Jacobian for Sellar discipline 2.
        """
        y1 = inputs['y1']
        if y1.real < 0.0:
            y1 *= -1

        J['y2', 'y1'] = .5*y1**-.5
        J['y2', 'z'] = np.array([[1.0, 1.0]])
        self.exec_count_d += 1

Я создаю аналогичный MDA как в документах OpendMDAO , новызывая SellarDis2withDerivatives, которые я создал, и SellarDis1withDerivatives и меняя нелинейный_сольвер для Newton_solver () следующим образом

    cycle.add_subsystem('d1', SellarDis1withDerivatives(), promotes_inputs=['x', 'z', 'y2'], promotes_outputs=['y1'])
    cycle.add_subsystem('d2', SellarDis2withDerivatives(), promotes_inputs=['z', 'y1'], promotes_outputs=['y2'])

    # Nonlinear Block Gauss Seidel is a gradient free solver
    cycle.nonlinear_solver = NewtonSolver()
    cycle.linear_solver = DirectSolver()

Затем я запускаю следующую задачу

 prob2 = Problem()

prob2.model = SellarMDA()

prob2.setup()

prob2.model.cycle.d1.declare_partials('*', '*', method='fd')
prob2.model.cycle.d2.declare_partials('*', '*', method='fd')

prob2['x'] = 2.
prob2['z'] = [-1., -1.]

prob2.run_model()


count = prob2.model.cycle.d2.exec_count_d
print("Number of derivatives calls (%i)"% (count))

И в результате я получаю

=====

цикл

NL: ньютон сходится за 3 итерации Количество вызовов производных (3)

Следовательно, похоже, что функция'compute_partials' все еще называется как-то (даже если производные вычисляются с помощью FD).Есть ли у кого объяснение?

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Другим обходным решением является наличие в вашем классе атрибута (здесь называемого _call_compute_partials), который отслеживает, если там, где объявлены какие-либо аналитические производные.А условное в compute_partials() может быть реализовано вне метода, где метод вызывается.

from openmdao.core.explicitcomponent import ExplicitComponent
from openmdao.core.indepvarcomp import IndepVarComp
from openmdao.core.problem import Problem
from openmdao.drivers.scipy_optimizer import ScipyOptimizeDriver


class ExplicitComponent2(ExplicitComponent):

    def __init__(self, **kwargs):
        super(ExplicitComponent2, self).__init__(**kwargs)
        self._call_compute_partials = False

    def declare_partials(self, of, wrt, dependent=True, rows=None, cols=None, val=None,
                         method='exact', step=None, form=None, step_calc=None):
        if method == 'exact':
            self._call_compute_partials = True
        super(ExplicitComponent2, self).declare_partials(of, wrt, dependent, rows, cols, val,
                         method, step, form, step_calc)


class Cylinder(ExplicitComponent2):
    """Main class"""

    def setup(self):
        self.add_input('radius', val=1.0)
        self.add_input('height', val=1.0)

        self.add_output('Area', val=1.0)
        self.add_output('Volume', val=1.0)

        # self.declare_partials('*', '*', method='fd')
        # self.declare_partials('*', '*')

        self.declare_partials('Volume', 'height', method='fd')
        self.declare_partials('Volume', 'radius', method='fd')
        self.declare_partials('Area', 'height', method='fd')
        self.declare_partials('Area', 'radius')
        # self.declare_partials('Area', 'radius', method='fd')

    def compute(self, inputs, outputs):
        radius = inputs['radius']
        height = inputs['height']

        area = height * radius * 2 * 3.14 + 3.14 * radius ** 2 * 2
        volume = 3.14 * radius ** 2 * height
        outputs['Area'] = area
        outputs['Volume'] = volume

    def compute_partials(self, inputs, partials):
        if self._call_compute_partials:
            print('Calculate partials...')


if __name__ == "__main__":

    prob = Problem()

    indeps = prob.model.add_subsystem('indeps', IndepVarComp(), promotes=['*'])
    indeps.add_output('radius', 2.)  # height
    indeps.add_output('height', 3.)  # radius
    main = prob.model.add_subsystem('cylinder', Cylinder(), promotes=['*'])

    # setup the optimization
    prob.driver = ScipyOptimizeDriver()

    prob.model.add_design_var('radius', lower=0.5, upper=5.)
    prob.model.add_design_var('height', lower=0.5, upper=5.)
    prob.model.add_objective('Area')
    prob.model.add_constraint('Volume', lower=10.)

    prob.setup()
    prob.run_driver()
    print(prob['Volume'])  # should be around 10
0 голосов
/ 28 января 2019

Я считаю, что это ошибка (или, возможно, непреднамеренное следствие того, как указаны производные.)

Это поведение является побочным продуктом смешанного объявления производных, где мы позволяем пользователю указать некоторыепроизводные на компоненте должны быть 'fd' и другие производные быть аналитическими.Таким образом, мы всегда способны выполнять как fd, так и compute_partials для компонента.

В openmdao можно внести два изменения, чтобы исправить это:

  1. Не вызывайте compute_partials, если никакие производные не были явно объявлены как аналитические.

  2. Отфильтруйте все переменные, объявленные как 'fd', чтобы, если пользователь попытается установить их в compute_partials, возникла ключевая ошибка (или, возможно, просто предупреждение, и производное значение не было перезаписано)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...