OpenMDAO优化中Newton求解器二次运行收敛失败问题咨询
我在OpenMDAO中使用om.ScipyOptimizeDriver(optimizer='SLSQP')驱动包含Newton求解器的隐式组件模型时,发现驱动会对初始点进行两次计算:第一次求解器收敛正常,第二次却收敛失败。相关输出如下:
Design variables: T = 119.9, r = [8. 8. 8.] ====== cycle1 ====== NL: Newton 0 ; 1.08292488e+09 1 NL: Newton 1 ; 87515540.2 0.0808140455 NL: Newton 2 ; 2017419.33 0.00186293562 NL: Newton 3 ; 127.643094 1.17868835e-07 NL: Newton 4 ; 0.00112882079 1.04238143e-12 NL: Newton Converged Design variables: T = 119.9, r = [8. 8. 8.] ====== cycle1 ====== NL: Newton 0 ; 0.00112882079 1 NL: Newton 1 ; 3.40715571e-05 0.0301833183 NL: Newton 2 ; 3.51611217e-05 0.031148542 NL: Newton 3 ; 3.01547334e-05 0.0267134817 NL: Newton 4 ; 2.10780761e-05 0.0186726506 NL: Newton 5 ; 1.78959329e-05 0.0158536529 NL: Newton 6 ; 3.52074448e-05 0.0311895788 NL: Newton 7 ; 3.21214883e-05 0.028455791 ... NL: Newton 91 ; 2.47905401e-05 0.021961449 NL: Newton 92 ; 1.78168883e-05 0.0157836288 NL: Newton 93 ; 1.24818173e-05 0.011057395 NL: Newton 94 ; 2.81148656e-05 0.0249064032 NL: Newton 95 ; 1.98432406e-05 0.0175787342 NL: Newton 96 ; 2.33725536e-05 0.0207052828 NL: Newton 97 ; 1.88897422e-05 0.0167340489 NL: Newton 98 ; 4.77892572e-05 0.0423355575 NL: Newton 99 ; 4.26819068e-05 0.0378110568 NL: Newton 100 ; 2.64288629e-05 0.0234128067 NL: NewtonSolver 'NL: Newton' on system 'cycle1' failed to converge in 100 iterations.
我注意到第二次运行时求解器以上一次收敛的输出值为起点,这是否是导致无法进一步降低残差从而收敛失败的原因?若如此,该如何解决此问题?
原因分析与解决方案
你观察到的现象就是核心原因:第二次Newton求解器从第一次收敛后的状态启动,引发了残差震荡无法收敛。SLSQP驱动在初始阶段会先执行一次正向分析(计算目标/约束),紧接着触发梯度计算(默认有限差分),梯度计算时会复用系统前一次的状态,导致Newton从已收敛的极小残差点开始迭代——此时Newton法的步长调整、雅可比矩阵精度问题会让残差出现震荡,无法进一步收敛到满足容限的范围。
以下是几种可行的解决方法:
强制重置求解器初始状态
在隐式组件的solve_nonlinear方法开头,手动将状态变量重置为初始猜测值,确保每次求解都从干净的起点开始:class MyImplicitComp(om.ImplicitComponent): def initialize(self): self.options.declare('initial_guess', default=0.0) def solve_nonlinear(self, inputs, outputs, residuals): # 重置状态到初始猜测值 outputs['state_var'] = self.options['initial_guess'] # 执行Newton求解 super().solve_nonlinear(inputs, outputs, residuals)开启求解器的
reset_on_run参数
给Newton求解器设置reset_on_run=True,确保每次调用求解器时自动重置状态到初始值,而非复用之前的收敛结果:# 假设cycle1是你的隐式系统 cycle1.nonlinear_solver = om.NewtonSolver() cycle1.nonlinear_solver.reset_on_run = True # 可同时配置其他求解器参数,比如容限 cycle1.nonlinear_solver.atol = 1e-10 cycle1.nonlinear_solver.rtol = 1e-10改用解析梯度替代有限差分
有限差分的步长扰动可能引发求解器的异常迭代,若能提供解析梯度,不仅能提升计算效率,还能让梯度计算过程更稳定。在隐式组件中实现linearize方法定义雅可比矩阵:class MyImplicitComp(om.ImplicitComponent): def linearize(self, inputs, outputs, jacobian): # 填充残差对状态变量的雅可比元素 jacobian['residual_name', 'state_var'] = ... # 填充残差对输入变量的雅可比元素(如果需要) jacobian['residual_name', 'input_var'] = ...调整求解器收敛参数(备选)
如果残差震荡的数值本身很小,可适当调小Newton求解器的绝对/相对容限,或增加最大迭代次数,但这只是临时缓解,优先推荐前面三种根治方法:cycle1.nonlinear_solver.maxiter = 200 cycle1.nonlinear_solver.atol = 1e-12
内容的提问来源于stack exchange,提问作者Kasia

