Source code for openmdao.lib.architectures.bliss2000

"""Implementation of the BLISS2000 optimization architecture based on the work described in 
the following journal article:

J. Sobieszczanski-Sobieski, T. Altus, M. Phillips, and Sandu, Bilevel Integrated System Synthesis 
for Concurrent and Distributed Processing, AIAA journal, vol. 41, no. 10, pp. 1996-2003, 2003.

"""


from openmdao.main.api import Driver, Architecture, SequentialWorkflow, \
                              Component, Assembly
from openmdao.lib.drivers.api import CONMINdriver, BroydenSolver, \
                                     IterateUntil, FixedPointIterator, \
                                     NeighborhoodDOEdriver, SLSQPdriver
from openmdao.lib.differentiators.finite_difference import FiniteDifference
from openmdao.lib.surrogatemodels.api import ResponseSurface
from openmdao.lib.doegenerators.api import CentralComposite, \
                                           OptLatinHypercube, LatinHypercube
from openmdao.lib.components.api import MetaModel
from openmdao.lib.datatypes.api import Float, Array, Slot
from openmdao.lib.casehandlers.api import DBCaseRecorder

[docs]class SubSystemObj(Component): """Component which adds the weight factors for each output state variable for a given subsystem. """ f_wy = Float(0.0,iotype="out",desc="subsystem objective") def __init__(self,num_vars): super(SubSystemObj,self).__init__() self.num_vars = num_vars self.var_names = [] self.weights = []
[docs] def configure(self): for i in range(0,self.num_vars): name = "y%d"%i self.add_trait(name,Float(0.0, iotype="in", desc=" input variable #%d"%i)) self.var_names.append(name) name = "w%d"%i self.add_trait(name,Float(1.0, iotype="in", desc="weighting factor for input variable #%d"%i)) self.weights.append(name)
[docs] def execute(self): self.f_wy = sum([getattr(self,w)*getattr(self,v) for w,v in zip(self.weights,self.var_names)])
[docs]class Broadcast(Component): """Used to create outputs in the SubSytemOpt assembly.""" input = Float(0.0,iotype="in") output = Float(0.0,iotype="out")
[docs] def execute(self): self.output = self.input
[docs]class SubSystemOpt(Assembly): """ Assembly which takes global inputs, coupling indeps, and weight factors as inputs and runs a local optimization on the local des vars.""" def __init__(self,component,global_params,local_params,couple_deps,couple_indeps,constraints): super(SubSystemOpt,self).__init__() self.component = component self.global_params = global_params self.local_params = local_params self.couple_deps = couple_deps self.couple_indeps = couple_indeps self.constraints = constraints self.var_map = {} self.weights= [] self.var_names = []
[docs] def configure(self): dep_couple_vars = set([c.dep.target for c in self.couple_deps]) self.add(self.component.name,self.component) for i,p in enumerate(self.global_params): name = "global_%d"%i self.var_map[p.target] = name self.add_trait(name,Float(0.0,iotype="in",desc="global design var for %s"%p.target.split(".")[-1])) self.connect(name,p.target) #promote the global des vars #setattr(self,name,self.get(p.target)) if self.local_params: #if there are none, you don't do an optimization self.add('objective_comp',SubSystemObj(len(dep_couple_vars))) self.weights = self.objective_comp.weights self.var_names = self.objective_comp.var_names self.add('driver',CONMINdriver()) self.driver.add_objective("objective_comp.f_wy") self.driver.fdch = .00001 #self.driver.fdchm = .0001 #this is not really necessary, but you might want to track it anyway... self.create_passthrough("objective_comp.f_wy") #promote the objective function for i,p in enumerate(self.local_params): target = p.target var_name = "local_%d"%i self.var_map[target] = var_name #since the local variables are optimized, they become outputs now broadcast_name = 'output_%s'%var_name self.add(broadcast_name,Broadcast()) self.add_trait(var_name,Float(0.0,iotype="out",desc="localy optimized value for %s"%target)) #setattr(self,var_name,self.get(target)) self.connect("%s.output"%broadcast_name,target) #connect broadcast output to input of component self.connect("%s.output"%broadcast_name,var_name) #connect broadcast output to variable in assembly self.driver.add_parameter("%s.input"%broadcast_name,low=p.low,high=p.high) #optimizer varries broadcast input for c in self.constraints: self.driver.add_constraint(str(c)) for i,(w,var,c) in enumerate(zip(self.objective_comp.weights, self.objective_comp.var_names, dep_couple_vars)): name = "couple_dep_%d"%i self.var_map[c] = name self.add_trait(name,Float(0.0,iotype="out",desc="coupling dependent for %s"%c)) self.connect(c,name) #prmote the coupling deps to be outputs self.connect(c,"objective_comp.%s"%var) #also connect the state vars to the inputs of the objective come self.create_passthrough("objective_comp.%s"%w) #promote the weights else: #no locals, so just promote the coupling deps #no optimizer, so add the comp to the default workflow self.driver.workflow.add(self.component.name) for i,c in enumerate(dep_couple_vars): name = "couple_dep_%d"%i self.var_map[c] = name self.add_trait(name,Float(0.0,iotype="out",desc="coupling dependent for %s"%c)) self.connect(c,name) #prmote the coupling deps to be outputs for i,c in enumerate(self.couple_indeps): name = "couple_indep_%d"%i self.var_map[c.indep.target] = name self.add_trait(name,Float(0.0,iotype="in",desc="coupling independent for %s"%c)) self.connect(name,c.indep.target)
[docs]class BLISS2000(Architecture): def __init__(self, *args, **kwargs): super(BLISS2000, self).__init__(*args, **kwargs) # the following variables determine the behavior of check_config self.param_types = ['continuous'] self.constraint_types = ['ineq'] self.num_allowed_objectives = 1 self.has_coupling_vars = True self.requires_global_des_vars = True
[docs] def configure(self): """setup and BLISS2000 architecture inside this assembly. """ global_dvs = self.parent.get_global_des_vars() des_vars=self.parent.get_des_vars_by_comp() local_dvs_by_comp = self.parent.get_local_des_vars_by_comp() global_dvs_by_comp = self.parent.get_global_des_vars_by_comp() locals=self.parent.get_local_des_vars() objective = self.parent.get_objectives().items()[0] comp_constraints = self.parent.get_constraints_by_comp() coupling = self.parent.get_coupling_vars() couple_deps = self.parent.get_coupling_deps_by_comp() couple_indeps = self.parent.get_coupling_indeps_by_comp() driver=self.parent.add("driver",FixedPointIterator()) driver.workflow = SequentialWorkflow() driver.max_iteration=15 #should be enough to converge driver.tolerance = .005 meta_models = {} self.sub_system_opts = {} system_var_map = {} for comp in des_vars: mm_name = "meta_model_%s"%comp meta_model = self.parent.add(mm_name,MetaModel()) #metamodel now replaces old component with same name driver.add_event("%s.reset_training_data"%mm_name) meta_models[comp] = meta_model meta_model.surrogate = {'default':ResponseSurface()} #if there are locals, you need to make a SubSystemOpt assembly comp_obj = self.parent.get(comp) sso = self.parent.add('sub_system_opt_%s'%comp, SubSystemOpt(comp_obj, global_dvs_by_comp.get(comp), local_dvs_by_comp.get(comp), couple_deps.get(comp), couple_indeps.get(comp), comp_constraints.get(comp))) self.sub_system_opts[comp] = sso meta_model.model = sso for name,mapped_name in sso.var_map.iteritems(): system_var_map[name] = "%s.%s"%(mm_name,mapped_name) meta_model.recorder = DBCaseRecorder() #add a doe trainer for each metamodel dis_doe=self.parent.add("DOE_Trainer_%s"%comp,NeighborhoodDOEdriver()) for couple in couple_indeps[comp] : mapped_name = system_var_map[couple.indep.target] dis_doe.add_parameter(mapped_name,low=-1e99,high=1e99) #change to -1e99/1e99 for dv in global_dvs_by_comp[comp]: dis_doe.add_parameter(system_var_map[dv.target],low=dv.low, high=dv.high,start=dv.start) if local_dvs_by_comp.get(comp): #add weights if they are there for w in meta_model.model.weights: dis_doe.add_parameter("meta_model_%s.%s"%(comp,w),low=-3,high=3) num_params = len(dis_doe.get_parameters()) dis_doe.DOEgenerator = LatinHypercube((num_params**2+3*num_params+2)/2) dis_doe.alpha= .1 dis_doe.beta = .01 dis_doe.add_event("meta_model_%s.train_next"%comp) dis_doe.force_execute = True driver.workflow.add(dis_doe.name) #run all doe training before system optimziation #optimization of system objective function using the discipline meta models sysopt=self.parent.add('sysopt', SLSQPdriver()) sysopt.recorders = self.data_recorders sysopt.iprint = 0 sysopt.differentiator = FiniteDifference() obj2= objective[1].text #for comp in objective[1].get_referenced_compnames(): # obj2=obj2.replace(comp,"meta_model_%s"%comp) for var_name, mapped_name in system_var_map.iteritems(): obj2=obj2.replace(var_name,mapped_name) sysopt.add_objective(obj2) #add global design variables as parameters for param,group in global_dvs: plist=[system_var_map[t] for t in group.targets] sysopt.add_parameter(plist, low=group.low, high=group.high,start=group.start) #add the subsytem weights to the system optimization for comp,sso in self.sub_system_opts.iteritems(): mm_name = "meta_model_%s"%comp for w in sso.weights: sysopt.add_parameter("%s.%s"%(mm_name,w),low=-3,high=3) for key,couple in coupling.iteritems(): s=couple.indep.target mapped_name = system_var_map[s] sysopt.add_parameter(mapped_name, low=-1e99, high=1e99) #feasibility constraints, referenced to metamodels s1,s2= system_var_map[couple.dep.target], system_var_map[couple.indep.target] sysopt.add_constraint('(%s-%s)**2<=0.0001'%(s2,s1)) #sysopt.add_constraint('%s>=%s'%(s2,s1)) #add constraints, referenced to metamodels for comp,constraints in comp_constraints.iteritems(): for c in constraints: new_c = str(c) for var,mapped_name in system_var_map.iteritems(): new_c = new_c.replace(var,mapped_name) sysopt.add_constraint(new_c) driver.workflow.add('sysopt') #setup paramter for fixedpointiterator comp=des_vars.keys()[0] mm='meta_model_%s'%comp #create some placeholder variables for the fixed point iteration for l in locals: s=system_var_map[l[0]].replace(".","_") s2='%s_store'%s self.parent.add(s2,Float(0.0)) driver.add_parameter(s2 , low=l[1].low, high=l[1].high) driver.add_constraint('%s = %s'%(system_var_map[l[1].target],s2)) for i,g in enumerate(global_dvs): s2='global%d_store'%i self.parent.add(s2,Float(0.0)) driver.add_parameter(s2 , low=g[1].low, high=g[1].high) driver.add_constraint('%s = %s'%(system_var_map[g[1].target],s2))
if __name__=="__main__": from openmdao.lib.optproblems.api import UnitScalableProblem p = UnitScalableProblem() p.architecture = BLISS2000() p.run()
OpenMDAO Home