Exemplo n.º 1
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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)
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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