openMDAO: оптимизация успешно завершается после 1 итерации, но не в оптимальной точке - PullRequest
0 голосов
/ 27 сентября 2019

Я пытаюсь сделать игрушечную задачу, чтобы немного узнать о программном обеспечении OpenMDAO, прежде чем применять уроки к более крупной проблеме.У меня есть проблема, настроенная так, чтобы целевая функция была минимизирована, когда обе проектные переменные минимальны.Однако оба значения остаются на своих первоначально присвоенных значениях, несмотря на получение сообщения «Оптимизация успешно завершена».

Я начал с написания кода, основанного на примерах проблемы Sellar.(http://openmdao.org/twodocs/versions/latest/basic_guide/sellar.html) Кроме того, я столкнулся с вопросом переполнения стека, который, кажется, та же самая проблема, но решение там не работает.( OpenMDAO: Solver сходится к неоптимальной точке ) (Когда я добавляю строку Declare_partials в IntermediateCycle или ScriptForTest, я получаю сообщение о том, что либо self не определено, либо что у объекта нет атрибута Declare_partials)

Это скрипт, который запускает все

import openmdao.api as om
from IntermediateForTest import IntermediateCycle

prob = om.Problem()
prob.model = IntermediateCycle()

prob.driver = om.ScipyOptimizeDriver()
#prob.driver.options['optimizer'] = 'SLSQP'
#prob.driver.options['tol'] = 1e-9

prob.model.add_design_var('n_gear', lower=2, upper=6)
prob.model.add_design_var('stroke', lower=0.0254, upper=1)

prob.model.add_objective('objective')

prob.setup()

prob.model.approx_totals()

prob.run_driver()

print(prob['objective'])
print(prob['cycle.f1.total_weight'])
print(prob['cycle.f1.stroke'])
print(prob['cycle.f1.n_gear'])

Вызывает промежуточную группу, как в примере с Sellar

import openmdao.api as om
from FunctionsForTest import FunctionForTest1
from FunctionsForTest import FunctionForTest2

class IntermediateCycle(om.Group):
    def setup(self):
        indeps = self.add_subsystem('indeps', om.IndepVarComp(), promotes=['*'])
        indeps.add_output('n_gear', 3.0)
        indeps.add_output('stroke', 0.2)
        indeps.add_output('total_weight', 26000.0)

        cycle = self.add_subsystem('cycle', om.Group())

        cycle.add_subsystem('f1', FunctionForTest1())
        cycle.add_subsystem('f2', FunctionForTest2())

        cycle.connect('f1.landing_gear_weight','f2.landing_gear_weight')
        cycle.connect('f2.total_weight','f1.total_weight')

        self.connect('n_gear','cycle.f1.n_gear')
        self.connect('stroke','cycle.f1.stroke')        

        #cycle.nonlinear_solver = om.NonlinearBlockGS()

        self.nonlinear_solver = om.NonlinearBlockGS()

        self.add_subsystem('objective', om.ExecComp('objective = total_weight', objective=26000, total_weight=26000), promotes=['objective', 'total_weight'])

Наконец, есть файл с двумяфункции в нем:

import openmdao.api as om

class FunctionForTest1(om.ExplicitComponent):
    def setup(self):
        self.add_input('stroke', val=0.2)
        self.add_input('n_gear', val=3.0)
        self.add_input('total_weight', val=26000)

        self.add_output('landing_gear_weight')

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

    def compute(self, inputs, outputs):
        stroke = inputs['stroke']
        n_gear = inputs['n_gear']
        total_weight = inputs['total_weight']

        outputs['landing_gear_weight'] = total_weight * 0.1 + 100*stroke * n_gear ** 2

class FunctionForTest2(om.ExplicitComponent):
    def setup(self):
        self.add_input('landing_gear_weight')

        self.add_output('total_weight')

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

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

        outputs['total_weight'] = 26000 + landing_gear_weight

Он сообщает, что оптимизация успешно завершена,

Optimization terminated successfully.    (Exit mode 0)
            Current function value: 26000.0
            Iterations: 1
            Function evaluations: 1
            Gradient evaluations: 1
Optimization Complete
-----------------------------------
[26000.]
[29088.88888889]
[0.2]
[3.]

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

Он достигает 29088,9, что правильно для значения n_gear = 3 и хода = 0,2, но если оба значения уменьшатся до границ n_gear = 2 и stroke = 0.0254, получится значение ~ 28900, меньше ~ 188.

Буду признателен за любые советы, ссылки на учебные пособия или решения..

1 Ответ

3 голосов
/ 27 сентября 2019

Давайте взглянем на n2 модели, как вы ее предоставили: n2 diagram of test model

Я выделил соединение от indeps.total_weight до objective.total_weight.Таким образом, это означает, что ваше вычисленное значение total_weight вообще не передается в ваш целевой результат.Вместо этого у вас есть постоянное значение, установленное там.

Теперь, сделав небольшой шаг назад, давайте посмотрим на вычисление самой цели:

self.add_subsystem('objective', om.ExecComp('objective = total_weight', objective=26000, total_weight=26000), promotes=['objective', 'total_weight'])

Так что это странное использование ExecComp, потому что он просто устанавливает вывод вименно вход.Это ничего не делает, и на самом деле не нужно вообще.

Я полагаю, что вы хотели просто сделать цель выходной f2.total_weight.Когда я делаю это (и делаю несколько небольших небольших очисток вашего кода, например, удаляя ненужный ExecComp, я получаю правильный ответ в двух основных итерациях оптимизатора:

import openmdao.api as om

class FunctionForTest1(om.ExplicitComponent):
    def setup(self):
        self.add_input('stroke', val=0.2)
        self.add_input('n_gear', val=3.0)
        self.add_input('total_weight', val=26000)

        self.add_output('landing_gear_weight')

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

    def compute(self, inputs, outputs):
        stroke = inputs['stroke']
        n_gear = inputs['n_gear']
        total_weight = inputs['total_weight']

        outputs['landing_gear_weight'] = total_weight * 0.1 + 100*stroke * n_gear ** 2

class FunctionForTest2(om.ExplicitComponent):
    def setup(self):
        self.add_input('landing_gear_weight')

        self.add_output('total_weight')

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

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

        outputs['total_weight'] = 26000 + landing_gear_weight

class IntermediateCycle(om.Group):
    def setup(self):
        indeps = self.add_subsystem('indeps', om.IndepVarComp(), promotes=['*'])
        indeps.add_output('n_gear', 3.0)
        indeps.add_output('stroke', 0.2)

        cycle = self.add_subsystem('cycle', om.Group())

        cycle.add_subsystem('f1', FunctionForTest1())
        cycle.add_subsystem('f2', FunctionForTest2())

        cycle.connect('f1.landing_gear_weight','f2.landing_gear_weight')
        cycle.connect('f2.total_weight','f1.total_weight')

        self.connect('n_gear','cycle.f1.n_gear')
        self.connect('stroke','cycle.f1.stroke')        

        #cycle.nonlinear_solver = om.NonlinearBlockGS()

        self.nonlinear_solver = om.NonlinearBlockGS()


prob = om.Problem()
prob.model = IntermediateCycle()

prob.driver = om.ScipyOptimizeDriver()
#prob.driver.options['optimizer'] = 'SLSQP'
#prob.driver.options['tol'] = 1e-9

prob.model.add_design_var('n_gear', lower=2, upper=6)
prob.model.add_design_var('stroke', lower=0.0254, upper=1)

prob.model.add_objective('cycle.f2.total_weight')

prob.model.approx_totals()


prob.setup()

prob.model.nl_solver.options['iprint'] = 2

prob.run_driver()


print(prob['cycle.f1.total_weight'])
print(prob['cycle.f2.total_weight'])
print(prob['cycle.f1.stroke'])
print(prob['cycle.f1.n_gear'])

дает:

Optimization terminated successfully.    (Exit mode 0)
            Current function value: 28900.177777779667
            Iterations: 2
            Function evaluations: 2
            Gradient evaluations: 2
Optimization Complete
-----------------------------------
[28900.1777778]
[28900.17777778]
[0.0254]
[2.]
...