Skip to main content
 首页 » 编程设计

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

2025年05月04日25lyhabc

我遇到了 Group 的问题,其中包括 OpenMDAO 中其子系统之间的反馈。我正在使用 NonlinearBlockBS 求解器。我希望 Gauss-Seidel 求解器按顺序运行子系统,将早期 block 的输出传递到其他 block 的输入。然而,当我在 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() 

A 和B 这两个组件由它们的输出a 和b 耦合。它们还共享一个参数 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 运行,它应该从 A 获取输出,a = 7,但是 a 也是设置为 1 的初始猜测。这不是我在使用 Gauss-Seidel 方法时所期望的结果。

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

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

请您参考如下方法:

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

如果 a 在你的系统 B 中不是有效值,用合适的默认值初始化它(现在你只在设置中指定形状,而不是值(value)。)。添加输入的方法的签名是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.a 和 B.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]: /image/ygBdn.png Hwang, JT 和 Martins, JRRA:“用于耦合异构数值模型和计算耦合导数的计算架构”