拆分组之间的连接

Splitting up connections between groups

我想知道拆分连接命令的最佳方法。我有两个要模块化的组,一个内部组和一个外部组。我希望内部组成为一种黑匣子,我可以在其中切换或更改内部组而不更改外部组的所有连接。我只希望外部组必须知道内部组的输入和输出。例如:

import numpy as np
from openmdao.api import Group, Problem, Component, IndepVarComp, ExecComp

class C(Component):
    def __init__(self, n):
        super(C, self).__init__()
        self.add_param('array_element', shape=1)
        self.add_output('new_element', shape=1)

    def solve_nonlinear(self, params, unknowns, resids):
        unknowns['new_element'] = params['array_element']*2.0

class MUX(Component):
    def __init__(self, n):
        super(MUX, self).__init__()
        for i in range(n):
            self.add_param('new_element' + str(i), shape=1)
        self.add_output('new_array', shape=n)
        self.n = n

    def solve_nonlinear(self, params, unknowns, resids):
        new_array = np.zeros(n)
        for i in range(n):
            new_array[i] = params['new_element' + str(i)]
        unknowns['new_array'] = new_array

class GroupInner(Group):
    def __init__(self, n):
        super(GroupInner, self).__init__()
        for i in range(n):
            self.add('c'+str(i), C(n))
            self.connect('array', 'c'+str(i) + '.array_element', src_indices=[i])                
            self.connect('c'+str(i)+'.new_element', 'new_element'+str(i))

        self.add('mux', MUX(n), promotes=['*'])

class GroupOuter(Group):
    def __init__(self, n):
        super(GroupOuter, self).__init__()
        self.add('array', IndepVarComp('array', np.zeros(n)), promotes=['*'])
        self.add('inner', GroupInner(n), promotes=['new_array'])
        for i in range(n):
            # self.connect('array', 'inner.c'+str(i) + '.array_element', src_indices=[i])
            self.connect('array', 'inner.array', src_indices=[i])
n = 3
p = Problem()
p.root = GroupOuter(n)
p.setup(check=False)
p['array'] = np.ones(n)
p.run()

print p['new_array']

当我 运行 代码时,我得到的错误是:

NameError: Source 'array' cannot be connected to target 'c0.array_element': 'array' does not exist.

为了尝试解决这个问题,我在 GroupInner 组中创建了 'array' IndepVarComp。但是,当我这样做时,出现错误:

NameError: Source 'array' cannot be connected to target 'inner.array': Target must be a parameter but 'inner.array' is an unknown.

我知道如果我只是建立完整的连接:self.connect('array', 'inner.c'+str(i) + '.array_element', src_indices=[i]) 然后它将起作用。但正如我所说,我希望 GroupInner 成为一种黑盒子,我不知道其中有哪些组或组件。我也不能只提升所有,因为 array_element 是不同的。是否可以这样做,还是必须在一个命令中完成整个连接?

我对你的问题有两个答案。首先,我会按照您指定的方式解决问题。其次,我将提出一个修改建议,我认为对于此模型结构的某些应用程序可能更有效。

首先,您指定的问题的主要问题是以下行

            self.connect('array', 'c'+str(i) + '.array_element', src_indices=[i])                

Inner 组内的任何地方都没有名为 array 的输出或状态,因此该连接将无法正常工作。您确实在 Outer 组中创建了一个名为 'array' 的变量,但您无法从 Inner 定义内部发出到它的连接,因为它在该范围内不可用。要使其按照您指定的方式工作,最简单的方法是执行以下操作:

class GroupInner(Group):
def __init__(self, n):
    super(GroupInner, self).__init__()
    for i in range(n):
        self.add('c'+str(i), C(n))
        self.connect('c%d.new_element'%i, 'new_element'+str(i))

    self.add('mux', MUX(n), promotes=['*'])


class GroupOuter(Group):
    def __init__(self, n):
        super(GroupOuter, self).__init__()
        self.add('array', IndepVarComp('array', np.zeros(n)), promotes=['*'])
        self.add('inner', GroupInner(n), promotes=['new_array'])
        for i in range(n):
            self.connect('array', 'inner.c%d.array_element'%i, src_indices=[i])

这是一种替代方法,可以减少模型中变量和组件的数量,如果 n 变大,这将有助于减少设置时间,即使用实际的分布式组件,并使用MPI 通信。这有一些很好的特性,除了节省设置成本之外,因为它还可以让您更灵活地扩展计算,并在串行 运行 时提高效率。如果您的模型确实有多个 c 个实例都在做同样的事情,并且该过程可以通过 numpy 简单地矢量化,则此解决方案效果很好。

import numpy as np
from openmdao.api import Group, Problem, Component, IndepVarComp
from openmdao.util.array_util import evenly_distrib_idxs

from openmdao.core.mpi_wrap import MPI

if MPI:
    # if you called this script with 'mpirun', then use the petsc data passing
    from openmdao.core.petsc_impl import PetscImpl as impl
else:
    # if you didn't use `mpirun`, then use the numpy data passing
    from openmdao.api import BasicImpl as impl


class C(Component):
    def __init__(self, n):
        super(C, self).__init__()
        self.add_param('in_array', shape=n)
        self.add_output('new_array', shape=n)
        self.n = n

    def get_req_procs(self):
        """
        min/max number of procs that this component can use
        """
        return (1,self.n)

    #NOTE: This needs to be setup_distrib_idx for <= version 1.5.0
    def setup_distrib(self):

        comm = self.comm
        rank = comm.rank

        # NOTE: evenly_distrib_idxs is a helper function to split the array
        #       up as evenly as possible
        sizes, offsets = evenly_distrib_idxs(comm.size, self.n)
        local_size, local_offset = sizes[rank], offsets[rank]
        self.local_size = int(local_size)

        start = local_offset
        end = local_offset + local_size

        self.set_var_indices('in_array', val=np.zeros(local_size, float),
            src_indices=np.arange(start, end, dtype=int))
        self.set_var_indices('new_array', val=np.zeros(local_size, float),
            src_indices=np.arange(start, end, dtype=int))

    def solve_nonlinear(self, params, unknowns, resids):
        unknowns['new_array'] = params['in_array']*2.0
        print "computing new_array: ", unknowns['new_array']


class GroupInner(Group):
    def __init__(self, n):
        super(GroupInner, self).__init__()
        self.add('c', C(n), promotes=['new_array', 'in_array'])


class GroupOuter(Group):
    def __init__(self, n):
        super(GroupOuter, self).__init__()
        self.add('array', IndepVarComp('array', np.zeros(n)), promotes=['*'])
        self.add('inner', GroupInner(n), promotes=['new_array',])
        self.connect('array', 'inner.in_array')
n = 3
p = Problem(impl=impl)
p.root = GroupOuter(n)
p.setup(check=False)
p['array'] = np.ones(n)
p.run()

print p['new_array']