OpenMDAO 并行采样 / 为什么 test_brute_force.py 不会失败
OpenMDAO parallelized sampling / why does test_brute_force.py not fail
我修改了 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
没有遇到这个错误?
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"])
我不完全确定您要建模的是什么,或者什么应该与什么相关联,但我可以帮助您克服该错误。问题是您在循环中多次添加抛物面组 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),但是也许这会让你超越停止点。
我修改了 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
没有遇到这个错误?
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"])
我不完全确定您要建模的是什么,或者什么应该与什么相关联,但我可以帮助您克服该错误。问题是您在循环中多次添加抛物面组 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),但是也许这会让你超越停止点。