def continuous_adjoint( filename , partitions = 0 , compute = True , step = 1e-4 ): # Config config = SU2.io.Config(filename) config.NUMBER_PART = partitions # State state = SU2.io.State() # Force CSV output in order to compute gradients config.WRT_CSV_SOL = 'YES' # check for existing files if not compute: config.RESTART_SOL = 'YES' state.find_files(config) else: state.FILES.MESH = config.MESH_FILENAME # Direct Solution if compute: info = SU2.run.direct(config) state.update(info) SU2.io.restart2solution(config,state) # Adjoint Solution objectives = config['OBJECTIVE_FUNCTION'] objectives = objectives.split(',') n_obj = len(objectives) marker_monitoring = config['MARKER_MONITORING'] # If using chain rule update coefficients using gradients as defined in downstream_function (local file) if 'OUTFLOW_GENERALIZED' in config.OBJECTIVE_FUNCTION: import downstream_function # Must be defined in run folder chaingrad = downstream_function.downstream_gradient(config,state,step) # Set coefficients for gradients config.OBJ_CHAIN_RULE_COEFF = str(chaingrad[0:5]) # Run all-at-once if compute: info = SU2.run.adjoint(config) state.update(info) info = SU2.run.projection(config,state, step) state.update(info) return state
def continuous_adjoint( filename , partitions = 0 , compute = True , step = 1e-4 ): # Config config = SU2.io.Config(filename) config.NUMBER_PART = partitions # State state = SU2.io.State() # Force CSV output in order to compute gradients config.WRT_CSV_SOL = 'YES' # check for existing files if not compute: config.RESTART_SOL = 'YES' state.find_files(config) else: state.FILES.MESH = config.MESH_FILENAME # Direct Solution if compute: info = SU2.run.direct(config) state.update(info) SU2.io.restart2solution(config,state) # If using chain rule update coefficients using gradients as defined in downstream_function (local file) if config.OBJECTIVE_FUNCTION == 'OUTFLOW_GENERALIZED': import downstream_function # Must be defined in run folder chaingrad = downstream_function.downstream_gradient(config,state,step) # Set coefficients for gradients config.OBJ_CHAIN_RULE_COEFF = str(chaingrad[0:5]) # Adjoint Solution if compute: info = SU2.run.adjoint(config) state.update(info) #SU2.io.restart2solution(config,state) # Gradient Projection info = SU2.run.projection(config,state, step) state.update(info) return state
def continuous_adjoint( filename , partitions = 0 , compute = True , step = 1e-4 ): # Config config = SU2.io.Config(filename) config.NUMBER_PART = partitions # State state = SU2.io.State() # Force CSV output in order to compute gradients config.WRT_CSV_SOL = 'YES' # check for existing files if not compute: config.RESTART_SOL = 'YES' state.find_files(config) else: state.FILES.MESH = config.MESH_FILENAME # Direct Solution if compute: info = SU2.run.direct(config) state.update(info) SU2.io.restart2solution(config,state) # If using chain rule update coefficients using gradients as defined in downstream_function (local file) if config.OBJECTIVE_FUNCTION == 'OUTLET_CHAIN_RULE': import downstream_function # Must be defined in run folder chaingrad = downstream_function.downstream_gradient(config,state,step) # Set coefficients for gradients config.OBJ_CHAIN_RULE_COEFF = str(chaingrad) # Adjoint Solution if compute: info = SU2.run.adjoint(config) state.update(info) #SU2.io.restart2solution(config,state) # Gradient Projection info = SU2.run.projection(config,step) state.update(info) return state
def projection( config, state={}, step = 1e-3 ): """ info = SU2.run.projection(config,state,step=1e-3) Runs an gradient projection with: SU2.run.decomp() SU2.run.DOT() Assumptions: Writes tecplot file of gradients Adds objective suffix to gradient plot filename Inputs: config - an SU2 config state - only required when using external custom DV step - a float or list of floats for geometry sensitivity finite difference step Outputs: info - SU2 State with keys: GRADIENTS.<config.OBJECTIVE_FUNCTION> Updates: config.MATH_PROBLEM Executes in: ./ """ # local copy konfig = copy.deepcopy(config) # choose dv values Definition_DV = konfig['DEFINITION_DV'] n_DV = sum(Definition_DV['SIZE']) if isinstance(step,list): assert len(step) == n_DV , 'unexpected step vector length' else: step = [step]*n_DV dv_old = [0.0]*n_DV # SU2_DOT input requirement, assumes linear superposition of design variables dv_new = step konfig.unpack_dvs(dv_new,dv_old) # filenames objective = konfig['OBJECTIVE_FUNCTION'] grad_filename = konfig['GRAD_OBJFUNC_FILENAME'] output_format = konfig['OUTPUT_FORMAT'] plot_extension = su2io.get_extension(output_format) adj_suffix = su2io.get_adjointSuffix(objective) grad_plotname = os.path.splitext(grad_filename)[0] + '_' + adj_suffix + plot_extension # Run Projection SU2_DOT(konfig) # read raw gradients raw_gradients = su2io.read_gradients(grad_filename) os.remove(grad_filename) info = su2io.State() if (objective == 'OUTFLOW_GENERALIZED') and ('CUSTOM' in konfig.DV_KIND): import downstream_function # Must be defined in run folder chaingrad = downstream_function.downstream_gradient(konfig,state,step) n_dv = len(raw_gradients) custom_dv=1 for idv in range(n_dv): if (konfig.DV_KIND[idv] == 'CUSTOM'): raw_gradients[idv] = chaingrad[4+custom_dv] custom_dv = custom_dv+1 # Write Gradients data_plot = su2util.ordered_bunch() data_plot['VARIABLE'] = range(len(raw_gradients)) data_plot['GRADIENT'] = raw_gradients data_plot['FINDIFF_STEP'] = step su2util.write_plot(grad_plotname,output_format,data_plot) # gradient output dictionary gradients = { objective : raw_gradients } # info out info.GRADIENTS.update( gradients ) return info
def gradient( func_name, method, config, state=None ): """ val = SU2.eval.grad(func_name,method,config,state=None) Evaluates the aerodynamic gradients. Wraps: SU2.eval.adjoint() SU2.eval.findiff() Assumptions: Config is already setup for deformation. Mesh need not be deformed. Updates config and state by reference. Redundancy if state.GRADIENTS has the key func_name. Executes in: ./ADJOINT_* or ./FINDIFF Inputs: func_name - SU2 objective function name method - 'CONTINUOUS_ADJOINT' or 'FINDIFF' or 'DISCRETE_ADJOINT' config - an SU2 config state - optional, an SU2 state Outputs: A list of floats of gradient values """ # Initialize grads = {} state = su2io.State(state) if func_name == 'ALL': raise Exception , "func_name = 'ALL' not yet supported" # redundancy check if not state['GRADIENTS'].has_key(func_name): # Adjoint Gradients if any([method == 'CONTINUOUS_ADJOINT', method == 'DISCRETE_ADJOINT']): # If using chain rule if config.OBJECTIVE_FUNCTION == 'OUTFLOW_GENERALIZED': import downstream_function chaingrad = downstream_function.downstream_gradient(config,state) # Set coefficients for gradients config.OBJ_CHAIN_RULE_COEFF = str(chaingrad[0:5]) # Aerodynamics if func_name in su2io.optnames_aero: grads = adjoint( func_name, config, state ) # Stability elif func_name in su2io.optnames_stab: grads = stability( func_name, config, state ) # Geometry (actually a finite difference) elif func_name in su2io.optnames_geo: grads = geometry( func_name, config, state ) else: raise Exception, 'unknown function name: %s' % func_name # Finite Difference Gradients elif method == 'FINDIFF': grads = findiff( config, state ) elif method == 'DIRECTDIFF': grad = directdiff (config , state ) else: raise Exception , 'unrecognized gradient method' if ('CUSTOM' in config.DV_KIND and 'OUTFLOW_GENERALIZED' in config.OBJECTIVE_FUNCTION ): import downstream_function chaingrad = downstream_function.downstream_gradient(config,state) n_dv = len(grads[func_name]) custom_dv=1 for idv in range(n_dv): if (config.DV_KIND[idv] == 'CUSTOM'): grads['OUTFLOW_GENERALIZED'][idv] = chaingrad[4+custom_dv] custom_dv = custom_dv+1 # store state['GRADIENTS'].update(grads) # if not redundant # prepare output grads_out = state['GRADIENTS'][func_name] return copy.deepcopy(grads_out)
def findiff( config, state=None, step=1e-4 ): """ vals = SU2.eval.findiff(config,state=None,step=1e-4) Evaluates the aerodynamics gradients using finite differencing with: SU2.eval.func() SU2.run.deform() SU2.run.direct() Assumptions: Config is already setup for deformation. Mesh may or may not be deformed. Updates config and state by reference. Gradient Redundancy if state.GRADIENTS has the key func_name. Direct Redundancy if state.FUNCTIONS has key func_name. Executes in: ./FINDIFF Inputs: config - an SU2 config state - optional, an SU2 state step - finite difference step size, as a float or list of floats of length n_DV Outputs: A Bunch() with keys of objective function names and values of list of floats of gradient values """ # ---------------------------------------------------- # Initialize # ---------------------------------------------------- # initialize state = su2io.State(state) special_cases = su2io.get_specialCases(config) Definition_DV = config['DEFINITION_DV'] # console output if config.get('CONSOLE','VERBOSE') in ['QUIET','CONCISE']: log_findiff = 'log_FinDiff.out' else: log_findiff = None # ---------------------------------------------------- # Redundancy Check # ---------------------------------------------------- # master redundancy check opt_names = su2io.optnames_aero + su2io.optnames_geo findiff_todo = all( [ state.GRADIENTS.has_key(key) for key in opt_names ] ) if findiff_todo: grads = state['GRADIENTS'] return copy.deepcopy(grads) # ---------------------------------------------------- # Zero Step # ---------------------------------------------------- # run func_base = function( 'ALL', config, state ) # ---------------------------------------------------- # Plot Setup # ---------------------------------------------------- grad_filename = config['GRAD_OBJFUNC_FILENAME'] grad_filename = os.path.splitext( grad_filename )[0] output_format = config['OUTPUT_FORMAT'] plot_extension = su2io.get_extension(output_format) grad_filename = grad_filename + '_findiff' + plot_extension # ---------------------------------------------------- # Finite Difference Steps # ---------------------------------------------------- # local config konfig = copy.deepcopy(config) # check deformation setup n_dv = sum(Definition_DV['SIZE']) deform_set = konfig['DV_KIND'] == Definition_DV['KIND'] if not deform_set: dvs_base = [0.0] * n_dv konfig.unpack_dvs(dvs_base,dvs_base) else: dvs_base = konfig['DV_VALUE_NEW'] # initialize gradients func_keys = func_base.keys() func_keys = ['VARIABLE'] + func_keys + ['FINDIFF_STEP'] grads = su2util.ordered_bunch.fromkeys(func_keys) for key in grads.keys(): grads[key] = [] # step vector if isinstance(step,list): assert n_dv == len(step) , 'unexpected step vector length' else: step = [step] * n_dv # files to pull files = state['FILES'] pull = []; link = [] # files: mesh name = files['MESH'] name = su2io.expand_part(name,konfig) link.extend(name) # files: direct solution if files.has_key('DIRECT'): name = files['DIRECT'] name = su2io.expand_time(name,config) link.extend(name) # files: target equivarea distribution if 'EQUIV_AREA' in special_cases and 'TARGET_EA' in files: pull.append(files['TARGET_EA']) # files: target pressure distribution if 'INV_DESIGN_CP' in special_cases and 'TARGET_CP' in files: pull.append(files['TARGET_CP']) # files: target heat flux distribution if 'INV_DESIGN_HEATFLUX' in special_cases and 'TARGET_HEATFLUX' in files: pull.append(files['TARGET_HEATFLUX']) # Use custom variable if ('CUSTOM' in konfig.DV_KIND and 'OUTFLOW_GENERALIZED' in config.OBJECTIVE_FUNCTION): import downstream_function chaingrad = downstream_function.downstream_gradient(config,state) custom_dv=1 # output redirection with redirect_folder('FINDIFF',pull,link) as push: with redirect_output(log_findiff): # iterate each dv for i_dv in range(n_dv): this_step = step[i_dv] temp_config_name = 'config_FINDIFF_%i.cfg' % i_dv this_dvs = copy.deepcopy(dvs_base) this_konfig = copy.deepcopy(konfig) this_dvs[i_dv] = this_dvs[i_dv] + this_step this_state = su2io.State() this_state.FILES = copy.deepcopy( state.FILES ) this_konfig.unpack_dvs(this_dvs,dvs_base) this_konfig.dump(temp_config_name) # Direct Solution, findiff step func_step = function( 'ALL', this_konfig, this_state ) # remove deform step files meshfiles = this_state.FILES.MESH meshfiles = su2io.expand_part(meshfiles,this_konfig) for name in meshfiles: os.remove(name) # calc finite difference and store for key in grads.keys(): if key == 'VARIABLE': grads[key].append(i_dv) elif key == 'FINDIFF_STEP': grads[key].append(this_step) else: this_grad = ( func_step[key] - func_base[key] ) / this_step grads[key].append(this_grad) # Use custom DV if (konfig.DV_KIND[i_dv] == 'CUSTOM'): grads['OUTFLOW_GENERALIZED'][i_dv] = chaingrad[4+custom_dv] custom_dv = custom_dv+1 #: for each grad name su2util.write_plot(grad_filename,output_format,grads) os.remove(temp_config_name) #: for each dv #: with output redirection # remove plot items del grads['VARIABLE'] del grads['FINDIFF_STEP'] state.GRADIENTS.update(grads) # return results grads = copy.deepcopy(grads) return grads
def projection(config, state={}, step=1e-3): """ info = SU2.run.projection(config,state,step=1e-3) Runs an gradient projection with: SU2.run.decomp() SU2.run.DOT() Assumptions: Writes tecplot file of gradients Adds objective suffix to gradient plot filename Inputs: config - an SU2 config state - only required when using external custom DV step - a float or list of floats for geometry sensitivity finite difference step Outputs: info - SU2 State with keys: GRADIENTS.<config.OBJECTIVE_FUNCTION> Updates: config.MATH_PROBLEM Executes in: ./ """ # local copy konfig = copy.deepcopy(config) # choose dv values Definition_DV = konfig['DEFINITION_DV'] n_DV = sum(Definition_DV['SIZE']) if isinstance(step, list): assert len(step) == n_DV, 'unexpected step vector length' else: step = [step] * n_DV dv_old = [ 0.0 ] * n_DV # SU2_DOT input requirement, assumes linear superposition of design variables dv_new = step konfig.unpack_dvs(dv_new, dv_old) # filenames objective = konfig['OBJECTIVE_FUNCTION'] grad_filename = konfig['GRAD_OBJFUNC_FILENAME'] output_format = konfig['OUTPUT_FORMAT'] plot_extension = su2io.get_extension(output_format) adj_suffix = su2io.get_adjointSuffix(objective) grad_plotname = os.path.splitext( grad_filename)[0] + '_' + adj_suffix + plot_extension # Run Projection SU2_DOT(konfig) # read raw gradients raw_gradients = su2io.read_gradients(grad_filename) os.remove(grad_filename) info = su2io.State() if (objective == 'OUTFLOW_GENERALIZED') and ('CUSTOM' in konfig.DV_KIND): import downstream_function # Must be defined in run folder chaingrad = downstream_function.downstream_gradient( konfig, state, step) n_dv = len(raw_gradients) custom_dv = 1 for idv in range(n_dv): if (konfig.DV_KIND[idv] == 'CUSTOM'): raw_gradients[idv] = chaingrad[4 + custom_dv] custom_dv = custom_dv + 1 # Write Gradients data_plot = su2util.ordered_bunch() data_plot['VARIABLE'] = range(len(raw_gradients)) data_plot['GRADIENT'] = raw_gradients data_plot['FINDIFF_STEP'] = step su2util.write_plot(grad_plotname, output_format, data_plot) # gradient output dictionary gradients = {objective: raw_gradients} # info out info.GRADIENTS.update(gradients) return info
def gradient(func_name, method, config, state=None): """ val = SU2.eval.grad(func_name,method,config,state=None) Evaluates the aerodynamic gradients. Wraps: SU2.eval.adjoint() SU2.eval.findiff() Assumptions: Config is already setup for deformation. Mesh need not be deformed. Updates config and state by reference. Redundancy if state.GRADIENTS has the key func_name. Executes in: ./ADJOINT_* or ./FINDIFF Inputs: func_name - SU2 objective function name method - 'CONTINUOUS_ADJOINT' or 'FINDIFF' or 'DISCRETE_ADJOINT' config - an SU2 config state - optional, an SU2 state Outputs: A list of floats of gradient values """ # Initialize grads = {} state = su2io.State(state) if func_name == 'ALL': raise Exception, "func_name = 'ALL' not yet supported" # redundancy check if not state['GRADIENTS'].has_key(func_name): # Adjoint Gradients if any([method == 'CONTINUOUS_ADJOINT', method == 'DISCRETE_ADJOINT']): # If using chain rule if config.OBJECTIVE_FUNCTION == 'OUTFLOW_GENERALIZED': import downstream_function chaingrad = downstream_function.downstream_gradient( config, state) # Set coefficients for gradients config.OBJ_CHAIN_RULE_COEFF = str(chaingrad[0:5]) # Aerodynamics if func_name in su2io.optnames_aero: grads = adjoint(func_name, config, state) # Stability elif func_name in su2io.optnames_stab: grads = stability(func_name, config, state) # Geometry (actually a finite difference) elif func_name in su2io.optnames_geo: grads = geometry(func_name, config, state) else: raise Exception, 'unknown function name: %s' % func_name # Finite Difference Gradients elif method == 'FINDIFF': grads = findiff(config, state) elif method == 'DIRECTDIFF': grad = directdiff(config, state) else: raise Exception, 'unrecognized gradient method' if ('CUSTOM' in config.DV_KIND and 'OUTFLOW_GENERALIZED' in config.OBJECTIVE_FUNCTION): import downstream_function chaingrad = downstream_function.downstream_gradient(config, state) n_dv = len(grads[func_name]) custom_dv = 1 for idv in range(n_dv): if (config.DV_KIND[idv] == 'CUSTOM'): grads['OUTFLOW_GENERALIZED'][idv] = chaingrad[4 + custom_dv] custom_dv = custom_dv + 1 # store state['GRADIENTS'].update(grads) # if not redundant # prepare output grads_out = state['GRADIENTS'][func_name] return copy.deepcopy(grads_out)
def findiff(config, state=None, step=1e-4): """ vals = SU2.eval.findiff(config,state=None,step=1e-4) Evaluates the aerodynamics gradients using finite differencing with: SU2.eval.func() SU2.run.deform() SU2.run.direct() Assumptions: Config is already setup for deformation. Mesh may or may not be deformed. Updates config and state by reference. Gradient Redundancy if state.GRADIENTS has the key func_name. Direct Redundancy if state.FUNCTIONS has key func_name. Executes in: ./FINDIFF Inputs: config - an SU2 config state - optional, an SU2 state step - finite difference step size, as a float or list of floats of length n_DV Outputs: A Bunch() with keys of objective function names and values of list of floats of gradient values """ # ---------------------------------------------------- # Initialize # ---------------------------------------------------- # initialize state = su2io.State(state) special_cases = su2io.get_specialCases(config) Definition_DV = config['DEFINITION_DV'] # console output if config.get('CONSOLE', 'VERBOSE') in ['QUIET', 'CONCISE']: log_findiff = 'log_FinDiff.out' else: log_findiff = None # ---------------------------------------------------- # Redundancy Check # ---------------------------------------------------- # master redundancy check opt_names = su2io.optnames_aero + su2io.optnames_geo findiff_todo = all([state.GRADIENTS.has_key(key) for key in opt_names]) if findiff_todo: grads = state['GRADIENTS'] return copy.deepcopy(grads) # ---------------------------------------------------- # Zero Step # ---------------------------------------------------- # run func_base = function('ALL', config, state) # ---------------------------------------------------- # Plot Setup # ---------------------------------------------------- grad_filename = config['GRAD_OBJFUNC_FILENAME'] grad_filename = os.path.splitext(grad_filename)[0] output_format = config['OUTPUT_FORMAT'] plot_extension = su2io.get_extension(output_format) grad_filename = grad_filename + '_findiff' + plot_extension # ---------------------------------------------------- # Finite Difference Steps # ---------------------------------------------------- # local config konfig = copy.deepcopy(config) # check deformation setup n_dv = sum(Definition_DV['SIZE']) deform_set = konfig['DV_KIND'] == Definition_DV['KIND'] if not deform_set: dvs_base = [0.0] * n_dv konfig.unpack_dvs(dvs_base, dvs_base) else: dvs_base = konfig['DV_VALUE_NEW'] # initialize gradients func_keys = func_base.keys() func_keys = ['VARIABLE'] + func_keys + ['FINDIFF_STEP'] grads = su2util.ordered_bunch.fromkeys(func_keys) for key in grads.keys(): grads[key] = [] # step vector if isinstance(step, list): assert n_dv == len(step), 'unexpected step vector length' else: step = [step] * n_dv # files to pull files = state['FILES'] pull = [] link = [] # files: mesh name = files['MESH'] name = su2io.expand_part(name, konfig) link.extend(name) # files: direct solution if files.has_key('DIRECT'): name = files['DIRECT'] name = su2io.expand_time(name, config) link.extend(name) # files: target equivarea distribution if 'EQUIV_AREA' in special_cases and 'TARGET_EA' in files: pull.append(files['TARGET_EA']) # files: target pressure distribution if 'INV_DESIGN_CP' in special_cases and 'TARGET_CP' in files: pull.append(files['TARGET_CP']) # files: target heat flux distribution if 'INV_DESIGN_HEATFLUX' in special_cases and 'TARGET_HEATFLUX' in files: pull.append(files['TARGET_HEATFLUX']) # Use custom variable if ('CUSTOM' in konfig.DV_KIND and 'OUTFLOW_GENERALIZED' in config.OBJECTIVE_FUNCTION): import downstream_function chaingrad = downstream_function.downstream_gradient(config, state) custom_dv = 1 # output redirection with redirect_folder('FINDIFF', pull, link) as push: with redirect_output(log_findiff): # iterate each dv for i_dv in range(n_dv): this_step = step[i_dv] temp_config_name = 'config_FINDIFF_%i.cfg' % i_dv this_dvs = copy.deepcopy(dvs_base) this_konfig = copy.deepcopy(konfig) this_dvs[i_dv] = this_dvs[i_dv] + this_step this_state = su2io.State() this_state.FILES = copy.deepcopy(state.FILES) this_konfig.unpack_dvs(this_dvs, dvs_base) this_konfig.dump(temp_config_name) # Direct Solution, findiff step func_step = function('ALL', this_konfig, this_state) # remove deform step files meshfiles = this_state.FILES.MESH meshfiles = su2io.expand_part(meshfiles, this_konfig) for name in meshfiles: os.remove(name) # calc finite difference and store for key in grads.keys(): if key == 'VARIABLE': grads[key].append(i_dv) elif key == 'FINDIFF_STEP': grads[key].append(this_step) else: this_grad = (func_step[key] - func_base[key]) / this_step grads[key].append(this_grad) # Use custom DV if (konfig.DV_KIND[i_dv] == 'CUSTOM'): grads['OUTFLOW_GENERALIZED'][i_dv] = chaingrad[4 + custom_dv] custom_dv = custom_dv + 1 #: for each grad name su2util.write_plot(grad_filename, output_format, grads) os.remove(temp_config_name) #: for each dv #: with output redirection # remove plot items del grads['VARIABLE'] del grads['FINDIFF_STEP'] state.GRADIENTS.update(grads) # return results grads = copy.deepcopy(grads) return grads