2017-01-11 8 views
0

I изменен 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 не столкнулся с этой ошибкой?Распараллеливание OpenMDAO/почему 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

Я не совсем уверен, что вы пытаетесь смоделировать, или то, что должно быть связано с тем, что, но я могу получить мимо этой ошибки. Проблема в том, что вы добавляете один и тот же экземпляр группы параболоидов 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()) 

Теперь, когда я сделал это, я получаю ошибки в connect, и я не уверен, что x_p предназначен для того, чтобы быть в корне (возможно, для него необходим IndepVarComp), но, возможно, это приведет вас к точке остановки.

+0

Что касается того, что я пытаюсь моделировать, я бы хотел найти ожидаемое значение 'f_xy', используя параллельную выборку, с учетом нормально распределенного' x_p'. Я добавил подключения, чтобы сделать это ближе к моей реальной проблеме. – kilojoules

+0

Теперь я получаю сообщение об ошибке «Источник» p1.x_p 'не может быть подключен к цели «sellars.sellar0.x»: «p1.x_p» не существует. «Почему подобная ошибка не появляется для' dist_z' в оригинальный пример? Мы инициализировали root как пустую 'Group(). Я был удивлен, что мы можем ссылаться на' dist_z' без ошибок. – kilojoules

+1

Итак, проблема в этой строке 'root.connect ('x_p', 'sellars.' + Name + '. X') #, src_indices = [i])'. Здесь вы связываете x_p в каждой из систем sellar с источником 'x_p', который где-то должен быть выходом, но ничем не предусмотрен. В исходном примере мы подключаемся к «dist_x», который предоставляется «Randomize». Чтобы совпасть, вам нужно либо переименовать dist_x в x_p в компоненте Randomize, либо подключиться к 'dist_x'. (и для этого вам придется раскомментировать исходные индексы) –