openmdao

Iterating a model in OpenMDAO


In OpenMDAO, within an optimisation driver, is there any way to repeat/iterate a part of the model (a group) over a range of inputs?

I'm developing an optimisation framework built on OpenMDAO. So far, I have been running the optimisation for one specific load case, and now I would like to extend it to optimising for an objective that is calculated as an average over a number of load cases.

For each set of design variables, I would like to run the model inside a for loop (as in for load_case in load_cases), record the result of each iteration, and then average them out to get the value of the objective for the driver. Is something like this possible?


Solution

  • Your suggested solution is fine. You have the right idea. I would just suggest that the two duplicate cost classes are not needed.

    import openmdao.api as om
    
    class Cost(om.ExplicitComponent):
        ''' normally defined in a separate script for convenience '''
    
        def setup(self):
            self.add_input('x', val=0)
            self.add_input('y', val=0)
            self.add_output('cost', val=0, units='USD')
            
        def setup_partials(self):
            self.declare_partials('*', '*', method='fd')
    
        def compute(self, inputs, outputs):
            cost_steel = inputs['x']
            cost_ballast = inputs['y']
    
            cost = cost_steel + cost_ballast
    
            outputs['cost'] = cost
                     
    
    class MDA(om.Group):
    
        class ObjCmp(om.ExplicitComponent):
            
            def setup(self):
                self.add_input('cost_1', val=0, units='USD')
                self.add_input('cost_2', val=0, units='USD')
                self.add_output('obj', val=0.0, units='USD')
    
            def setup_partials(self):
                self.declare_partials('*', '*', method='fd')
    
            def compute(self, inputs, outputs):
                outputs['obj'] =  inputs['cost_1'] + inputs['cost_2']
    
        def setup(self):     
            group_ls = self.add_subsystem('ls1', om.Group(), promotes_inputs=['x','y'],
                                promotes_outputs=['cost_1']) 
    
            group_ls.add_subsystem('Cost1', Cost(), promotes_inputs=['x','y'],
                               promotes_outputs=[('cost', 'cost_1')])
                               
            group_ls.add_subsystem('Cost2', Cost(), promotes_inputs=['x','y'],
                               promotes_outputs=[('cost', 'cost_2')])
            
            self.add_subsystem('obj_cmp', self.ObjCmp(), promotes_inputs=['cost_1','cost_2'],
                               promotes_outputs=['obj'])
    
    
    # build the model
    prob = om.Problem(model=MDA())
    model = prob.model
    
    model.add_design_var('x', lower=0.1, upper=1)
    model.add_design_var('y', lower=0.1, upper=1)
    model.add_objective('obj')
    
    # setup the optimization
    prob.driver = om.ScipyOptimizeDriver()
    prob.driver.options['optimizer'] = 'SLSQP'
    
    prob.setup()
    
    # run the optimization
    prob.set_val('x', .5)
    prob.set_val('y', .5)
    
    prob.run_driver()
    
    # minimum value
    print(prob.get_val('obj'))
    
    # location of the minimum
    print(prob.get_val('x'))
    print(prob.get_val('y'))