openMDAO:优化在 1 次迭代后成功终止,而不是在最佳点

openMDAO: Optimization terminates successfully after 1 iteration, not at optimal point

我正在尝试做一个玩具问题,以便在将课程应用到更大的问题之前了解一些 OpenMDAO 软件。我设置了一个问题,因此当两个设计变量都处于最小值时,objective 函数应该最小化。然而,尽管收到 'Optimization terminated successfully' 消息,这两个值仍保持其最初分配的值。

我已经开始根据 Sellar 问题示例编写代码。 ( http://openmdao.org/twodocs/versions/latest/basic_guide/sellar.html ) Additionally I have come across a stack overflow question that seems to be the same problem, but the solution there doesn't work. ( ) (当我将 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和stroke=0.2的值是正确的,但是如果两者都减少到n_gear=2和stroke=0.0254的界限,它会达到 ~28900 的价值,少了 ~188。

如有任何建议、教程链接或解决方案,我们将不胜感激。

让我们看一下您提供的模型的 n2:

我已经突出显示了从 indeps.total_weightobjective.total_weight 的联系。因此,这意味着您计算出的 total_weight 值根本没有传递给您的 objective 输出。相反,您在那里设置了一个常量值。

现在,退一步,让我们看看 objective 本身的计算:

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

所以这是 ExecComp 的一种奇怪用法,因为它只是将输出精确设置为输入。它什么都不做,也根本不需要。

我相信你想要的只是让 objective 成为输出 f2.total_weight。当我这样做时(并对您的代码进行一些额外的小清理,例如删除不必要的 ExecComp,然后我在优化器的 2 个主要迭代中得到了正确的答案:

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.]