为什么在使用 NonlinearBlockGS 求解器的耦合组中,第一个组件的输出没有直接传递到第二个组件的输入?

Why are outputs from the first component not passed to the second's inputs directly in a coupled group using a NonlinearBlockGS solver?

我正在 运行 解决 Group 的问题,其中包括 OpenMDAO 中其子系统之间的反馈。我正在使用 NonlinearBlockBS 求解器。我希望 Gauss-Seidel 求解器按顺序 运行 子系统,将较早块的输出传递到其他块的输入。但是,当我在 OpenMDAO 中实现它时,这似乎并没有发生。

我制作了一个示例脚本来演示这个问题:

class A(ExplicitComponent):

    def setup(self):
        self.add_input('x', shape=1)
        self.add_input('b', shape=1)
        self.add_output('a', shape=1)

    def compute(self, inputs, outputs):
        outputs['a'] = inputs['x'] + 2 * inputs['b']
        print('A: x = {:1.0f}, b = {:1.0f}, a = {:1.0f}'.format(inputs['x'][0], inputs['b'][0], outputs['a'][0]))


class B(ExplicitComponent):

    def setup(self):
        self.add_input('x', shape=1)
        self.add_input('a', shape=1)
        self.add_output('b', shape=1)

    def compute(self, inputs, outputs):
        outputs['b'] = inputs['x'] - 0.5 * inputs['a']
        print('B: x = {:1.0f}, a = {:1.0}, b = {:1.0f}'.format(inputs['x'][0], inputs['a'][0], outputs['b'][0]))


if __name__ == '__main__':
    ivc = IndepVarComp()
    ivc.add_output('x', val=3.)

    coupled_group = Group()
    coupled_group.add_subsystem('A', A(), promotes=['*'])
    coupled_group.add_subsystem('B', B(), promotes=['*'])
    coupled_group.nonlinear_solver = NonlinearBlockGS()

    prob = Problem()
    model = prob.model = Group()
    model.add_subsystem('I', ivc, promotes=['*'])
    model.add_subsystem('C', coupled_group, promotes=['*'])

    prob.setup()
    prob.run_model()

两个组件 AB 由它们的输出 ab 耦合。它们还共享一个参数 x,该参数最初由 IndepVarComp 设置。当 运行 时,代码产生以下输出:

 =
 C
 =
 A: x = 3, b = 1, a = 7
 B: x = 3, a = 1, b = 4
 A: x = 3, b = 1, a = 7
 B: x = 3, a = 7, b = 1
 A: x = 3, b = 1, a = 7
 B: x = 3, a = 7, b = 1
 NL: NLBGS Converged in 1 iterations

作为A输入的b参数在A先运行时还没有定义。因此,它的初始值为 1。这符合预期。然后 B 是 运行,它应该从 Aa = 7 获取输出,但是 a 也设置为初始猜测值 1。这是不是我在使用 Gauss-Seidel 方法时所期望的。

A是运行之后B没有得到a的更新值这一事实并不影响这个系统收敛到正确解的事实在这种情况下。但是,在我的例子中,a = 1 不是 B 的有效输入。因此系统无法收敛。

我是不是做错了什么?我该怎么做才能确保 B 获得第一个 a 的更新值 运行?

在 OpenMDAO 中,变量被定义为所有其他变量的隐式函数。与基于流的架构相比,这是一个主要区别,在基于流的架构中,组件被定义为显式函数。参见[Flow based architecture and MAUD architecture][1]。在 OpenMDAO 中,代替这个非线性方程组求解残差。所以这是正常的,在第一步中组件B中使用默认值输入aB.a的输入是A.a,所以B.a 在第一次计算中将等于 A.a)

的默认值

如果 a 在您的系统 B 中不是有效值,请使用合适的默认值对其进行初始化(现在您只在设置中指定形状,而不是值。)。添加输入的方法的签名是add_input(self, name, val=1.0, shape=None, src_indices=None, flat_src_indices=None, units=None, desc=''),所以如果你不指定它,你的默认值将是1.0。 我重写了您的 类 以从您的变量设置中定义的初始值开始。对于组件 B a 是从 A 的设置传递的,而不是 A 的第一次计算。同样适用于组件 A 中的 b。在此之后,求解器将尝试最小化残差,将 B.a-A.aB.b-A.b 驱动为零。

class A(ExplicitComponent):

    def setup(self):
        self.add_input('x', shape=1)
        self.add_input('b', val=2)
        self.add_output('a', val=1)

    def compute(self, inputs, outputs):
        outputs['a'] = inputs['x'] + 2 * inputs['b']
        print('A: x = {:1.0f}, b = {:1.0f}, a = {:1.0f}'.format(inputs['x'][0], inputs['b'][0], outputs['a'][0]))


class B(ExplicitComponent):

    def setup(self):
        self.add_input('x', shape=1)
        self.add_input('a', val=3)
        self.add_output('b', val=4)

    def compute(self, inputs, outputs):
        outputs['b'] = inputs['x'] - 0.5 * inputs['a']
        print('B: x = {:1.0f}, a = {:1.0}, b = {:1.0f}'.format(inputs['x'][0], inputs['a'][0], outputs['b'][0]))

求解器第一步:

=
C
=
A: x = 3, b = 4, a = 11
B: x = 3, a = 1e+00, b = 2

[1]:https://i.stack.imgur.com/ygBdn.png Hwang,JT 和 Martins,JRRA:"A Computational Architecture for Coupling Heterogeneous Numerical Models and Computing Coupled Derivatives"