拆分组之间的连接

时间:2016-02-02 21:01:36

标签: openmdao

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

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组中的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&# 39;,src_indices = [i])那么它会起作用。但就像我说的那样,我希望GroupInner成为一个黑盒子,我不知道其中包含哪些组或组件。我也不能只提升所有因为array_elements不同。是可以这样做还是你必须在一个命令中完成整个连接?

1 个答案:

答案 0 :(得分:2)

我对你的问题有两个答案。首先,我会在你指定的时候解决问题。其次,我会建议一种修改,我认为对于这种模型结构的某些应用可能更有效。

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

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

array组内的任何地方都没有名为Inner的输出或状态,因此连接无法正常工作。您确实在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对数组进行分区COMM。除了节省设置成本之外,它还具有一些不错的属性,因为它还允许您在串行运行时扩展计算的灵活性并提高效率。如果你的模型真的有多个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']