def main(num_par_doe): # First, define a Problem to be able to optimize our function. sub = Problem(root=MultiMinGroup()) # set up our SLSQP optimizer sub.driver = subdriver = ScipyOptimizer() subdriver.options['optimizer'] = 'SLSQP' subdriver.options['disp'] = False # disable optimizer output # In this case, our design variable is indep.x, which happens # to be connected to the x parameter on our 'comp' component. subdriver.add_desvar("indep.x", lower=-pi, upper=pi) # We are minimizing comp.fx, so that's our objective. subdriver.add_objective("comp.fx") # Now, create our top level problem prob = Problem(root=Group()) prob.root.add("top_indep", IndepVarComp('x', 0.0)) # add our subproblem. Note that 'indep.x' is actually an unknown # inside of the subproblem, but outside of the subproblem we're treating # it as a parameter. prob.root.add("subprob", SubProblem(sub, params=['indep.x'], unknowns=['comp.fx'])) prob.root.connect("top_indep.x", "subprob.indep.x") # use a CaseDriver as our top level driver so we can run multiple # separate optimizations concurrently. We'll run 'num_par_doe' # concurrent cases. In this case we need no more than 2 because # we're only running 2 total cases. prob.driver = CaseDriver(num_par_doe=num_par_doe) prob.driver.add_desvar('top_indep.x') prob.driver.add_response(['subprob.indep.x', 'subprob.comp.fx']) # these are the two cases we're going to run. The top_indep.x values of # -1 and 1 will end up at the local and global minima when we run the # concurrent subproblem optimizers. prob.driver.cases = [[('top_indep.x', -1.0)], [('top_indep.x', 1.0)]] prob.setup(check=False) # run the concurrent optimizations prob.run() # collect responses for all of our input cases optvals = [ dict(resp) for resp, success, msg in prob.driver.get_responses() ] # find the minimum value of subprob.comp.fx in our responses global_opt = sorted(optvals, key=lambda x: x['subprob.comp.fx'])[0] return global_opt
def test_byobj_run(self): subprob = Problem(root=ExampleByObjGroup()) prob = Problem(root=Group()) prob.root.add('subprob', SubProblem(subprob, params=['G2.C1.x'], unknowns=['G3.C4.y'])) prob.setup(check=False) prob.run() self.assertEqual(prob['subprob.G3.C4.y'], 'fooC2C3C4')
def test_simplest_run_w_promote(self): subprob = Problem(root=Group()) subprob.root.add('indep', IndepVarComp('x', 7.0), promotes=['x']) subprob.root.add('mycomp', ExecComp('y=x*2.0'), promotes=['x','y']) prob = Problem(root=Group()) prob.root.add('subprob', SubProblem(subprob, ['x'], ['y'])) prob.setup(check=False) prob.run() result = prob.root.unknowns['subprob.y'] self.assertAlmostEqual(14.0, result, 3)
def test_calc_gradient(self): root = Group() root.add('indep', IndepVarComp('x', np.array([1., 1., 1., 1.]))) root.add('comp', RosenSuzuki()) root.connect('indep.x', 'comp.x') subprob = Problem(root) subprob.driver.add_desvar('indep.x', lower=-10, upper=99) subprob.driver.add_objective('comp.f') subprob.driver.add_constraint('comp.g', upper=0.) prob = Problem(root=Group()) prob.root.add('desvars', IndepVarComp('x', np.ones(4))) prob.root.add('subprob', SubProblem(subprob, params=['indep.x'], unknowns=['comp.f', 'comp.g'])) prob.root.connect('desvars.x', 'subprob.indep.x') prob.setup(check=False) prob.run() indep_list = ['desvars.x'] unknown_list = ['subprob.comp.f', 'subprob.comp.g'] # check that calc_gradient returns proper dict value when mode is 'fwd' J = prob.calc_gradient(indep_list, unknown_list, mode='fwd', return_format='dict') assert_almost_equal(J['subprob.comp.f']['desvars.x'], expectedJ['subprob.comp.f']['desvars.x']) assert_almost_equal(J['subprob.comp.g']['desvars.x'], expectedJ['subprob.comp.g']['desvars.x']) # check that calc_gradient returns proper array value when mode is 'fwd' J = prob.calc_gradient(indep_list, unknown_list, mode='fwd', return_format='array') assert_almost_equal(J, expectedJ_array) # check that calc_gradient returns proper dict value when mode is 'rev' J = prob.calc_gradient(indep_list, unknown_list, mode='rev', return_format='dict') assert_almost_equal(J['subprob.comp.f']['desvars.x'], expectedJ['subprob.comp.f']['desvars.x']) assert_almost_equal(J['subprob.comp.g']['desvars.x'], expectedJ['subprob.comp.g']['desvars.x']) # check that calc_gradient returns proper array value when mode is 'rev' J = prob.calc_gradient(indep_list, unknown_list, mode='rev', return_format='array') assert_almost_equal(J, expectedJ_array) # check that calc_gradient returns proper dict value when mode is 'fd' J = prob.calc_gradient(indep_list, unknown_list, mode='fd', return_format='dict') assert_almost_equal(J['subprob.comp.f']['desvars.x'], expectedJ['subprob.comp.f']['desvars.x'], decimal=5) assert_almost_equal(J['subprob.comp.g']['desvars.x'], expectedJ['subprob.comp.g']['desvars.x'], decimal=5) # check that calc_gradient returns proper array value when mode is 'fd' J = prob.calc_gradient(indep_list, unknown_list, mode='fd', return_format='array') assert_almost_equal(J, expectedJ_array, decimal=5)
def test_basic_run(self): subprob = Problem(root=ExampleGroup()) prob = Problem(root=Group()) prob.root.add('subprob', SubProblem(subprob, ['G2.C1.x'], ['G3.C4.y'])) prob.setup(check=False) prob.run() self.assertAlmostEqual(prob['subprob.G3.C4.y'], 40.) stream = cStringIO() # get test coverage for list_connections and make sure it doesn't barf prob.root.subprob.list_connections(stream=stream)
def test_general_access(self): sprob = Problem(root=Group()) sroot = sprob.root sroot.add('Indep', IndepVarComp('x', 7.0)) sroot.add('C1', ExecComp(['y1=x1*2.0', 'y2=x2*3.0'])) sroot.connect('Indep.x', 'C1.x1') prob = Problem(root=Group()) prob.root.add('subprob', SubProblem(sprob, params=['Indep.x', 'C1.x2'], unknowns=['C1.y1', 'C1.y2'])) prob.setup(check=False) prob['subprob.Indep.x'] = 99.0 # set a param that maps to an unknown in subproblem prob['subprob.C1.x2'] = 5.0 # set a dangling param prob.run() self.assertEqual(prob['subprob.C1.y1'], 198.0) self.assertEqual(prob['subprob.C1.y2'], 15.0)
def test_opt_cylinder_nested(self, which_err=None, check=False): prob = Problem(root=Group()) driver = prob.driver = ScipyOptimizer() prob.driver.options['optimizer'] = 'SLSQP' prob.driver.options['disp'] = False prob.driver.add_desvar("indep.r", lower=0.0, upper=1.e99) prob.driver.add_desvar("indep.h", lower=0.0, upper=1.e99) prob.driver.add_objective("subprob.cylinder.area") prob.driver.add_constraint("subprob.cylinder.volume", equals=1.5) # we need IndepVarComp for model params at top level because the top level # driver has them as design vars. prob.root.add("indep", IndepVarComp([('r', 1.0, {'units':'cm'}), ('h', 1.0, {'units':'cm'})])) subprob = ErrProb(which_err=which_err, root=CylinderGroup()) prob.root.add('subprob', SubProblem(subprob, params=['indep.r', 'indep.h'], unknowns=['cylinder.area', 'cylinder.volume'])) prob.root.connect('indep.r', 'subprob.indep.r') prob.root.connect('indep.h', 'subprob.indep.h') # we have to set check=True to test some error handling, but never # want to see the output, so just send it to a cStringIO prob.setup(check=check, out_stream=cStringIO()) prob.run() self.assertAlmostEqual(prob['subprob.cylinder.volume'], 1.5, places=4, msg="volume should be 1.5, but got %s" % prob['subprob.cylinder.volume']) for name, opt in cylinder_opts: self.assertAlmostEqual(prob[name], opt, places=4, msg="%s should be %s, but got %s" % (name, opt, prob[name])) prob.cleanup()
def test_opt_cylinder_nested_w_promotes(self): prob = Problem(root=Group()) driver = prob.driver = ScipyOptimizer() prob.driver.options['optimizer'] = 'SLSQP' prob.driver.options['disp'] = False prob.driver.add_desvar("indep.r", lower=0.0, upper=1.e99) prob.driver.add_desvar("indep.h", lower=0.0, upper=1.e99) prob.driver.add_objective("cylinder.area") prob.driver.add_constraint("cylinder.volume", equals=1.5) # we need IndepVarComp for model params at top level because the top level # driver has them as design vars. prob.root.add("indep", IndepVarComp([('r', 1.0, {'units':'cm'}), ('h', 1.0, {'units':'cm'})])) subprob = Problem(root=CylinderGroup()) prob.root.add('subprob', SubProblem(subprob, params=list(prob.driver._desvars), unknowns=list(driver._cons)+list(driver._objs)), promotes=['indep.r', 'indep.h', 'cylinder.area', 'cylinder.volume']) # the names of the indep vars match the promoted names from the subproblem, so # they're implicitly connected. prob.setup(check=False) prob.run() for name, opt in cylinder_opts: self.assertAlmostEqual(prob[name], opt, places=4, msg="%s should be %s, but got %s" % (name, opt, prob[name])) self.assertAlmostEqual(prob['cylinder.volume'], 1.5, places=4, msg="volume should be 1.5, but got %s" % prob['cylinder.volume'])
# TopProblem: define a Problem to set up different optimization cases top = Problem(root=Group()) # TopProblem: add independent variables top.root.add('indep1', IndepVarComp('range', 50.0)) top.root.add('indep2', IndepVarComp('rProp', 100.0)) top.root.add('indep3', IndepVarComp('cruiseSpeed', 50.0)) top.root.add('indep4', IndepVarComp('batteryMass', 11.70)) top.root.add('indep5', IndepVarComp('motorMass', 3.00)) top.root.add('indep6', IndepVarComp('mtom', 6.500)) # top.root.add('indep7', IndepVarComp('vehicle', 'tiltwing')) # 1st get this working with just the tiltwing # TopProblem: add the SubProblem top.root.add('subprob', SubProblem(sub, params=['indep1.range', 'indep2.rProp', \ 'indep3.cruiseSpeed', 'indep4.batteryMass', \ 'indep5.motorMass', 'indep6.mtom'], unknowns=['OperatingCost.C_costPerFlight'])) # TopProblem: connect top's independent variables to sub's params top.root.connect('indep1.range', 'subprob.indep1.range') top.root.connect('indep2.rProp', 'subprob.indep2.rProp') # Each of SubProblem's IndepVarComp components has to be connected (maybe promoted works too) to a top.root.connect('indep3.cruiseSpeed', 'subprob.indep3.cruiseSpeed') # IndepVarComp component in the top level. top.root.connect('indep4.batteryMass', 'subprob.indep4.batteryMass') # Alternatively it might make more sense to output the design variables states as metrics. top.root.connect('indep5.motorMass', 'subprob.indep5.motorMass') top.root.connect('indep6.mtom', 'subprob.indep6.mtom') # TopProblem: set up the parameter study # for a parameter study, the following drivers can be used: # UniformDriver, FullFactorialDriver, LatinHypercubeDriver, OptimizedLatinHypercubeDriver # in this case, we will use FullFactorialDriver top.driver = FullFactorialDriver(num_levels=77)
#sub.driver.opt_settings['rhobeg'] = 1.0 # COBYLA-specific setting. Initial step size. Default: 1.0 #sub.driver.opt_settings['catol'] = 0.1 # COBYLA-specific setting. Absolute tolerance for constraint violations. Default: 0.1 # Add design variables, objective, and constraints to the optimization driver sub.driver.add_desvar('p1.x_init', lower=-50, upper=50) sub.driver.add_objective('Paraboloid.f_xy') # Instantiate a Problem 'top' # Instantiate a Group and add it to top top = Problem() top.root = Group() # Add sub to top as a SubProblem called 'Sub' # Include sub's Problem Inputs and Problem Outputs in 'params' and 'unknowns' fields of SubProblem top.root.add( 'Sub', SubProblem(sub, params=['p1.x_init'], unknowns=[]) ) # This is where you designate what to expose to the outside world) # Initialize x and y as IndepVarComps and add them to top's root group top.root.add('p1', IndepVarComp('x_init', 0.0)) # Connections top.root.connect('p1.x_init', 'Sub.p1.x_init') # Add driver top.driver = FullFactorialDriver(num_levels=11) # Add design variables and objectives to the parameter study driver top.driver.add_desvar('p1.x_init', lower=-50, upper=50) # Data collection
# Instantiate a mid-level Problem 'OptimizationProfiler' # Instantiate a Group and add it to OptimizationProfiler OptimizationProfiler = Problem() OptimizationProfiler.root = Group() # Initialize x and y as IndepVarComps and add them to OptimizationProfiler's root group OptimizationProfiler.root.add('p1', IndepVarComp('x_0', 0.0)) OptimizationProfiler.root.add('p2', IndepVarComp('y_0', 0.0)) OptimizationProfiler.root.add('p3', IndepVarComp('n', 0.0)) # Add optimizationProblem to OptimizationProfiler as a SubProblem called 'OptimizationProblem' # Include optimizationProblem's Problem Inputs and Problem Outputs in 'params' and 'unknowns' fields of SubProblem OptimizationProfiler.root.add( 'OptimizationProblem', SubProblem(optimizationProblem, params=['p1.x', 'p2.y'], unknowns=['output1.x_f', 'output2.y_f', 'Paraboloid.f_xy']) ) # This is where you designate what to expose to the outside world # Add the 'SaveTime' and 'MeasureTime' Components to OptimizationProfiler's root Group. OptimizationProfiler.root.add('SaveTime', SaveTime()) OptimizationProfiler.root.add('MeasureTime', MeasureTime()) # Connections OptimizationProfiler.root.connect('p1.x_0', 'SaveTime.pass_in') OptimizationProfiler.root.connect('p2.y_0', 'OptimizationProblem.p2.y') OptimizationProfiler.root.connect('SaveTime.pass_out', 'OptimizationProblem.p1.x') OptimizationProfiler.root.connect('OptimizationProblem.Paraboloid.f_xy', 'MeasureTime.finished')
# Add design variables, objective, and constraints to the optimization driver sub.driver.add_desvar('p1.x', lower=-50, upper=50) sub.driver.add_desvar('p2.y_i', lower=-50, upper=50) sub.driver.add_objective('Paraboloid.f_xy') # Instantiate a Problem 'top' # Instantiate a Group and add it to top top = Problem() top.root = Group() # Add sub to top as a SubProblem called 'Sub' # Include sub's Problem Inputs and Problem Outputs in 'params' and 'unknowns' fields of SubProblem top.root.add( 'Sub', SubProblem( sub, params=['p2.y_i', 'p3.z'], unknowns=['Paraboloid.f_xy', 'p1.x', 'output1.y_f', 'output2.z']) ) # This is where you designate what to expose to the outside world) # Add PythonWrapper Component 'Sum' top.root.add('Sum', Sum()) # Initialize x and z as IndepVarComps and add them to top's root group top.root.add('p1', IndepVarComp('y_init', 0.0)) top.root.add('p2', IndepVarComp('z', 0.0)) # Connections top.root.connect('p1.y_init', 'Sub.p2.y_i') top.root.connect('p2.z', 'Sub.p3.z') top.root.connect('Sub.output1.y_f', 'Sum.y') top.root.connect('Sub.output2.z', 'Sum.z')
# the constraint is violated, it does not eliminate the violation # ^ Jonathan comments that you often have to add constraints for the design variable ranges since the design variable bounds # are either not passed along to SciPy or are not enforced. # Instantiate a top-level Problem 'top' # Instantiate a Group and add it to sub top = Problem() top.root = Group() # Initialize y as IndepVarComp and add it to top's root group top.root.add('p2', IndepVarComp('y', -14.0)) # Add Problem 'sub' to 'top' as a SubProblem top.root.add( 'subprob', SubProblem(sub, params=['p2.y'], unknowns=[ 'P.f_xy' ])) # This is where you designate what to expose to the outside world # Connect top's IndepVarComps to SubProblem's params top.root.connect('p2.y', 'subprob.p2.y') # Add driver top.driver = FullFactorialDriver(num_levels=20) # Add design variables, objective, and constraints top.driver.add_desvar('p2.y', lower=-50, upper=50) # Data collection recorder = SqliteRecorder('record_results') recorder.options['record_params'] = True recorder.options['record_metadata'] = True
def test_opt_over_doe_uq(self): np.random.seed(42) prob = Problem(impl=impl, root=Group()) prob.root.deriv_options['type'] = 'fd' subprob = Problem(impl=impl, root=SellarDerivatives()) subprob.root.deriv_options['type'] = 'fd' if MPI: npardoe = self.N_PROCS else: npardoe = 1 subprob.driver = UQTestDriver(nsamples=100, num_par_doe=npardoe) subprob.driver.add_desvar('z', std_dev=1e-2) subprob.driver.add_desvar('x', std_dev=1e-2) subprob.driver.add_response('obj') subprob.driver.add_response('con1') subprob.driver.add_response('con2') #subprob.driver.recorders.append(SqliteRecorder("subsellar.db")) prob.root.add("indeps", IndepVarComp([('x', 1.0), ('z', np.array([5.0, 2.0]))]), promotes=['x', 'z']) prob.root.add( "sub", SubProblem(subprob, params=['z', 'x'], unknowns=['obj', 'con1', 'con2'])) prob.root.connect('x', 'sub.x') prob.root.connect('z', 'sub.z') # top level driver setup prob.driver = ScipyOptimizer() prob.driver.options['optimizer'] = 'SLSQP' prob.driver.options['tol'] = 1.0e-8 prob.driver.options['maxiter'] = 50 prob.driver.options['disp'] = False prob.driver.add_desvar('z', lower=np.array([-10.0, 0.0]), upper=np.array([10.0, 10.0])) prob.driver.add_desvar('x', lower=0.0, upper=10.0) prob.driver.add_objective('sub.obj') prob.driver.add_constraint('sub.con1', upper=0.0) prob.driver.add_constraint('sub.con2', upper=0.0) #prob.driver.recorders.append(SqliteRecorder("sellar.db")) prob.setup(check=False) prob.run() tol = 1.e-3 assert_rel_error(self, prob['sub.obj'], 3.1833940, tol) assert_rel_error(self, prob['z'][0], 1.977639, tol) assert_rel_error(self, prob['z'][1], 0.0, tol) assert_rel_error(self, prob['x'], 0.0, tol)
# Instantiate a top-level Problem 'OptimizationProfiler' # Instantiate a Group and add it to OptimizationProfiler OptimizationProfiler = Problem() OptimizationProfiler.root = Group() # Initialize x and y as IndepVarComps and add them to OptimizationProfiler's root group OptimizationProfiler.root.add('p1', IndepVarComp('x_0', 0.0)) OptimizationProfiler.root.add('p2', IndepVarComp('y_0', 0.0)) # Add optimizationProblem to OptimizationProfiler as a SubProblem called 'OptimizationProblem' # Include optimizationProblem's Problem Inputs and Problem Outputs in 'params' and 'unknowns' fields SubProblem OptimizationProfiler.root.add( 'OptimizationProblem', SubProblem(optimizationProblem, params=['p1.x', 'p2.y'], unknowns=['output1.x_f', 'output2.y_f', 'Paraboloid.f_xy']) ) # This is where you designate what to expose to the outside world # Add the 'SaveTime' and 'MeasureTime' Components to OptimizationProfiler's root Group. OptimizationProfiler.root.add('SaveTime', SaveTime()) OptimizationProfiler.root.add('MeasureTime', MeasureTime()) # Connections OptimizationProfiler.root.connect('p1.x_0', 'SaveTime.pass_in') OptimizationProfiler.root.connect('p2.y_0', 'OptimizationProblem.p2.y') OptimizationProfiler.root.connect('SaveTime.pass_out', 'OptimizationProblem.p1.x') OptimizationProfiler.root.connect('OptimizationProblem.Paraboloid.f_xy', 'MeasureTime.finished')
# Instantiate a top-level Problem 'ParaboloidOptimization' # Instantiate a Group and add it to ParaboloidOptimization ParaboloidOptimization = Problem() ParaboloidOptimization.root = Group() # Initialize x and y as IndepVarComps and add them to ParaboloidOptimization's root group ParaboloidOptimization.root.add('p1', IndepVarComp('x', 0.0)) ParaboloidOptimization.root.add('p2', IndepVarComp('y', 0.0)) # Add paraboloidProblem to ParaboloidOptimization as a SubProblem called 'ParaboloidProblem' # Include paraboloidProblem's Problem Inputs and Problem Outputs in 'params' and 'unknowns' fields SubProblem ParaboloidOptimization.root.add( 'ParaboloidProblem', SubProblem(paraboloidProblem, params=['p1.x', 'p2.y'], unknowns=['Paraboloid.f_xy']) ) # This is where you designate what to expose to the outside world # Connect ParaboloidOptimization's IndepVarComps to ParaboloidProblem's params ParaboloidOptimization.root.connect('p1.x', 'ParaboloidProblem.p1.x') ParaboloidOptimization.root.connect('p2.y', 'ParaboloidProblem.p2.y') # Add driver ParaboloidOptimization.driver = ScipyOptimizer() # Modify the optimization driver's settings ParaboloidOptimization.driver.options[ 'optimizer'] = 'COBYLA' # Type of Optimizer. 'COBYLA' does not require derivatives ParaboloidOptimization.driver.options[ 'tol'] = 1.0e-4 # Tolerance for termination. Not sure exactly what it represents. Default: 1.0e-6
# Add design variables, objective, and constraints to the optimization driver sub.driver.add_desvar('p1.x_init', lower=-50, upper=50) sub.driver.add_objective('Paraboloid.f_xy') # Instantiate a Problem 'top' # Instantiate a Group and add it to top top = Problem() top.root = Group() # Add sub to top as a SubProblem called 'Sub' # Include sub's Problem Inputs and Problem Outputs in 'params' and 'unknowns' fields of SubProblem top.root.add( 'Sub', SubProblem(sub, params=['p1.x_init'], unknowns=[ 'Paraboloid.f_xy' ])) # This is where you designate what to expose to the outside world) # Initialize x and y as IndepVarComps and add them to top's root group top.root.add('p1', IndepVarComp('x_init', 0.0)) # Connections top.root.connect('p1.x_init', 'Sub.p1.x_init') # Add driver top.driver = FullFactorialDriver(num_levels=11) # Add design variables and objectives to the parameter study driver top.driver.add_desvar('p1.x_init', lower=-50, upper=50) top.driver.add_objective('Sub.Paraboloid.f_xy')
top.root.add('indep1', IndepVarComp('range', 50.0)) top.root.add('indep2', IndepVarComp('rProp', 30.0)) top.root.add('indep3', IndepVarComp('cruiseSpeed', 50.0)) top.root.add('indep4', IndepVarComp('batteryMass', 11.70)) top.root.add('indep5', IndepVarComp('motorMass', 3.00)) top.root.add('indep6', IndepVarComp('mtom', 6.500)) # top.root.add('indep7', IndepVarComp('vehicle', 'tiltwing')) # 1st get this working with just the tiltwing # TopProblem: add the SubProblem top.root.add('subprob', SubProblem(sub, params=['indep1.range', 'indep2.rProp', \ 'indep3.cruiseSpeed', 'indep4.batteryMass', \ 'indep5.motorMass', 'indep6.mtom'], unknowns=['OperatingCost.C_costPerFlight', \ 'ConfigWeight.mass_rotor', \ 'ConfigWeight.mass_tailRotor', \ 'ConfigWeight.mass_wire', \ 'ConfigWeight.mass_fuselage', \ 'ConfigWeight.mass_m', \ 'HoverPower.hoverPower_PMax', \ 'HoverPower.hoverPower_PMaxBattery', \ 'CruisePower.PCruise', \ 'CruisePower.PBattery'])) # TopProblem: connect top's independent variables to sub's params top.root.connect('indep1.range', 'subprob.indep1.range') top.root.connect( 'indep2.rProp', 'subprob.indep2.rProp' ) # Each of SubProblem's IndepVarComp components has to be connected (maybe promoted works too) to a top.root.connect('indep3.cruiseSpeed', 'subprob.indep3.cruiseSpeed' ) # IndepVarComp component in the top level. top.root.connect(
def with_problem(mdao_config, original_dir, override_driver=None, additional_recorders=(), is_subproblem=False, append_csv=False, profile=False): testbenchexecutor.progress_service.update_progress("Configuring PET...", -1, -1) # TODO: can we support more than one driver if len(mdao_config['drivers']) == 0: driver = None else: driver = next(iter(mdao_config['drivers'].values())) top = Problem(impl=impl) root = top.root = Group() recorder = None driver_params = {'original_dir': original_dir} if driver is not None: eval( compile(driver['details'].get('Code', ''), '<driver Code>', 'exec'), globals(), driver_params) subProblemInputMeta = {} subProblemOutputMeta = {} if driver is not None: if driver['type'] == 'optimizer': if driver.get('details', {}).get('OptimizationFunction') == 'Custom': class_path = driver['details']['OptimizationClass'].split('.') mod = __import__('.'.join(class_path[:-1]), fromlist=[class_path[-1]]) top.driver = getattr(mod, class_path[-1])() else: top.driver = ScipyOptimizer() top.driver.options['optimizer'] = str( driver.get('details', {}).get('OptimizationFunction', 'SLSQP')) for key, value in six.iteritems(driver_params): try: top.driver.options[key] = value except KeyError: pass # Ignore options that aren't valid for driver elif driver['type'] == 'parameterStudy': drivers = { "Uniform": UniformDriver, "Full Factorial": FullFactorialDriver, "Latin Hypercube": LatinHypercubeDriver, "Opt Latin Hypercube": OptimizedLatinHypercubeDriver, "CSV File": CsvDriver, } driver_type = drivers.get(driver['details']['DOEType']) if driver_type is None: raise Exception('DOEType "{}" is unsupported'.format( driver['details']['DOEType'])) if override_driver is None: top.driver = driver_type(**driver_params) else: top.driver = override_driver seed = getattr(top.driver, 'seed', None) if seed is not None: print('Using random seed {}'.format(seed)) elif driver['type'] == 'PCCDriver': import PCC.pcc_driver driver_params.update(driver['details']) driver_params[ "_run_mdao_subproblem_output_meta"] = subProblemOutputMeta top.driver = PCC.pcc_driver.PCCdriver(**driver_params) else: raise ValueError('Unsupported driver type %s' % driver['type']) driver_vars = [] for var_name, var in six.iteritems(driver['designVariables']): if var.get('type', 'double') == 'double': default = 0.0 range_min = var.get('RangeMin') range_max = var.get('RangeMax') if range_min is not None and range_max is not None: default = range_min + (range_max - range_min) / 2 driver_vars.append((var_name, default, {})) elif var['type'] == 'enum': driver_vars.append((var_name, var['items'][0], { "pass_by_obj": True })) elif var['type'] == 'int': driver_vars.append((var_name, 0, {})) else: raise ValueError( 'Unimplemented designVariable type "{}"'.format( var['type'])) units = var.get('units') if units: driver_vars[-1][2]['units'] = str(var['units']) root.add(get_desvar_path('').split('.')[0], IndepVarComp(driver_vars)) for var_name, var in six.iteritems(driver['designVariables']): if var.get('type', 'double') == 'double': top.driver.add_desvar(get_desvar_path(var_name), lower=var.get('RangeMin'), upper=var.get('RangeMax')) elif var['type'] == 'enum': driver_vars.append((var_name, var['items'][0], { "pass_by_obj": True })) formatted_name = get_desvar_path(var_name) top.driver.add_desvar(formatted_name) top.driver._desvars[formatted_name]['type'] = var['type'] top.driver._desvars[formatted_name]['items'] = var['items'] elif var['type'] == 'int': driver_vars.append((var_name, 0.0)) formatted_name = get_desvar_path(var_name) top.driver.add_desvar(formatted_name, lower=var.get('RangeMin'), upper=var.get('RangeMax')) top.driver._desvars[formatted_name]['type'] = var['type'] else: raise ValueError( 'Unimplemented designVariable type "{}"'.format( var['type'])) for subProblemName, subProblemConfig in six.iteritems( mdao_config.get('subProblems', {})): subProblemDir = os.path.join(original_dir, subProblemName) with with_problem(subProblemConfig, subProblemDir, is_subproblem=True) as (subProblem, inputMeta, outputMeta): root.add(subProblemName, subProblem) subProblemInputMeta[subProblemName] = inputMeta subProblemOutputMeta[subProblemName] = outputMeta if is_subproblem: subProblemInputs = [] inputMeta = {} for name, problemInput in six.iteritems(mdao_config['problemInputs']): if problemInput.get("innerSource"): if problemInput["innerSource"][0] in mdao_config['drivers']: path = get_desvar_path(problemInput["innerSource"][1]) else: path = '{}.{}'.format(problemInput["innerSource"][0], problemInput["innerSource"][1]) subProblemInputs.append(path) inputMeta[name] = path else: # TODO: How important is it to figure out the correct type here? # We might be able to infer the type from a component that connects to # this ProblemInput, but might have to refer to something outside the # subproblem (initial_value, pass_by_obj) = get_problem_input_value(problemInput) root.add( name, IndepVarComp(name, initial_value, pass_by_obj=pass_by_obj)) path = "{0}.{0}".format(name) subProblemInputs.append(path) inputMeta[name] = path # TODO: Handle direct connection between ProblemInput and ProblemOutput (single-element Source) # TODO: Pass-through ExecComps to allow direct ProblemInput->ProblemOutput connections to behave subProblemOutputs = [] outputMeta = {} for name, source in six.iteritems(mdao_config['problemOutputs']): if len(source) == 1: if source[0] in mdao_config[ 'problemInputs'] and 'innerSource' in mdao_config[ 'problemInputs'][source[0]]: # Assume inner source is a design variable desvar = driver['designVariables'] passByObj = False if desvar.get('type', 'double') == 'double': initialVal = 0.0 elif desvar['type'] == 'enum': initialVal = '' # TODO or maybe initialVal = 0.0 elif desvar['type'] == 'int': initialVal = 0 else: raise ValueError( 'Unimplemented designVariable type "{}"'.format( desvar['type'])) else: if source[0] in mdao_config['problemInputs']: (initialVal, passByObj) = get_problem_input_value( mdao_config['problemInputs'][source[0]]) else: raise ValueError( 'Missing ProblemOutput source: {}'.format( source[0])) comp_name = "pass_through_{}".format(name) comp = PassThroughComponent() comp.add_var(name, initialVal) root.add(comp_name, comp) inputPath = "{}.{}".format(comp_name, name) path = "{}.{}_out".format(comp_name, name) root.connect(inputMeta[source[0]], inputPath) else: if source[0] in mdao_config['drivers']: # TODO: If it's legal for this desvar to also point to a ProblemInput, # we need to create a PassThroughComponent just like above this_driver = mdao_config['drivers'][source[0]] if source[1] in this_driver.get('designVariables', {}): path = get_desvar_path(source[1]) else: # Source is an objective, ivar, or constraint; need to get the actual source if source[1] in this_driver.get('objectives', {}): driver_output_type = 'objectives' elif source[1] in this_driver.get('constraints', {}): driver_output_type = 'constraints' elif source[1] in this_driver.get( 'intermediateVariables', {}): driver_output_type = 'intermediateVariables' else: raise ValueError( 'Driver output "{}"" not found'.format( source[1])) real_source = this_driver[driver_output_type][ source[1]]['source'] if real_source[0] in mdao_config['subProblems']: unknown_name = subProblemOutputMeta[ real_source[0]][real_source[1]] path = '{}.{}'.format(real_source[0], unknown_name) else: path = '{}.{}'.format(real_source[0], real_source[1]) elif source[0] in mdao_config['subProblems']: unknown_name = subProblemOutputMeta[source[0]][source[1]] path = '{}.{}'.format(source[0], unknown_name) else: path = '{}.{}'.format(source[0], source[1]) subProblemOutputs.append(path) outputMeta[name] = path def get_sorted_components(): """Apply Tarjan's algorithm to the Components.""" visited = {} tbs_sorted = [] def get_ordinal(name): ordinal = visited.get(name, -1) if ordinal is None: raise ValueError('Loop involving component "{}"'.format(name)) if ordinal != -1: return ordinal component = mdao_config['components'][name] visited[name] = None ordinal = 0 for source in ( param.get('source') for param in component.get('parameters', {}).values()): if not source: continue if source[0] in mdao_config['drivers']: continue if source[0] in mdao_config.get('problemInputs', {}): continue if source[0] in mdao_config.get('subProblems', {}): continue ordinal = max(ordinal, get_ordinal(source[0]) + 1) visited[name] = ordinal tbs_sorted.append(name) return ordinal for component_name in mdao_config['components']: get_ordinal(component_name) return tbs_sorted tbs_sorted = get_sorted_components() # TestBenchComponents look at params they're connected to, so create them last def is_testbenchcomponent(component_name): return mdao_config['components'][component_name].get( 'type', 'TestBenchComponent') == 'TestBenchComponent' tbs_sorted = sorted(tbs_sorted, key=is_testbenchcomponent) for component_name in tbs_sorted: component = mdao_config['components'][component_name] mdao_component = instantiate_component(component, component_name, mdao_config, root, subProblemOutputMeta) root.add(component_name, mdao_component) for component_name, component in six.iteritems(mdao_config['components']): for parameter_name, parameter in six.iteritems( component.get('parameters', {})): if parameter.get('source'): source = parameter['source'] if len(source) == 1 and is_subproblem: # Points to a top-level ProblemInput; look up what design variable it points to and reference that IVC problemInputName = source[0] if problemInputName in inputMeta: root.connect( inputMeta[problemInputName], '{}.{}'.format( component_name, _get_param_name(parameter_name, component.get('type')))) else: # TODO: Warn or fail if name isn't found? print("WARNING: Missing problem input reference") else: if source[0] in mdao_config['drivers']: # print('driver{}.{}'.format(source[1], source[1])) root.connect( get_desvar_path(source[1]), '{}.{}'.format( component_name, _get_param_name(parameter_name, component.get('type')))) elif source[0] in mdao_config.get('subProblems', {}): # Map subproblem output name to actual unknown name unknown_name = subProblemOutputMeta[source[0]][ source[1]] root.connect( '{}.{}'.format(source[0], unknown_name), '{}.{}'.format( component_name, _get_param_name(parameter_name, component.get('type')))) else: root.connect( '{}.{}'.format(source[0], source[1]), '{}.{}'.format( component_name, _get_param_name(parameter_name, component.get('type')))) else: pass # TODO warn or fail? for subProblemName, subProblem in six.iteritems( mdao_config.get('subProblems', {})): for problemInputName, problemInput in six.iteritems( subProblem.get('problemInputs', {})): if problemInput.get('outerSource'): source = problemInput['outerSource'] subProblemInputPath = subProblemInputMeta[subProblemName][ problemInputName] if len(source) == 1 and is_subproblem: problemInputName = source[0] if problemInputName in inputMeta: root.connect( inputMeta[problemInputName], '{}.{}'.format(subProblemName, subProblemInputPath)) else: # TODO: Warn or fail if name isn't found? print("WARNING: Missing problem input reference") else: if source[0] in mdao_config['drivers']: # print('driver{}.{}'.format(source[1], source[1])) root.connect( get_desvar_path(source[1]), '{}.{}'.format(subProblemName, subProblemInputPath)) elif source[0] in mdao_config['subProblems']: # Map subproblem output name to actual unknown name unknown_name = subProblemOutputMeta[source[0]][ source[1]] root.connect( '{}.{}'.format(source[0], unknown_name), '{}.{}'.format(subProblemName, subProblemInputPath)) else: root.connect( '{}.{}'.format(source[0], source[1]), '{}.{}'.format(subProblemName, subProblemInputPath)) else: print("Failed to connect") pass # TODO warn or fail? # TODO: Make sure objectives/constraints coming from subproblems and # from ProblemInputs/ProblemOutputs are handled correctly if driver is not None and driver['type'] == 'optimizer': for objective in six.itervalues(driver['objectives']): if objective["source"][0] in mdao_config.get('subProblems', {}): unknown_name = subProblemOutputMeta[objective["source"][0]][ objective["source"][1]] top.driver.add_objective("{}.{}".format( objective["source"][0], unknown_name)) else: top.driver.add_objective(str('.'.join(objective['source']))) for constraint in six.itervalues(driver['constraints']): if constraint['source'][0] in mdao_config['drivers']: # References the driver; need to get the path to the design var constraintPath = get_desvar_path(constraint['source'][1]) elif constraint['source'][0] in mdao_config.get('subProblems', {}): unknown_name = subProblemOutputMeta[objective.source[0]][ objective.source[1]] constraintPath = "{}.{}".format(objective.source[0], unknown_name) else: constraintPath = str('.'.join(constraint['source'])) if 'RangeMin' in constraint and 'RangeMax' in constraint: top.driver.add_constraint(constraintPath, lower=constraint['RangeMin'], upper=constraint['RangeMax']) elif 'RangeMin' in constraint and 'RangeMax' not in constraint: top.driver.add_constraint(constraintPath, lower=constraint['RangeMin']) elif 'RangeMin' not in constraint and 'RangeMax' in constraint: top.driver.add_constraint(constraintPath, upper=constraint['RangeMax']) else: pass # TODO: No min or max provided with constraint; warn or fail here? def add_recorders(): recorders = [] design_var_map = { get_desvar_path(designVariable): designVariable for designVariable in driver['designVariables'] } objective_map = { '{}.{}'.format(objective['source'][0], objective['source'][1]): objective_name for objective_name, objective in six.iteritems( driver['objectives']) } intermediate_var_map = { '{}.{}'.format(intermediate_var['source'][0], intermediate_var['source'][1]): intermediate_var_name for intermediate_var_name, intermediate_var in six.iteritems( driver.get('intermediateVariables', {})) } constants_map = {} for name, constant in ( c for c in six.iteritems(mdao_config['components']) if c[1].get('type', 'TestBenchComponent') == 'IndepVarComp'): constants_map.update({ '{}.{}'.format(name, unknown): unknown for unknown in constant['unknowns'] }) constraints_map = { '{}.{}'.format(constraint['source'][0], constraint['source'][1]): constraint_name for constraint_name, constraint in six.iteritems( driver.get('constraints', {})) if constraint['source'][0] not in mdao_config['drivers'] } # All constraints that don't point back to design variables unknowns_map = defaultdict(list) def add_to_unknowns(map): for key, val in six.iteritems(map): unknowns_map[key].append(val) add_to_unknowns(design_var_map) add_to_unknowns(objective_map) add_to_unknowns(intermediate_var_map) add_to_unknowns(constants_map) add_to_unknowns(constraints_map) new_unknowns_map = defaultdict(list) # Locate/fix any unknowns that point to subproblem outputs for unknown_path, unknown_names in six.iteritems(unknowns_map): for unknown_name in unknown_names: split_path = unknown_path.split('.') if split_path[0] in subProblemOutputMeta: split_path[1] = subProblemOutputMeta[split_path[0]][ split_path[1]] new_path = '.'.join(split_path) new_unknowns_map[new_path].append(unknown_name) else: new_unknowns_map[unknown_path].append(unknown_name) unknowns_map = new_unknowns_map for recorder in mdao_config.get('recorders', [{ 'type': 'DriverCsvRecorder', 'filename': 'output.csv' }]): if recorder['type'] == 'DriverCsvRecorder': mode = 'w' exists = os.path.isfile(recorder['filename']) if RestartRecorder.is_restartable(original_dir) or append_csv: mode = 'a' if six.PY2: mode += 'b' open_kwargs = {} else: open_kwargs = {'newline': ''} recorder = MappingCsvRecorder({}, unknowns_map, io.open(recorder['filename'], mode, **open_kwargs), include_id=recorder.get( 'include_id', False)) if (append_csv and exists) or mode == 'ab': recorder._wrote_header = True elif recorder['type'] == 'AllCsvRecorder': mode = 'w' if six.PY2: mode += 'b' open_kwargs = {} else: open_kwargs = {'newline': ''} recorder = CsvRecorder( out=io.open(recorder['filename'], mode, **open_kwargs)) elif recorder['type'] == 'CouchDBRecorder': recorder = CouchDBRecorder( recorder.get('url', 'http://localhost:5984/'), recorder['run_id']) recorder.options['record_params'] = True recorder.options['record_unknowns'] = True recorder.options['record_resids'] = False recorder.options['includes'] = list(unknowns_map.keys()) else: mod_name = '.'.join(recorder['type'].split('.')[:-1]) class_name = recorder['type'].split('.')[-1] recorder = getattr(importlib.import_module(mod_name), class_name)() top.driver.add_recorder(recorder) return recorders if is_subproblem: recorders = [] # print("SubProblemInputs:", subProblemInputs) # print("SubProblemOutputs:", subProblemOutputs) # print("InputMeta:", inputMeta) # print("OutputMeta:", outputMeta) top.setup() subProblem = SubProblem(top, params=subProblemInputs, unknowns=subProblemOutputs) yield (subProblem, inputMeta, outputMeta) else: if driver: recorders = add_recorders() else: recorders = [] for recorder in additional_recorders: recorders.append(recorder) top.driver.add_recorder(recorder) if profile: openmdao_profile.setup(top) openmdao_profile.start() try: top.setup() # from openmdao.devtools.debug import dump_meta # dump_meta(top.root) # for subproblemName in six.iterkeys(mdao_config['subProblems']): # subProblem = top.root.find_subsystem(subproblemName) # subProblem._problem.root.dump(verbose=True) # top.root.dump(verbose=True) yield top finally: for recorder in recorders: recorder.close()