OpenMDAO:如何处理 ExplicitComponent.compute 中的非收敛点?
OpenMDAO: How to handle non-converging points in ExplicitComponent.compute?
我已经按照 What is the best way to tell openMDAO driver or solver that it is impossible to evaluate the model at some point? 中的建议(最初是我想在此线程中发表评论,但由于我的 Stack Overflow 信誉评分较低而不允许我发表评论)。但是,这似乎并不能解决我的问题。我期望的是错误会被捕获,设计点会被跳过,优化器会继续评估其他点以找到解决方案。错误被捕获是正确的,但由于某种原因,错误随后在 ScipyOptimizeDriver.run
中重新出现。这样做的目的是什么?
这是重现行为的示例脚本:
import numpy as np
from openmdao.api import Problem, Group, IndepVarComp, ExplicitComponent, ScipyOptimizeDriver, ScipyKrylov, AnalysisError
class Test1Comp(ExplicitComponent):
def setup(self):
self.add_input('design_x', 1.0)
self.add_input('design_y', 1.0)
self.add_input('design_z', 0.5)
self.add_output('y', val=0.1)
self.add_output('z', val=0.1)
self.add_output('obj', val=0.0)
self.declare_partials(of='*', wrt='*', method='fd', form='central', step=1.0e-4)
def compute(self, inputs, outputs):
design_z = inputs['design_z']
design_x = inputs['design_x']
design_y = inputs['design_y']
# Let's assume we have a model that has problems converging around design_x = 5.0
if 0.49999 < design_x < 0.500001:
raise AnalysisError()
z = 4/(design_z + 1)
y = - design_z - 2*z
obj = (y/5.833333 - design_x)**2 + z/2.666667*100*(design_y - design_x**2)**2
outputs["z"] = z
outputs["y"] = y
outputs['obj'] = obj
if __name__ == "__main__":
prob = Problem()
model = prob.model = Group()
model.add_subsystem('d1', IndepVarComp('design_x', 1.0))
model.add_subsystem('d2', IndepVarComp('design_y', 1.0))
model.add_subsystem('d3', IndepVarComp('design_z', 0.5))
model.add_subsystem('comp', Test1Comp())
model.connect('d1.design_x', 'comp.design_x')
model.connect('d2.design_y', 'comp.design_y')
model.connect('d3.design_z', 'comp.design_z')
prob.driver = ScipyOptimizeDriver()
prob.driver.options["optimizer"] = 'SLSQP'
prob.driver.options['tol'] = 1e-8
model.add_design_var("d1.design_x", lower=0.5, upper=1.5)
model.add_design_var("d2.design_y", lower=0.5, upper=1.5)
model.add_design_var("d3.design_z", lower=0.0, upper=1.0)
model.add_objective('comp.obj')
model.linear_solver = ScipyKrylov()
model.linear_solver.options['maxiter'] = int(1200)
model.linear_solver.options['restart'] = int(20)
# prob.model.approx_totals()
prob.setup()
prob.run_driver()
print(prob['comp.y'])
print(prob['comp.z'])
此外,在查看调用 ExplicitComponent.compute
的方法 ExplicitComponent._solve_nonlinear
时,在我看来,与 OpenMDAO 沟通的自然方式是点不收敛 [= =18=] return True
。方法见源码:
def _solve_nonlinear(self):
"""
Compute outputs. The model is assumed to be in a scaled state.
Returns
-------
boolean
Failure flag; True if failed to converge, False is successful.
float
absolute error.
float
relative error.
"""
super(ExplicitComponent, self)._solve_nonlinear()
with Recording(self.pathname + '._solve_nonlinear', self.iter_count, self):
with self._unscaled_context(
outputs=[self._outputs], residuals=[self._residuals]):
self._residuals.set_const(0.0)
failed = self.compute(self._inputs, self._outputs)
return bool(failed), 0., 0.
总而言之,有人可以阐明在 ExplicitComponent.compute
中处理非收敛计算的推荐方法是什么吗?
我看过你的代码,你指定了所有正确的方式来告诉优化器组件无法评估设计点。问题是 scipy.minimize(ScipyOptimizeDriver
下的优化器)不知道在遇到失败点时该做什么(除了引发异常),并且无法报告失败回来(至少据我所知)。
然而,pyOptSparseDriver
可以在点失败时做一些事情:尝试在梯度方向上再次前进,但步长较小。我拿了你的代码,并用 pyOptSparseDriver
交换了 ScipyOptimizeDriver
,使用了那个包中的 "SLSQP" 优化器,它很好地解决了那个问题点,达到了我认为是好的最优:
[0.69651727]
[-5.]
[2.]
我的驱动程序选项代码如下所示:
prob.driver = pyOptSparseDriver()
prob.driver.options["optimizer"] = 'SLSQP'
prob.driver.opt_settings['ACC'] = 1e-8
如果您还没有 pyoptsparse,它是一个我们不维护的独立包,您可以从 https://github.com/mdolab/pyoptsparse 获取它——您可以使用以下方法构建和安装它:
python setup.py build
python setup.py install
对于您的其他问题,我查看了我们的代码,发现 _solve_nonlinear
上的失败标志 return 从未用于任何用途。因此,将非收敛状态传达给驱动程序或更高级别的求解器的唯一方法是引发 AnalysisError。如果 "err_on_maxiter" 的选项设置为 True(默认为 False),求解器可以在不收敛时引发 AE。
最后一点,我认为虽然我们的错误处理机制正在被使用,但我们并没有考虑到所有的事情,并且总是乐于接受改进建议。
我已经按照 What is the best way to tell openMDAO driver or solver that it is impossible to evaluate the model at some point? 中的建议(最初是我想在此线程中发表评论,但由于我的 Stack Overflow 信誉评分较低而不允许我发表评论)。但是,这似乎并不能解决我的问题。我期望的是错误会被捕获,设计点会被跳过,优化器会继续评估其他点以找到解决方案。错误被捕获是正确的,但由于某种原因,错误随后在 ScipyOptimizeDriver.run
中重新出现。这样做的目的是什么?
这是重现行为的示例脚本:
import numpy as np
from openmdao.api import Problem, Group, IndepVarComp, ExplicitComponent, ScipyOptimizeDriver, ScipyKrylov, AnalysisError
class Test1Comp(ExplicitComponent):
def setup(self):
self.add_input('design_x', 1.0)
self.add_input('design_y', 1.0)
self.add_input('design_z', 0.5)
self.add_output('y', val=0.1)
self.add_output('z', val=0.1)
self.add_output('obj', val=0.0)
self.declare_partials(of='*', wrt='*', method='fd', form='central', step=1.0e-4)
def compute(self, inputs, outputs):
design_z = inputs['design_z']
design_x = inputs['design_x']
design_y = inputs['design_y']
# Let's assume we have a model that has problems converging around design_x = 5.0
if 0.49999 < design_x < 0.500001:
raise AnalysisError()
z = 4/(design_z + 1)
y = - design_z - 2*z
obj = (y/5.833333 - design_x)**2 + z/2.666667*100*(design_y - design_x**2)**2
outputs["z"] = z
outputs["y"] = y
outputs['obj'] = obj
if __name__ == "__main__":
prob = Problem()
model = prob.model = Group()
model.add_subsystem('d1', IndepVarComp('design_x', 1.0))
model.add_subsystem('d2', IndepVarComp('design_y', 1.0))
model.add_subsystem('d3', IndepVarComp('design_z', 0.5))
model.add_subsystem('comp', Test1Comp())
model.connect('d1.design_x', 'comp.design_x')
model.connect('d2.design_y', 'comp.design_y')
model.connect('d3.design_z', 'comp.design_z')
prob.driver = ScipyOptimizeDriver()
prob.driver.options["optimizer"] = 'SLSQP'
prob.driver.options['tol'] = 1e-8
model.add_design_var("d1.design_x", lower=0.5, upper=1.5)
model.add_design_var("d2.design_y", lower=0.5, upper=1.5)
model.add_design_var("d3.design_z", lower=0.0, upper=1.0)
model.add_objective('comp.obj')
model.linear_solver = ScipyKrylov()
model.linear_solver.options['maxiter'] = int(1200)
model.linear_solver.options['restart'] = int(20)
# prob.model.approx_totals()
prob.setup()
prob.run_driver()
print(prob['comp.y'])
print(prob['comp.z'])
此外,在查看调用 ExplicitComponent.compute
的方法 ExplicitComponent._solve_nonlinear
时,在我看来,与 OpenMDAO 沟通的自然方式是点不收敛 [= =18=] return True
。方法见源码:
def _solve_nonlinear(self):
"""
Compute outputs. The model is assumed to be in a scaled state.
Returns
-------
boolean
Failure flag; True if failed to converge, False is successful.
float
absolute error.
float
relative error.
"""
super(ExplicitComponent, self)._solve_nonlinear()
with Recording(self.pathname + '._solve_nonlinear', self.iter_count, self):
with self._unscaled_context(
outputs=[self._outputs], residuals=[self._residuals]):
self._residuals.set_const(0.0)
failed = self.compute(self._inputs, self._outputs)
return bool(failed), 0., 0.
总而言之,有人可以阐明在 ExplicitComponent.compute
中处理非收敛计算的推荐方法是什么吗?
我看过你的代码,你指定了所有正确的方式来告诉优化器组件无法评估设计点。问题是 scipy.minimize(ScipyOptimizeDriver
下的优化器)不知道在遇到失败点时该做什么(除了引发异常),并且无法报告失败回来(至少据我所知)。
然而,pyOptSparseDriver
可以在点失败时做一些事情:尝试在梯度方向上再次前进,但步长较小。我拿了你的代码,并用 pyOptSparseDriver
交换了 ScipyOptimizeDriver
,使用了那个包中的 "SLSQP" 优化器,它很好地解决了那个问题点,达到了我认为是好的最优:
[0.69651727]
[-5.]
[2.]
我的驱动程序选项代码如下所示:
prob.driver = pyOptSparseDriver()
prob.driver.options["optimizer"] = 'SLSQP'
prob.driver.opt_settings['ACC'] = 1e-8
如果您还没有 pyoptsparse,它是一个我们不维护的独立包,您可以从 https://github.com/mdolab/pyoptsparse 获取它——您可以使用以下方法构建和安装它:
python setup.py build
python setup.py install
对于您的其他问题,我查看了我们的代码,发现 _solve_nonlinear
上的失败标志 return 从未用于任何用途。因此,将非收敛状态传达给驱动程序或更高级别的求解器的唯一方法是引发 AnalysisError。如果 "err_on_maxiter" 的选项设置为 True(默认为 False),求解器可以在不收敛时引发 AE。
最后一点,我认为虽然我们的错误处理机制正在被使用,但我们并没有考虑到所有的事情,并且总是乐于接受改进建议。