OpenMDAO 如何设置子组属性?
OpenMDAO how to set subgroup properties?
通常,当我使用我的优化组时,我会将其包含在一个问题中。然后,我可以设置它的组件属性:
# import modules, prepare data for Problem setup
...
# Initialize problem with my group
prob = Problem(impl=impl, root=AEPGroup(nTurbines=10,
nDirections=5,
minSpacing=2))
# Configure driver, desvars, and constraints
prob.driver = pyOptSparseDriver()
prob.driver.add_desvar('turbineX', lower=np.ones(nTurbs)*min(turbineX), upper=np.ones(nTurbs)*max(turbineX), scaler=1E-2)
prob.driver.add_objective('obj', scaler=1E-8)
# run setup()
prob.setup(check=True)
# Now I set several specifications
prob['turbineX'] = turbineX
....
请看下面我的例子(改编自test_brute_force.py
)。在第 204 行中,我想将 运行 AEPGroup
作为一个组内的另一个组。是否有类似的方法来配置子组内的 turbineX
等规范?
from __future__ import print_function
from florisse.floris import AEPGroup
import unittest
from florisse.GeneralWindFarmComponents import calculate_boundary
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.test.sellar import *
from openmdao.test.util import assert_rel_error
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
# load wind rose data
windRose = np.loadtxt('./input_files/windrose_amalia_directionally_averaged_speeds.txt')
indexes = np.where(windRose[:, 1] > 0.1)
#print ("ypppp indexes are ", indexes)
indexes = [[8]]
#print ("ypppp indexes are ", indexes) ; quit()
windDirections = windRose[indexes[0], 0]
windSpeeds = windRose[indexes[0], 1]
windFrequencies = windRose[indexes[0], 2]
nDirections = len(windDirections)
# load turbine positions
locations = np.loadtxt('./input_files/layout_amalia.txt')
turbineX = locations[:, 0]
turbineY = locations[:, 1]
# generate boundary constraint
boundaryVertices, boundaryNormals = calculate_boundary(locations)
nVertices = boundaryVertices.shape[0]
# define turbine size
rotor_diameter = 126.4 # (m)
# initialize input variable arrays
nTurbines = turbineX.size
rotorDiameter = np.zeros(nTurbines)
axialInduction = np.zeros(nTurbines)
Ct = np.zeros(nTurbines)
Cp = np.zeros(nTurbines)
generatorEfficiency = np.zeros(nTurbines)
yaw = np.zeros(nTurbines)
minSpacing = 2. # number of rotor diameters
# define initial values
for turbI in range(0, nTurbines):
rotorDiameter[turbI] = rotor_diameter # m
axialInduction[turbI] = 1.0/3.0
Ct[turbI] = 4.0*axialInduction[turbI]*(1.0-axialInduction[turbI])
Cp[turbI] = 0.7737/0.944 * 4.0 * 1.0/3.0 * np.power((1 - 1.0/3.0), 2)
generatorEfficiency[turbI] = 0.944
yaw[turbI] = 0. # deg.
# Define flow properties
air_density = 1.1716 # kg/m^3
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 AEP 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
AEP 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, AEPGroup(nTurbines=nTurbines, nDirections=nDirections,
differentiable=True,
use_rotor_components=False))
#sellars.add(name, SellarDerivatives())
root.connect('dist_air_density', 'sellars.'+name+'.air_density', 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+'.AEP', '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([
('air_density', 1.0),
('z', np.array([5.0, 2.0]))
]),
promotes=['air_density', 'z'])
root.add('random', Randomize(n=n, params=[
# name, value, std dev
('air_density', 1.0, 1e-2),
('z', np.array([5.0, 2.0]), 1e-2)
]),
promotes=['z', 'dist_air_density', 'dist_z'])
#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"])
因为您在调用
时没有进行任何变量提升
sellars.add(name, AEPGroup(nTurbines=nTurbines, nDirections=nDirections,
differentiable=True,
use_rotor_components=False))
您可以将变量名称设置为
prob['sellars.sellar0.turbineX'] = turbineX
您只需调整您的可变路径名以说明其他父组的存在以及您的 AEPGroup
现在被命名为 sellar0
(或您需要设置的任何索引)这一事实。
通常,当我使用我的优化组时,我会将其包含在一个问题中。然后,我可以设置它的组件属性:
# import modules, prepare data for Problem setup
...
# Initialize problem with my group
prob = Problem(impl=impl, root=AEPGroup(nTurbines=10,
nDirections=5,
minSpacing=2))
# Configure driver, desvars, and constraints
prob.driver = pyOptSparseDriver()
prob.driver.add_desvar('turbineX', lower=np.ones(nTurbs)*min(turbineX), upper=np.ones(nTurbs)*max(turbineX), scaler=1E-2)
prob.driver.add_objective('obj', scaler=1E-8)
# run setup()
prob.setup(check=True)
# Now I set several specifications
prob['turbineX'] = turbineX
....
请看下面我的例子(改编自test_brute_force.py
)。在第 204 行中,我想将 运行 AEPGroup
作为一个组内的另一个组。是否有类似的方法来配置子组内的 turbineX
等规范?
from __future__ import print_function
from florisse.floris import AEPGroup
import unittest
from florisse.GeneralWindFarmComponents import calculate_boundary
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.test.sellar import *
from openmdao.test.util import assert_rel_error
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
# load wind rose data
windRose = np.loadtxt('./input_files/windrose_amalia_directionally_averaged_speeds.txt')
indexes = np.where(windRose[:, 1] > 0.1)
#print ("ypppp indexes are ", indexes)
indexes = [[8]]
#print ("ypppp indexes are ", indexes) ; quit()
windDirections = windRose[indexes[0], 0]
windSpeeds = windRose[indexes[0], 1]
windFrequencies = windRose[indexes[0], 2]
nDirections = len(windDirections)
# load turbine positions
locations = np.loadtxt('./input_files/layout_amalia.txt')
turbineX = locations[:, 0]
turbineY = locations[:, 1]
# generate boundary constraint
boundaryVertices, boundaryNormals = calculate_boundary(locations)
nVertices = boundaryVertices.shape[0]
# define turbine size
rotor_diameter = 126.4 # (m)
# initialize input variable arrays
nTurbines = turbineX.size
rotorDiameter = np.zeros(nTurbines)
axialInduction = np.zeros(nTurbines)
Ct = np.zeros(nTurbines)
Cp = np.zeros(nTurbines)
generatorEfficiency = np.zeros(nTurbines)
yaw = np.zeros(nTurbines)
minSpacing = 2. # number of rotor diameters
# define initial values
for turbI in range(0, nTurbines):
rotorDiameter[turbI] = rotor_diameter # m
axialInduction[turbI] = 1.0/3.0
Ct[turbI] = 4.0*axialInduction[turbI]*(1.0-axialInduction[turbI])
Cp[turbI] = 0.7737/0.944 * 4.0 * 1.0/3.0 * np.power((1 - 1.0/3.0), 2)
generatorEfficiency[turbI] = 0.944
yaw[turbI] = 0. # deg.
# Define flow properties
air_density = 1.1716 # kg/m^3
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 AEP 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
AEP 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, AEPGroup(nTurbines=nTurbines, nDirections=nDirections,
differentiable=True,
use_rotor_components=False))
#sellars.add(name, SellarDerivatives())
root.connect('dist_air_density', 'sellars.'+name+'.air_density', 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+'.AEP', '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([
('air_density', 1.0),
('z', np.array([5.0, 2.0]))
]),
promotes=['air_density', 'z'])
root.add('random', Randomize(n=n, params=[
# name, value, std dev
('air_density', 1.0, 1e-2),
('z', np.array([5.0, 2.0]), 1e-2)
]),
promotes=['z', 'dist_air_density', 'dist_z'])
#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"])
因为您在调用
时没有进行任何变量提升sellars.add(name, AEPGroup(nTurbines=nTurbines, nDirections=nDirections,
differentiable=True,
use_rotor_components=False))
您可以将变量名称设置为
prob['sellars.sellar0.turbineX'] = turbineX
您只需调整您的可变路径名以说明其他父组的存在以及您的 AEPGroup
现在被命名为 sellar0
(或您需要设置的任何索引)这一事实。