用混合分析策略逼近整个问题

问题描述

我遇到了问题,我为某些组件实现了分析导数,而我对其余部分使用了复杂的步骤。它们之间存在循环依赖,所以我也使用求解器来收敛它们。当我使用 NonlinearBlockGS 时它会收敛。但是当我将 NewtonSolver 与线性求解器结合使用时,优化失败(超出迭代限制),即使迭代次数很高。但是我发现当我使用 prob.model.approx_totals() 时它很容易收敛并且工作得很好。我读到 approx_totals 使用 fdcs 来查找模型梯度。所以我有两个问题。

  1. 一般来说,当我使用 approx_totals() 时,我会失去混合分析方法的好处吗?有没有办法用混合分析策略找到整个模型(或组)的导数? (无论如何,在我的情况下,耦合的 explicitcomponents 使用“复杂步骤”。但我只是对此感到好奇。)

  2. 一般情况下(不是在这种情况下),Openmdao 会自动检测混合策略还是我应该如何指定它?

如果您能指出一些使用混合导数的例子,我也将不胜感激。我自己没有运气找到它们。

编辑:添加示例。我无法在示例代码中重现该问题。此外,我不想在我的代码上浪费您的时间(有 30 多个显式组件和 7 个组)。所以我在下面做了一个简单的结构来更好地解释它。其中有 7 个分量 A to G,只有 F and G 没有解析导数并使用 FD

import openmdao.api as om
import numpy as np

class ComponentA_withDerivatives(om.ExplicitComponent):
    def setup(self):
        #setup inputs and outputs
    
    def setup_partials(self):
        #partial declaration

    def compute(self,inputs,outputs):

    def compute_partials(self,J):
        #Partial deFinition

class ComponentB_withDerivatives(om.ExplicitComponent):
    .....

class ComponentC_withDerivatives(om.ExplicitComponent):
    ......

class ComponentD_withDerivatives(om.ExplicitComponent):
    ......

class ComponentE_withDerivatives(om.ExplicitComponent):
    ......


class ComponentF(om.ExplicitComponent):
    def setup(self):
        #setup inputs and outputs

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

    def compute(self,outputs):
        # computation

class ComponentG(om.ExplicitComponent):
    def setup(self):
        #setup inputs and outputs

        self.declare_partials(of='*',outputs):
        # computation 

class GroupAB(om.Group):
    def setup(self):
        self.add_subsystem('A',ComponentA_withDerivatives(),promotes_inputs=['x','y'],promotes_outputs=['z'])
        self.add_subsystem('B',ComponentB_withDerivatives(),'y','w','u'],promotes_outputs=['k'])

class GroupCD(om.Group):
    def setup(self):
        self.add_subsystem('C',ComponentC_withDerivatives(),.....)
        self.add_subsystem('D',ComponentD_withDerivatives(),...)


class Final(om.Group):
    def setup(self):
        cycle1 = self.add_subsystem('cycle1',om.Group(),promotes=['*'])
        cycle1.add_subsystem('GroupAB',GroupAB())
        cycle1.add_subsystem('ComponentF',ComponentF())

        cycle1.linear_solver = om.DirectSolver()
        cycle1.nonlinear_solver = om.NewtonSolver(solve_subsystems=True)

        cycle2 = self.add_subsystem('cycle2',promotes=['*'])
        cycle2.add_subsystem('GroupCD',GroupCD())
        cycle2.add_subsystem('ComponentE_withDerivatives',ComponentE_withDerivatives())

        cycle2.linear_solver = om.DirectSolver()
        cycle2.nonlinear_solver = om.NewtonSolver(solve_subsystems=True)

        self.add_subsystem('ComponentG',ComponentG(),promotes_inputs=['a1','a2','a3'],promotes_outputs=['b1'])


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

prob.driver = om.pyOptSparseDriver()
prob.driver.options['optimizer'] = 'SnopT'
prob.driver.options['print_results']= True

## Design Variables

## Costraints

## Objectives

# Setup
prob.setup()

##prob.model.approx_totals(method='fd')

prob.run_model()

prob.run_driver()

在这里这不起作用。 cycle1 不收敛。当我完全删除 cycle1 或使用 NonlinearBlockGS 而不是 Newton 或者如果我取消注释 prob.model.approx_total(method='FD') 时,代码有效。 (cycle2没问题。和牛顿一起工作)

因此,如果我不使用 approx_totals(),我假设 Openmdao 使用混合策略。或者我应该以某种方式手动提及它?当我确实使用 approx_totals() 时,我是否会失去我所拥有的分析导数的好处?

解决方法

您提供的代码示例无法运行,因此我必须进行一些猜测。您同时调用 run_model()run_driver()。不过,您费心在示例代码中包含优化器,而且您已经显示 approx_totals 在模型层次结构的顶部被调用。 所以当你说它不起作用时,我会假设你的意思是优化器不收敛。

您已正确理解 approx_totals 的行为。当您在模型顶部设置它时,OpenMDAO 将从组级别 FD 相关变量。在这种情况下,这意味着您还将对求解器本身进行 FD-ing。您说这似乎有效,但混合分析方法无效。

一般来说,当我使用 approx_totals() 时,我会失去混合分析方法的好处吗?

是的。您不再使用混合方法。您只是在整个模型中进行 FD-ing。

有没有办法用混合分析策略找到整个模型(或组)的导数?

当您不使用 approx_totals 时,OpenMDAO 使用混合策略计算总导数。问题是对于您的模型,它似乎不起作用。

一般情况下(不是在这种情况下),Openmdao 会自动检测混合策略吗?

它会“检测”它(它实际上并没有检测到任何东西,但是底层算法将使用混合策略,除非您用 approx_totals 告诉它不要这样做。同样,问题不在于混合策略没有被使用,但它不起作用。

那么为什么混合策略不起作用?

我只能猜测,因为我无法运行代码......所以YMMV。 您提到您正在对显式组件的部分使用复杂步骤。 Complex-step 是一种比 FD 更准确的近似方案,但它并非没有自身的缺陷。并非每个计算都是复杂安全的。有些可以重写为复杂安全,有些则不能。 “复数安全”是指计算正确处理复数部分以给出导数。

两种常用的复杂安全方法是 np.linalg.normnp.abs。两者都会很乐意接受复数并给你一个答案,但当你需要导数时,这不是正确的答案。 因此,OpenMDAO 附带了一个 set of custom functions that are cs-safe --- 提供了自定义 normabs

非 cs-safe 方法通常会发生复杂部分以某种方式被丢弃并且您得到 0 偏导数。错误的部分,错误的总数。

要检查这一点,请确保使用有限差分检查对复杂步进的组件调用 check_partials。您可能会发现一些差异。

您可以使用的修复程序是:

  1. 将这些组件切换为使用 FD 分音。不太准确,但可能会起作用
  2. 纠正计算中导致代码不安全的任何问题。如果这是问题,请使用 OpenMDAO 的自定义函数,或者您可能需要更加小心如何在计算中分配和使用 numpy 数组(如果您要分配自己的数组,则需要小心确保它们很复杂还有!)。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...