OpenMDAO并行采样/为什么test_brute_force.py不会失败

时间:2017-01-11 19:32:50

标签: openmdao

我修改test_brute_force.py以尝试并行化Paraboloid模型的采样(参见下面的代码)。我收到错误'sellars': promoted name 'sellar99.p1.f_xy' matches multiple unknowns: ('sellars.sellar99.p1.f_xy', 'sellars.sellar99.p1.f_xy')。这个错误是什么意思?为什么test_brute_force.py没有遇到此错误?

from __future__ import print_function
from florisse.floris import AEPGroup
import unittest

from six.moves import range
from six import iteritems
import numpy as np
from openmdao.api import Problem, Group, ParallelGroup, \
                         Component, IndepVarComp, ExecComp, \
                         Driver, ScipyOptimizer, SqliteRecorder
from openmdao.core.mpi_wrap import MPI

if MPI:
    from openmdao.core.petsc_impl import PetscImpl as impl
else:
    from openmdao.api import BasicImpl as impl

class Paraboloid(Component):
    """ Evaluates the equation f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3 """

    def __init__(self):
        super(Paraboloid, self).__init__()

        self.add_param('x_p', val=6.0)
        self.add_param('y', val=-7.0)

        self.add_output('f_xy', val=0.0)

    def solve_nonlinear(self, params, unknowns, resids):
        """f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3
        """

        x = params['x_p']
        y = params['y']

        unknowns['f_xy'] = (x-3.0)**2 + x*y + (y+4.0)**2 - 3.0

    def linearize(self, params, unknowns, resids):
        """ Jacobian for our paraboloid."""

        x = params['x_p']
        y = params['y']
        J = {}

        J['f_xy', 'x_p'] = 2.0*x - 6.0 + y
        J['f_xy', 'y'] = 2.0*y + 8.0 + x
        return J

pboidGroup = Group()
pboidGroup.add('p1', Paraboloid(), promotes=['x_p', 'y'])
pboidGroup.add('p2', Paraboloid(), promotes=['x_p', 'y'])
pboidGroup.connect('p1.x_p', 'p2.x_p')
pboidGroup.connect('p1.x_p', 'p2.y')

class Randomize(Component):
    """ add random uncertainty to params and distribute

    Args
    ----
    n : number of points to generate for each param

    params : collection of (name, value, std_dev) specifying the params
             that are to be randommized.
    """
    def __init__(self, n=0, params=[]):
        super(Randomize, self).__init__()

        self.dists = {}

        for name, value, std_dev in params:
            # add param
            self.add_param(name, val=value)

            # add an output array var to distribute the modified param values
            if isinstance(value, np.ndarray):
                shape = (n, value.size)
            else:
                shape = (n, 1)

            # generate a standard normal distribution (size n) for this param
            self.dists[name] = np.random.normal(0.0, std_dev, n*shape[1]).reshape(shape)
            #self.dists[name] = std_dev*np.random.normal(0.0, 1.0, n*shape[1]).reshape(shape)

            self.add_output('dist_'+name, val=np.zeros(shape))

    def solve_nonlinear(self, params, unknowns, resids):
        """ add random uncertainty to params
        """
        for name, dist in iteritems(self.dists):
            unknowns['dist_'+name] = params[name] + dist

    def linearize(self, params, unknowns, resids):
        """ derivatives
        """
        J = {}
        for u in unknowns:
            name = u.split('_', 1)[1]
            for p in params:
                shape = (unknowns[u].size, params[p].size)
                if p == name:
                    J[u, p] = np.eye(shape[0], shape[1])
                else:
                    J[u, p] = np.zeros(shape)
        return J


class Collector(Component):
    """ collect the inputs and compute the mean of each

    Args
    ----
    n : number of points to collect for each input

    names : collection of `Str` specifying the names of the inputs to
            collect and the resulting outputs.
    """
    def __init__(self, n=10, names=[]):
        super(Collector, self).__init__()

        self.names = names

        # create n params for each input
        for i in range(n):
            for name in names:
                self.add_param('%s_%i' % (name, i),  val=0.)

        # create an output for the mean of each input
        for name in names:
            self.add_output(name,  val=0.)

    def solve_nonlinear(self, params, unknowns, resids):
        """ compute the mean of each input
        """
        inputs = {}

        for p in params:
            name = p.split('_', 1)[0]
            if name not in inputs:
                inputs[name] = data = [0.0, 0.0]
            else:
                data = inputs[name]
            data[0] += 1
            data[1] += params[p]

        for name in self.names:
            unknowns[name]  = inputs[name][1]/inputs[name][0]

    def linearize(self, params, unknowns, resids):
        """ derivatives
        """
        J = {}
        for p in params:
            name, idx = p.split('_', 1)
            for u in unknowns:
                if u == name:
                    J[u, p] = 1
                else:
                    J[u, p] = 0
        return J


class BruteForceSellarProblem(Problem):
    """ Performs optimization on the Sellar problem.

        Applies a normal distribution to the design vars and runs all of the
        samples, then collects the values of all of the outputs, calculates
        the mean of those and stuffs that back into the unknowns vector.

        This is the brute force version that just stamps out N separate
        sellar models in a parallel group and sets the input of each
        one to be one of these random design vars.

    Args
    ----
    n : number of randomized points to generate for each input value

    derivs : if True, use user-defined derivatives, else use Finite Difference
    """
    def __init__(self, n=10, derivs=False):
        super(BruteForceSellarProblem, self).__init__(impl=impl)

        root = self.root = Group()
        if not derivs:
            root.deriv_options['type'] = 'fd'

        sellars = root.add('sellars', ParallelGroup())
        for i in range(n):
            name = 'sellar%i' % i
            sellars.add(name, pboidGroup)
            #sellars.add(name, SellarDerivatives())

            root.connect('x_p', 'sellars.'+name+'.x')#, src_indices=[i])
            #root.connect('yaw0', 'sellars.'+name+'.yaw0')#, src_indices=[i])
            #root.connect('dist_z', 'sellars.'+name+'.z', src_indices=[i*2, i*2+1])

            root.connect('sellars.'+name+'.f_xy',  'collect.obj_%i'  % i)
            #root.connect('sellars.'+name+'.con1', 'collect.con1_%i' % i)
            #root.connect('sellars.'+name+'.con2', 'collect.con2_%i' % i)

        root.add('indep', IndepVarComp([
                    ('x', 1.0),
                    ('z', np.array([5.0, 2.0]))
                ]),
                promotes=['x', 'z'])

        root.add('random', Randomize(n=n, params=[
                    # name, value, std dev
                    ('x', 1.0, 1e-2),
                    ('z', np.array([5.0, 2.0]), 1e-2)
                ]),
                promotes=['x', 'z', 'dist_x', 'dist_z'])

        root.add('collect', Collector(n=n, names=['obj', 'con1', 'con2']),
                promotes=['obj', 'con1', 'con2'])

        # top level driver setup
        self.driver = ScipyOptimizer()
        self.driver.options['optimizer'] = 'SLSQP'
        self.driver.options['tol'] = 1.0e-8
        self.driver.options['maxiter'] = 50
        self.driver.options['disp'] = False

        self.driver.add_desvar('z', lower=np.array([-10.0,  0.0]),
                                    upper=np.array([ 10.0, 10.0]))
        self.driver.add_desvar('x', lower=0.0, upper=10.0)

        self.driver.add_objective('obj')
        self.driver.add_constraint('con1', upper=0.0)
        self.driver.add_constraint('con2', upper=0.0)

prob = BruteForceSellarProblem(100, derivs=False)
prob.setup(check=False)
prob.run()
print(prob["obj"])

1 个答案:

答案 0 :(得分:1)

我不完全确定你想要建模什么,或者应该与什么相关联,但我可以让你超越那个错误。问题是您在循环中多次添加抛物面组pboidGroup的相同实例,而OpenMDAO不支持在多个位置使用相同的组件实例。您需要每次都创建一个新实例。

要解决这个问题,我只是将代码移动到接近使用位置的循环中,因此在循环中我们得到:

        pboidGroup = Group()
        pboidGroup.add('p1', Paraboloid())
        pboidGroup.add('p2', Paraboloid())
        pboidGroup.connect('p1.x_p', 'p2.x_p')
        pboidGroup.connect('p1.x_p', 'p2.y')

        name = 'sellar%i' % i
        sellars.add(name, pboidGroup)
        #sellars.add(name, SellarDerivatives())

现在我已经完成了,我在连接中遇到错误,我不确定x_p是什么意思在根目录中(可能需要IndepVarComp),但是这可能会让你超过停工点。