0

We are interested in using a surrogate model in an aircraft design process implemented in OpenMDAO. Basically we want to use an aerodynamic code (such as VSPaero in our aim) to produce a database (using a DOE ) and then built a surrogate that will be used in the design process. It looks like your proposal 2) in use of MOE in openMDAO and we also want to access to the "gradient" information of the surrogate to be used in the full design problem .
We started from the code you have provided in nested problem question and try to built a mock up case with simplified component for aerodynamic . The example code is below (using kriging) and we have two concerns to finish it:

  • we need to implement a "linearize" function in our component if we want to use surrogate gradient information: I guess we should use the "calc_gradient" function of problem to do this . Is it right ?
  • in our example code, the training will be done each time we call the component what is not very efficient : is there a way to call it only once or to do the surrogate training only after the setup() of the bigger problem (aircraft design in our case )?

Here is the code (sorry it is a bit long):

from openmdao.api import IndepVarComp, Group, Problem, ScipyOptimizer, ExecComp, DumpRecorder, Component, NLGaussSeidel,ScipyGMRES, Newton,SqliteRecorder,MetaModel, \
    KrigingSurrogate, FloatKrigingSurrogate
from openmdao.drivers.latinhypercube_driver import LatinHypercubeDriver, OptimizedLatinHypercubeDriver

from openmdao.solvers.solver_base import NonLinearSolver
import numpy as np
import sys

alpha_test = np.array([0.56, 0.24, 0.30, 0.32, 0.20])
eta_test = np.array([-0.30, -0.14, -0.19, -0.18, -0.12])

num_elem = len(alpha_test)    

class SysAeroSurrogate(Component):
    """ Simulates the presence of an aero surrogate mode using linear aerodynamic model """
    """ coming from pymission code """
    """ https://github.com/OpenMDAO-Plugins/pyMission/blob/master/src/pyMission/aerodynamics.py """    

    def __init__(self, num_elem=1):
        super(SysAeroSurrogate, self).__init__()

        self.add_param('alpha', 0.5)
        self.add_param('eta', -0.33)
        self.add_param('AR', 0.0)
        self.add_param('oswald', 0.0)

        self.add_output('CL',  val=0.0)
        self.add_output('CD',  val=0.0) ## Drag Coefficient        

    def solve_nonlinear(self, params, unknowns, resids):
        """ Compute lift and drag coefficient using angle of attack and tail
        rotation angles. Linear aerodynamics is assumed."""

        alpha = params['alpha']
        eta = params['eta']
        aspect_ratio = params['AR']
        oswald = params['oswald']

        lift_c0 = 0.30
        lift_ca = 6.00
        lift_ce = 0.27
        drag_c0 = 0.015      

        unknowns['CL'] = lift_c0 + lift_ca*alpha*1e-1 + lift_ce*eta*1e-1
        unknowns['CD'] = (drag_c0 + (unknowns['CL'])**2 /(np.pi * aspect_ratio * oswald))/1e-1


class SuroMM(Group):
    def __init__(self):
        super(SuroMM, self).__init__()

        #kriging
        AeroMM = self.add("AeroMM", MetaModel())
        AeroMM.add_param('alpha', val=0.)
        AeroMM.add_param('eta', val=0.)                  
        AeroMM.add_output('CL_MM', val=0., surrogate=FloatKrigingSurrogate())
        AeroMM.add_output('CD_MM', val=0., surrogate=FloatKrigingSurrogate())      


class SurrogateAero(Component):

    def __init__(self):
        super(SurrogateAero, self).__init__()

        ## Inputs to this subprob

        self.add_param('alpha', val=0.5*np.ones(num_elem)) ## Angle of attack
        self.add_param('eta', val=0.5*np.ones(num_elem)) ## Tail rotation angle    
        self.add_param('AR', 0.0)
        self.add_param('oswald', 0.0)           

        ## Unknowns for this sub prob
        self.add_output('CD',  val=np.zeros(num_elem))
        self.add_output('CL',  val=np.zeros(num_elem))  

        #####
        self.problem = prob = Problem()
        prob.root = Group()

        prob.root.add('d1', SuroMM(), promotes=['*'])
        prob.setup()

        #### training of metamodel        
        prob['AeroMM.train:alpha'] = DOEX1
        prob['AeroMM.train:eta'] = DOEX2               
        prob['AeroMM.train:CL_MM'] = DOEY1
        prob['AeroMM.train:CD_MM'] =DOEY2

    def solve_nonlinear(self, params, unknowns, resids):

        CL_temp=np.zeros(num_elem)
        CD_temp=np.zeros(num_elem)        

        prob = self.problem

        # Pass values into our problem
        for i in range(len(params['alpha'])):
            prob['AeroMM.alpha'] = params['alpha'][i]
            prob['AeroMM.eta'] = params['eta'][i]

            # Run problem
            prob.run()

            CL_temp[i] = prob['AeroMM.CL_MM']
            CD_temp[i] = prob['AeroMM.CD_MM']        

        # Pull values from problem
        unknowns['CL'] =  CL_temp
        unknowns['CD'] =  CD_temp


if __name__ == "__main__":

###### creation of database with DOE #####
    top = Problem()
    root = top.root = Group()

    root.add('comp', SysAeroSurrogate(), promotes=['*'])
    root.add('p1', IndepVarComp('alpha', val=0.50), promotes=['*'])
    root.add('p2', IndepVarComp('eta',val=0.50), promotes=['*'])
    root.add('p3', IndepVarComp('AR', 10.), promotes=['*'])
    root.add('p4', IndepVarComp('oswald',  0.92), promotes=['*'])     

    top.driver = OptimizedLatinHypercubeDriver(num_samples=16, seed=0, population=20, generations=4, norm_method=2)
    top.driver.add_desvar('alpha', lower=-5.0*(np.pi/180.0)*1e-1, upper=15.0*(np.pi/180.0)*1e-1)
    top.driver.add_desvar('eta', lower=-5.0*(np.pi/180.0)*1e-1, upper=15.0*(np.pi/180.0)*1e-1)

    top.driver.add_objective('CD')

    recorder = SqliteRecorder('Aero')
    recorder.options['record_params'] = True
    recorder.options['record_unknowns'] = True
    recorder.options['record_resids'] = False
    recorder.options['record_metadata'] = False
    top.driver.add_recorder(recorder)

    top.setup()
    top.run()

    import sqlitedict
    db = sqlitedict.SqliteDict( 'Aero', 'openmdao' )
    print( list( db.keys() ) )
    DOEX1 = []
    DOEX2 = []
    DOEY1 = []
    DOEY2 = []
    for i in list(db.keys()):
            data = db[i]
            p = data['Parameters']
            DOEX1.append(p['comp.alpha'])
            DOEX2.append(p['comp.eta'])

            p = data['Unknowns']
            DOEY1.append(p['CL'])
            DOEY2.append(p['CD'])

    ################  use of surrogate model ######

    prob2 = Problem(root=Group())
    prob2.root.add('SurrAero', SurrogateAero(), promotes=['*'])

    prob2.root.add('v1', IndepVarComp('alpha', val=alpha_test), promotes=['*'])
    prob2.root.add('v2', IndepVarComp('eta',val=eta_test), promotes=['*'])

    prob2.setup()

    prob2.run()

    print'CL predicted:', prob2['CL']
    print'CD predicted:', prob2['CD']
Community
  • 1
  • 1
RicONERA
  • 1
  • 1
  • Is the generation of the aero data, for training the surrogate, a one time process. In other words, do you generate it once and then optimize on the surrogate model using a gradient based method? Or do you want to change the training data to the surrogate as part of the optimization loop? – Justin Gray Apr 21 '16 at 17:11

1 Answers1

0

The way you have your model set up seems correct. The MetaModel component will only train its data one time (the first pass through the model), as you can see in this part of the source code. Every subsequent iteration, it just uses the trained surrogate thats already there.

The meta-model is also already setup to provide analytic derivatives of the predicted output with respect to the input independent variables. Derivatives of the prediction with respect to the training point values are not available in the base implementation. That requires a more complex setup that, at least for the moment, will require some custom setup that is not in the standard library.

Justin Gray
  • 5,605
  • 1
  • 11
  • 16