2017-01-11 19 views
0

我修改了test_brute_force.py以試圖並行化拋物面模型的採樣(請參閱下面的代碼)。我收到錯誤'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()) 

現在,我已經做了,我得到的錯誤中連接,我不知道什麼x_p是爲了在根(也許需要一個IndepVarComp),但也許這會讓你通過停止點。

+0

至於我想模型,我想找到'f_xy'的期望值,使用並行採樣,給定一個正態分佈的'x_p'。我添加了這些連接以使它更接近我真正的問題。 – kilojoules

+0

現在我得到錯誤'Source'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])'。在這裏,你將每個sellar系統中的x_p連接到一個源'x_p',它需要在某處輸出,但不是由任何東西提供的。在最初的例子中,我們連接到由'Randomize'提供的'dist_x'。爲了匹配,您需要在隨機化組件中將dist_x重命名爲x_p,或者連接到'dist_x'。 (並且要這樣做,您必須取消源代碼索引) –

相關問題