compute_totals returns 不使用 approx_totals 时的错误全导数

compute_totals returns the wrong total derivatives when approx_totals is not used

我注意到 prob.compute_totals() returns 之前未指定 prob.model.approx_totals() 时的错误答案。通过有限差分手动定义或计算偏导数不会改变任何东西,如果之前没有调用 prob.model.approx_totals() ,答案仍然是错误的。此外,与之前调用 approx_totals 相比,调用 compute_totals 实际上更快。这似乎与手动定义的部分有悖常理,因为 approx_totals 应该添加不必要的有限差分计算。

这是一个 MWE,其中的 Sellar 示例取自 OpenMDAO 文档。我也注意到 OpenAeroStruct 中的相同行为,尽管差异小于本例中的差异。

import openmdao.api as om
from openmdao.test_suite.components.sellar_feature import SellarMDA

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

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

prob.model.add_design_var('x', lower=0, upper=10)
prob.model.add_design_var('z', lower=0, upper=10)
prob.model.add_objective('obj')
prob.model.add_constraint('con1', upper=0)
prob.model.add_constraint('con2', upper=0)

prob.setup()
prob.set_solver_print(level=0)

prob.model.approx_totals() # Commenting this line gives the wrong result

prob.run_driver()
totals = prob.compute_totals(of=['obj'],wrt=['x','z'])

print("""
Obj = {}
x = {}
z = {}
y1 = {}
y2 = {}
Totals = {}""".format(prob['obj'][0],prob['x'][0],prob['z'][0],prob['y1'][0],prob['y2'][0],totals))

好结果,用approx_totals

Optimization terminated successfully.    (Exit mode 0)
            Current function value: 3.183393951729169
            Iterations: 6
            Function evaluations: 6
            Gradient evaluations: 6
Optimization Complete
-----------------------------------

Obj = 3.183393951729169
x = 0.0
z = 1.977638883487764
y1 = 3.1600000000897133
y2 = 3.755277766976125
Totals = OrderedDict([(('obj', 'x'), array([[0.94051147]])), (('obj', 'z'), array([[3.50849282, 1.72901602]]))])

错误的结果,没有 approx_totals :

Optimization terminated successfully.    (Exit mode 0)
            Current function value: 3.1833939532752136
            Iterations: 11
            Function evaluations: 12
            Gradient evaluations: 11
Optimization Complete
-----------------------------------

Obj = 3.1833939532752136
x = 4.401421628747386e-15
z = 1.9776388839289216
y1 = 3.1600000016563765
y2 = 3.755277767857951
Totals = OrderedDict([(('obj', 'x'), array([[0.99341446]])), (('obj', 'z'), array([[3.90585351, 1.97002055]]))])

在此示例中,问题是您在 SellarMDA 中有一个循环,但该模型不包含可以计算整个循环的总导数的线性求解器。检查此问题的一种方法是在命令行中输入 运行 "openmdao check myfilename.py"。我 运行 它在你的模型上,并收到以下警告:

INFO: checking comp_has_no_outputs
INFO: checking dup_inputs
INFO: checking missing_recorders
WARNING: The Problem has no recorder of any kind attached
INFO: checking out_of_order
INFO: checking solvers
WARNING: Group 'cycle' contains cycles [['d1', 'd2']], but does not have an iterative linear solver.
INFO: checking system

对此有一些补救措施。您可以手动添加不同的线性求解器,例如 DirectSolverPETScKrylov 到 "cycle" 组。您也可以导入 SellarMDALinearSolver 而不是 SellarMDA。 SellarMDALinearSolver 使用牛顿求解器收敛循环,使用 DirectSolver 计算导数。 SellarMDA 使用 NonlinearBlockGS 来收敛循环,但遗憾的是不包含适当的线性求解器来计算导数。这些组件用于各种测试角色,但我认为回想起来,我们将来可能应该在 SellarMDA 中添加一个 LinearBlockGS,这样就可以在不修改的情况下计算总导数。不过现在,您必须使用 SellarMDALinearSolver 或自己添加求解器。

顺便说一句,我怀疑优化速度较慢,因为导数太差了。它花费了两倍的迭代次数,尽管它仍然以某种方式设法非常接近答案。

您在 OpenAeroStruct 模型中提到了类似的症状。我怀疑 1) 一个子组件在其解析导数中有错误,或者 2) 线性求解器设置不正确(也许你在某个地方有一个循环,在那个组或父组中没有一个好的线性求解器。)我认为Problem.check_partialsProblem.check_totals 会让您更深入地了解问题出在哪里。关于这些 here.

有更多信息