Exemple #1
0
def PowerCurveParametricExample1():
    """ Example to run a set of FAST simulations to determine a power curve.
    In this example, the WS, RPM and Pitch are set within a for loop.
    If the controller and generator are active, these are just "initial conditions".
    Additional parameters may be set by adjusting the BaseDict.

    This script is based on a reference directory which contains a reference main input file (.fst)
    Everything is copied to a working directory.
    The different fast inputs are generated based on a list of dictionaries, named `PARAMS`.
    For each dictionary:
       - they keys are "path" to a input parameter, e.g. `EDFile|RotSpeed`  or `TMax`.
           These should correspond to whater name of the variable is used in the FAST inputs files.
       - they values are the values corresponding to this parameter
    """
    # --- Parameters for this script
    FAST_EXE  = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll)
    ref_dir   = os.path.join(MyDir, '../../../data/NREL5MW/')     # Folder where the fast input files are located (will be copied)
    main_file = 'Main_Onshore_OF2.fst'               # Main file in ref_dir, used as a template
    work_dir  = '_NREL5MW_PowerCurveParametric/'     # Output folder (will be created)

    # --- Defining the parametric study  (list of dictionnaries with keys as FAST parameters)
    WS    = [3,5,7,9 ,11,13,15]
    RPM   = [5,6,7,10,10,10,10] # initial conditions
    PITCH = [0,0,0,0 ,5 ,10,15] # initial conditions
    BaseDict = {'TMax': 100, 'DT': 0.01, 'DT_Out': 0.1}
    #BaseDict = case_gen.paramsNoController(BaseDict)
    #BaseDict = case_gen.paramsStiff(BaseDict)
    #BaseDict = case_gen.paramsNoGen(BaseDict)
    PARAMS=[]
    for wsp,rpm,pitch in zip(WS,RPM,PITCH): # NOTE: same length of WS and RPM otherwise do multiple for loops
        p=BaseDict.copy()
        p['EDFile|RotSpeed']       = rpm
        p['EDFile|BlPitch(1)']     = pitch
        p['EDFile|BlPitch(2)']     = pitch
        p['EDFile|BlPitch(3)']     = pitch
        p['InflowFile|HWindSpeed'] = wsp
        p['InflowFile|WindType']   = 1 # Setting steady wind
        p['__name__']              = 'ws{:04.1f}'.format(p['InflowFile|HWindSpeed'])
        PARAMS.append(p)

    # --- Generating all files in a workdir
    fastFiles=case_gen.templateReplace(PARAMS, ref_dir, work_dir, removeRefSubFiles=True, main_file=main_file)
    print(fastFiles)

    # --- Creating a batch script just in case
    runner.writeBatch(os.path.join(work_dir,'_RUN_ALL.bat'), fastFiles,fastExe=FAST_EXE)
    # --- Running the simulations
    print('>>> Running {} simulations in {} ...'.format(len(fastFiles), work_dir))
    runner.run_fastfiles(fastFiles, fastExe=FAST_EXE, parallel=True, showOutputs=False, nCores=2)

    # --- Simple Postprocessing
    outFiles = [os.path.splitext(f)[0]+'.outb' for f in fastFiles]

    avg_results = postpro.averagePostPro(outFiles,avgMethod='constantwindow',avgParam=10, ColMap = {'WS_[m/s]':'Wind1VelX_[m/s]'},ColSort='WS_[m/s]')
    print('>>> Average results:')
    print(avg_results)
    avg_results.to_csv('_PowerCurve1.csv',sep='\t',index=False)
def main():
    # --- Main Parameters
    ref_dir = os.path.join(
        MyDir, '../../../data/NREL5MW/'
    )  # Folder where the fast input files are located (will be copied)
    FAST_EXE = os.path.join(
        MyDir,
        '../../../data/openfast.exe')  # Location of a FAST exe (and dll)
    main_file = 'Main_Onshore_OF2.fst'  # Main file in ref_dir, used as a template
    work_dir = '_NREL5MW_ParametricExcel/'  # Output folder (will be created)
    parametricFile = 'ParametricExcel.xlsx'  # Excel file containing set of parameters

    # --- Reading Excel file, converting it to a list of dictionaries, and generate input files
    dfs = io.excel_file.ExcelFile(parametricFile).toDataFrame()
    df = dfs[list(dfs.keys())[0]]
    PARAMS = df.to_dict('records')
    print(df)
    fastFiles = case_gen.templateReplace(PARAMS,
                                         ref_dir,
                                         outputDir=work_dir,
                                         removeRefSubFiles=True,
                                         removeAllowed=False,
                                         main_file=main_file)

    # --- Running fast simulations
    print('>>> Running {} simulations in {} ...'.format(
        len(fastFiles), work_dir))
    runner.writeBatch(os.path.join(work_dir, '_RUN_ALL.bat'),
                      fastFiles,
                      fastExe=FAST_EXE)
    runner.run_fastfiles(fastFiles,
                         showOutputs=False,
                         fastExe=FAST_EXE,
                         nCores=4)

    # --- Postpro - Computing averages at the end of the simluation
    print('>>> Postprocessing...')
    outFiles = [os.path.splitext(f)[0] + '.outb' for f in fastFiles]
    ColKeepStats = [
        'RotSpeed_[rpm]', 'BldPitch1_[deg]', 'RtAeroCp_[-]', 'RtAeroCt_[-]',
        'Wind1VelX_[m/s]'
    ]
    result = postpro.averagePostPro(outFiles,
                                    avgMethod='constantwindow',
                                    avgParam=5,
                                    ColKeep=ColKeepStats,
                                    ColSort='RotSpeed_[rpm]')
    result.to_csv('ParametricExcel_Summary.csv', sep='\t', index=False)
    print('Average values saved to _ParametricExcel_Summary.csv')
Exemple #3
0
def PowerCurveParametricExample2():
    """ Example to run a set of FAST simulations to determine a power curve.
    In this example, the WS, RPM and Pitch are set within a for loop.
    If the controller and generator are active, these are just "initial conditions".
    Additional parameters may be set by adjusting the BaseDict.

    This script is based on a reference directory which contains a reference main input file (.fst)
    Everything is copied to a working directory.
    The different fast inputs are generated based on a list of dictionaries, named `PARAMS`.
    For each dictionary:
       - they keys are "path" to a input parameter, e.g. `EDFile|RotSpeed`  or `TMax`.
           These should correspond to whater name of the variable is used in the FAST inputs files.
       - they values are the values corresponding to this parameter
    """
    # --- Parameters for this script
    FAST_EXE  = os.path.join(MyDir, '../../../data/openfast.exe') # Location of a FAST exe (and dll)
    ref_dir   = os.path.join(MyDir, '../../../data/NREL5MW/')     # Folder where the fast input files are located (will be copied)
    main_file = 'Main_Onshore_OF2.fst'                # Main file in ref_dir, used as a template
    work_dir  = '_NREL5MW_PowerCurveParametric2/'     # Output folder (will be created)
    out_Ext   = '.outb' # Output extension

    # --- Defining the parametric study  (list of dictionnaries with keys as FAST parameters)
    WS    = [3,5,7,9 ,11,13,15]
    RPM   = [5,6,7,10,10,10,10] 
    PITCH = [0,0,0,0 ,5 ,10,15] 
    BaseDict = {'TMax': 10, 'DT': 0.01, 'DT_Out': 0.1}
    PARAMS = case_gen.paramsWS_RPM_Pitch(WS, RPM, PITCH, baseDict=BaseDict, flatInputs=True)

    # --- Generating all files in a workdir
    fastFiles = case_gen.templateReplace(PARAMS, ref_dir, work_dir, removeRefSubFiles=True, removeAllowed=True, main_file=main_file)

    # --- Creating a batch script just in case
    runner.writeBatch(os.path.join(work_dir,'_RUN_ALL.bat'), fastFiles,fastExe=FAST_EXE)

    # --- Running the simulations
    runner.run_fastfiles(fastFiles, fastExe=FAST_EXE, parallel=True, showOutputs=False, nCores=2)

    # --- Simple Postprocessing
    outFiles = [os.path.splitext(f)[0]+out_Ext for f in fastFiles]
    avg_results = postpro.averagePostPro(outFiles,avgMethod='constantwindow',avgParam=10, ColMap = {'WS_[m/s]':'Wind1VelX_[m/s]'},ColSort='WS_[m/s]')
    print('>>> Average results:')
    print(avg_results)
    avg_results.to_csv('_PowerCurve2.csv',sep='\t',index=False)
def run_linearization():
    """ Example to run a set of OpenFAST simulations (linearizations)

    This script uses a reference directory (`ref_dir`) which contains a reference input file (.fst)
    1) The reference directory is copied to a working directory (`out_dir`).
    2) All the fast input files are generated in this directory based on a list of dictionaries (`PARAMS`).
    For each dictionary in this list:
       - The keys are "path" to a input parameter, e.g. `EDFile|RotSpeed`  or `FAST|TMax`.
         These should correspond to the variables used in the FAST inputs files.
       - The values are the values corresponding to this parameter
    For instance:
         PARAMS[0]['DT']                    = 0.01
         PARAMS[0]['EDFile|RotSpeed']       = 5
         PARAMS[0]['InflowFile|HWindSpeed'] = 10

    3) The simulations are run, successively distributed on `nCores` CPUs.
    4) The output files are read, and averaged based on a method (e.g. average over a set of periods,
        see averagePostPro in postpro for the different averaging methods).
       A pandas DataFrame is returned

    """
    # --- Parameters for this script
    of_dir           = '/home/equon/WEIS/local'
    this_dir         = os.path.dirname(__file__)
    ref_dir          = '../OpenFAST'   # Folder where the fast input files are located (will be copied)
    out_dir          = os.path.join(this_dir,'outputs')     # Output folder (will be created)
    main_file        = 'NREL-1p7-103.fst'  # Main file in ref_dir, used as a template
    FAST_EXE         = os.path.join(of_dir,'bin/openfast') # Location of a FAST exe (and dll)

    # --- Read outputs from OF power curve to get operating points (EWQ)
    #op = pd.read_csv('../NREL-1.7-103_openfast.csv').set_index('Wind1VelX_[m/s]')
    op = pd.read_csv('../NREL-1.7-103_openfast.csv').set_index('rotor speed [RPM]')
    op = op.loc[op['Wind1VelX_[m/s]'] <= 11]
    GenTorque = op['generator torque [kN-m]'] * 1000.
    minRotSpeed = op.index[0]

    # --- Defining the parametric study  (list of dictionnaries with keys as FAST parameters)
    LinStart = 600.0
    BaseDict = {'TMax': 660.0}
    BaseDict = case_gen.paramsLinearTrim(BaseDict)   # Run linear trim case
    print(BaseDict)

    RotSpeeds = np.arange(0,17)

    PARAMS=[]
    for i,rpm in enumerate(RotSpeeds): 
        p=BaseDict.copy()

        GenTq = np.interp(rpm, GenTorque.index, GenTorque, left=1e-9)

        # Turn off aero
        p['CompAero']   = 0
        p['CompInflow'] = 0
        
        # Turn on all pertinent structural DOFs
        p['EDFile|FlapDOF1'] = 'True'
        p['EDFile|FlapDOF2'] = 'True'
        p['EDFile|EdgeDOF'] = 'True'
        p['EDFile|DrTrDOF'] = 'True'
        p['EDFile|GenDOF'] = 'False' #'True'
        p['EDFile|TwFADOF1'] = 'True'
        p['EDFile|TwFADOF2'] = 'True'
        p['EDFile|TwSSDOF1'] = 'True'
        p['EDFile|TwSSDOF2'] = 'True'

        # rated generator speed, torque, control params
        #p['ServoFile|VS_RtGnSp'] = 1429.36707 * .9 # Rated generator speed [RPM]
        #p['ServoFile|VS_RtTq'] = 12389.06909 # Rated torque, from DISCON.IN [Nm]
        #p['ServoFile|VS_Rgn2K'] = 0.006851918024 # Generator torque const, from DISCON.IN [Nm/RPM^2]
        #p['ServoFile|VS_SlPc']   = 10.

        # const generator torque
        # https://github.com/OpenFAST/openfast/issues/478#issuecomment-649819664
        #p['ServoFile|VS_RtTq'] = np.interp(rpm, GenTorque.index, GenTorque, left=1e-9)
        p['ServoFile|VS_RtTq'] = GenTq
        p['ServoFile|VS_RtGnSp'] = 1e-9
        p['ServoFile|VS_Rgn2K'] = 1e-9
        p['ServoFile|VS_SlPc'] = 1e-9

        # Operating conditions
        p['EDFile|RotSpeed'] = rpm
        p['EDFile|BlPitch(1)'] = 0.0
        p['EDFile|BlPitch(2)'] = 0.0
        p['EDFile|BlPitch(3)'] = 0.0

        # Set number of linearizations
        #p['TrimCase'] = 2
        p['CalcSteady'] = 'False'
        #p['OutFmt'] = 'E20.12'
        if rpm > 0:
            p['NLinTimes'] = 36
            LinTimes = np.linspace(LinStart, LinStart+60./rpm, num=p['NLinTimes'], endpoint=False)
            p['LinTimes'] = np.array_str(LinTimes, max_line_width=9000, precision=3)[1:-1]
        else:
            p['NLinTimes'] = 1
            p['LinTimes'] = f'{LinStart:.3f}'
            p['TMax'] = LinStart

        # Set case name
        p['__name__'] = f'{i:03d}_{rpm:.1f}rpm'
        PARAMS.append(p)

    # --- Generating all files in a output directory
    fastfiles=case_gen.templateReplace(PARAMS,ref_dir,outputDir=out_dir,removeRefSubFiles=True,main_file=main_file, oneSimPerDir=False)
    print(fastfiles)

    # --- Creating a batch script just in case
    #runner.writeBatch(os.path.join(out_dir,'_RUN_ALL.bat'),fastfiles,fastExe=FAST_EXE)

    # --- Running the simulations
    if parallel_flag:
        nCores = min(len(RotSpeeds), os.cpu_count())
        runner.run_fastfiles(fastfiles,fastExe=FAST_EXE,parallel=True,showOutputs=True,nCores=nCores)
    else:
        #runner.run_fastfiles(fastfiles,fastExe=FAST_EXE,parallel=False,showOutputs=True) # causes error when calling wait()
        runner.run_fastfiles(fastfiles,fastExe=FAST_EXE,parallel=True,showOutputs=True,nCores=1)

    # --- Simple Postprocessing
    # (averaging each signal over the last period for each simulation)
    outFiles = [os.path.splitext(f)[0]+'.outb' for f in fastfiles]
    # avg_results = postpro.averagePostPro(outFiles, avgMethod='periods', avgParam=1, ColMap = {'WS_[m/s]':'Wind1VelX_[m/s]'},ColSort='WS_[m/s]')
    # avg_results.drop('Time_[s]',axis=1, inplace=True)

    return outFiles
def writeLinearizationFiles(main_fst,
                            workDir,
                            operatingPointsFile,
                            nPerPeriod=36,
                            baseDict=None,
                            tStart=100,
                            LinInputs=0,
                            LinOutputs=0):
    """
    Write FAST inputs files for linearization, to a given directory `workDir`.


    INPUTS:
      - main_fst: path to an existing  .fst file
                  This file (and the ones it refers to) will be used as templates.
                  Values of the templates can be modified using `baseDict`.
                  The parent directory of the main fst file will be copied to `workDir`.
      - workDir: directory (will be created) where the simulation files will be generated

      - operatingPointsFile: csv file containing operating conditions
      - nPerPeriod : number of linearization points per rotation (usually 12 or 36)
      - baseDict : a dictionary of inputs files keys to be applied to all simulations
                   Ignored if not provided.
                   e.g. baseDict={'DT':0.01, 'EDFile|ShftTilt':-5, 'InflowFile|PLexp':0.0}
                   see templateReplaceGeneral.
      - tStart: time at which the linearization will start. 
                When triming option is not available, this needs to be sufficiently large for
                the rotor to reach an equilibrium
      - LinInputs:  linearize wrt. inputs (see OpenFAST documentation). {0,1,2, default:1}
      - LinOutputs: linearize wrt. outputs (see OpenFAST documentation). {0,1, default:0}

    OUTPUTS:
       - list of fst files created
    """
    # --- Optional values
    if baseDict is None:
        baseDict = dict()

    # --- Checking main fst file
    fst = FASTInputFile(main_fst)
    hasTrim = 'TrimCase' in fst.keys()
    if fst['CompServo'] == 1:
        print('[WARN] For now linearization is done without controller.')
        baseDict['CompServo'] = 0

    # --- Reading operating points
    OP = readOperatingPoints(operatingPointsFile)

    # --- Generating list of parameters that vary based on the operating conditions provided
    PARAMS = []
    for i, op in OP.iterrows():
        # Extract operating conditions (TODO, handling of missing fields)
        ws = op['WindSpeed_[m/s]']
        rpm = op['RotorSpeed_[rpm]']
        pitch = op['PitchAngle_[deg]']
        filename = op['Filename_[-]']
        # TODO gen trq or Tower top displacement

        # Determine linearization times based on RPM and nPerPeriod
        Omega = rpm / 60 * 2 * np.pi
        if abs(Omega) < 0.001:
            LinTimes = [tStart]
            Tmax = tStart + 1
        else:
            T = 2 * np.pi / Omega
            LinTimes = np.linspace(tStart, tStart + T, nPerPeriod + 1)[:-1]
            Tmax = tStart + 1.01 * T
        # --- Creating "linDict", dictionary of changes to fast input files for linearization
        linDict = dict()
        linDict['__name__'] = os.path.splitext(filename)[0]
        # --- Main fst options
        linDict['TMax'] = Tmax
        linDict['TStart'] = 0
        if abs(ws) < 0.001:
            linDict['CompAero'] = 0
            linDict['CompInflow'] = 0
        # --- Linearization options
        linDict['Linearize'] = True
        linDict['NLinTimes'] = len(LinTimes)
        linDict['LinTimes'] = list(LinTimes)
        linDict['OutFmt'] = '"ES20.12E3"'  # Important for decent resolution
        linDict[
            'LinInputs'] = LinInputs  # 0: none, 1: standard, 2: to get full linearizations
        linDict['LinOutputs'] = LinOutputs  # 0: none, 1: based on outlist
        # --- New Linearization options
        # TrimCase - Controller parameter to be trimmed {1:yaw; 2:torque; 3:pitch} [used only when CalcSteady=True]
        # TrimTol - Tolerance for the rotational speed convergence [>eps] [used only when CalcSteady=True]
        # TrimGain - Proportional gain for the rotational speed error (rad/(rad/s) or Nm/(rad/s)) [>0] [used only when CalcSteady=True]
        # Twr_Kdmp - Damping factor for the tower (N/(m/s)) [>=0] [used only when CalcSteady=True]
        # Bld_Kdmp - Damping factor for the blade (N/(m/s)) [>=0] [used only when CalcSteady=True]
        # --- Mode shape vizualization options
        if hasTrim:
            linDict['WrVTK'] = 3
            linDict['VTK_type'] = 1
            linDict['VTK_fields'] = True
            linDict['VTK_fps'] = 30
        else:
            linDict['WrVTK'] = 0
        # --- Aero options
        linDict['AeroFile|WakeMod'] = 1  # Needed for linearization
        linDict['AeroFile|AFAeroMod'] = 1  # Needed for linearization
        linDict['AeroFile|FrozenWake'] = True  # Needed for linearization
        # --- Inflow options
        linDict['InflowFile|WindType'] = 1
        linDict['InflowFile|HWindSpeed'] = ws
        # --- ElastoDyn options
        linDict['EDFile|BlPitch(1)'] = pitch
        linDict['EDFile|BlPitch(2)'] = pitch
        linDict['EDFile|BlPitch(3)'] = pitch
        linDict['EDFile|RotSpeed'] = rpm
        #linDict['EDFile|TTDspFA']    = tt
        # --- Servo options

        # --- Merging linDict dictionary with user override inputs
        for k, v in baseDict.items():
            if k in linDict and v != linDict[k]:
                print('Overriding key {} with value {} (previous value {})'.
                      format(k, v, linDict[k]))
            linDict[k] = v

        PARAMS.append(linDict)

    # --- Generating all files in a workDir
    refDir = os.path.dirname(main_fst)
    main_file = os.path.basename(main_fst)
    fastfiles = templateReplace(PARAMS,
                                refDir,
                                outputDir=workDir,
                                removeRefSubFiles=True,
                                main_file=main_file)

    return fastfiles
def ParametricExample():
    """ Example to run a set of OpenFAST simulations (parametric study)

    This script uses a reference directory (`ref_dir`) which contains a reference input file (.fst)
    1) The reference directory is copied to a working directory (`out_dir`).
    2) All the fast input files are generated in this directory based on a list of dictionaries (`PARAMS`).
    For each dictionary in this list:
       - The keys are "path" to a input parameter, e.g. `EDFile|RotSpeed`  or `FAST|TMax`.
         These should correspond to the variables used in the FAST inputs files.
       - The values are the values corresponding to this parameter
    For instance:
         PARAMS[0]['DT']                    = 0.01
         PARAMS[0]['EDFile|RotSpeed']       = 5
         PARAMS[0]['InflowFile|HWindSpeed'] = 10

    3) The simulations are run, successively distributed on `nCores` CPUs.
    4) The output files are read, and averaged based on a method (e.g. average over a set of periods,
        see averagePostPro in postpro for the different averaging methods).
       A pandas DataFrame is returned

    """
    # --- Parameters for this script
    ref_dir = 'NREL5MW/'  # Folder where the fast input files are located (will be copied)
    out_dir = 'NREL5MW_Parametric/'  # Output folder (will be created)
    main_file = 'Main_Onshore_OF2.fst'  # Main file in ref_dir, used as a template
    FAST_EXE = 'openfast2.3_x64s.exe'  # Location of a FAST exe (and dll)

    # --- Defining the parametric study  (list of dictionnaries with keys as FAST parameters)
    WS = [3, 5, 6, 7]
    RPM = [10, 12, 13, 15]
    BaseDict = {'TMax': 10, 'DT': 0.01, 'DT_Out': 0.1}
    BaseDict = case_gen.paramsNoController(BaseDict)  # Remove the controller
    #BaseDict = case_gen.paramsControllerDLL(BaseDict) # Activate the controller
    #BaseDict = case_gen.paramsStiff(BaseDict)         # Make the turbine stiff (except generator)
    #BaseDict = case_gen.paramsNoGen(BaseDict)         # Remove the Generator DOF
    PARAMS = []
    for i, (wsp, rpm) in enumerate(
            zip(WS, RPM)
    ):  # NOTE: same length of WS and RPM otherwise do multiple for loops
        p = BaseDict.copy()
        #p['AeroFile|TwrAero']       = True
        #p['EDFile|BldFile(1)|AdjBlMs'] =1.1
        #p['EDFile|BldFile(2)|AdjBlMs'] =1.1
        #p['EDFile|BldFile(3)|AdjBlMs'] =1.1
        p['EDFile|RotSpeed'] = rpm
        p['InflowFile|HWindSpeed'] = wsp
        p['InflowFile|WindType'] = 1  # Setting steady wind
        p['__name__'] = '{:03d}_ws{:04.1f}_om{:04.2f}'.format(
            i, p['InflowFile|HWindSpeed'], p['EDFile|RotSpeed'])
        PARAMS.append(p)
        i = i + 1
    # --- Generating all files in a output directory
    fastfiles = case_gen.templateReplace(PARAMS,
                                         ref_dir,
                                         outputDir=out_dir,
                                         removeRefSubFiles=True,
                                         main_file=main_file,
                                         oneSimPerDir=False)
    print(fastfiles)

    # --- Creating a batch script just in case
    runner.writeBatch(os.path.join(out_dir, '_RUN_ALL.bat'),
                      fastfiles,
                      fastExe=FAST_EXE)
    # --- Running the simulations
    runner.run_fastfiles(fastfiles,
                         fastExe=FAST_EXE,
                         parallel=True,
                         showOutputs=False,
                         nCores=4)

    # --- Simple Postprocessing
    # (averaging each signal over the last period for each simulation)
    outFiles = [os.path.splitext(f)[0] + '.outb' for f in fastfiles]
    avg_results = postpro.averagePostPro(
        outFiles,
        avgMethod='periods',
        avgParam=1,
        ColMap={'WS_[m/s]': 'Wind1VelX_[m/s]'},
        ColSort='WS_[m/s]')
    avg_results.drop('Time_[s]', axis=1, inplace=True)
    print(avg_results)
    return avg_results
def run_linearization():
    """ Example to run a set of OpenFAST simulations (linearizations)

    This script uses a reference directory (`ref_dir`) which contains a reference input file (.fst)
    1) The reference directory is copied to a working directory (`out_dir`).
    2) All the fast input files are generated in this directory based on a list of dictionaries (`PARAMS`).
    For each dictionary in this list:
       - The keys are "path" to a input parameter, e.g. `EDFile|RotSpeed`  or `FAST|TMax`.
         These should correspond to the variables used in the FAST inputs files.
       - The values are the values corresponding to this parameter
    For instance:
         PARAMS[0]['DT']                    = 0.01
         PARAMS[0]['EDFile|RotSpeed']       = 5
         PARAMS[0]['InflowFile|HWindSpeed'] = 10

    3) The simulations are run, successively distributed on `nCores` CPUs.
    4) The output files are read, and averaged based on a method (e.g. average over a set of periods,
        see averagePostPro in postpro for the different averaging methods).
       A pandas DataFrame is returned

    """
    # --- Parameters for this script
    of_dir = '/Users/dzalkind/Tools/openfast-dev'  # openfast dir, using dev branch as of Jan-14
    this_dir = os.path.dirname(__file__)
    ref_dir = '/Users/dzalkind/Tools/ROSCO_toolbox/Test_Cases/NREL-5MW'  # Folder where the fast input files are located (will be copied)
    out_dir = os.path.join(
        this_dir, 'NREL-5MW_Linear/')  # Output folder (will be created)
    main_file = 'NREL-5MW.fst'  # Main file in ref_dir, used as a template
    FAST_EXE = os.path.join(
        of_dir, 'install/bin/openfast')  # Location of a FAST exe (and dll)

    # --- Defining the parametric study  (list of dictionnaries with keys as FAST parameters)
    WS = [14, 16, 18]
    BaseDict = {'TMax': 600}
    BaseDict = case_gen.paramsLinearTrim(BaseDict)  # Run linear trim case

    PARAMS = []
    for i, wsp in enumerate(WS):
        p = BaseDict.copy()
        p['InflowFile|HWindSpeed'] = wsp
        p['InflowFile|WindType'] = 1  # Setting steady wind

        # Set DOFs
        p['EDFile|GenDOF'] = 'True'
        p['EDFile|FlapDOF1'] = 'True'
        p['EDFile|TwFADOF1'] = 'True'

        # NREL-5MW rated generator speed, torque, control params
        p['ServoFile|VS_RtGnSp'] = 1173 * .9
        p['ServoFile|VS_RtTq'] = 47402
        p['ServoFile|VS_Rgn2K'] = 0.0226
        p['ServoFile|VS_SlPc'] = 10.

        # Trim solution will converge to this rotor speed
        p['EDFile|RotSpeed'] = 12.1

        # Set number of linearizations
        p['NLinTimes'] = 12

        p['__name__'] = '{:03d}_ws{:04.1f}'.format(i,
                                                   p['InflowFile|HWindSpeed'])
        PARAMS.append(p)
        i = i + 1
    # --- Generating all files in a output directory
    fastfiles = case_gen.templateReplace(PARAMS,
                                         ref_dir,
                                         outputDir=out_dir,
                                         removeRefSubFiles=True,
                                         main_file=main_file,
                                         oneSimPerDir=False)
    print(fastfiles)

    # --- Creating a batch script just in case
    runner.writeBatch(os.path.join(out_dir, '_RUN_ALL.bat'),
                      fastfiles,
                      fastExe=FAST_EXE)
    # --- Running the simulations
    runner.run_fastfiles(fastfiles,
                         fastExe=FAST_EXE,
                         parallel=True,
                         showOutputs=True,
                         nCores=4)

    # --- Simple Postprocessing
    # (averaging each signal over the last period for each simulation)
    outFiles = [os.path.splitext(f)[0] + '.outb' for f in fastfiles]
    # avg_results = postpro.averagePostPro(outFiles, avgMethod='periods', avgParam=1, ColMap = {'WS_[m/s]':'Wind1VelX_[m/s]'},ColSort='WS_[m/s]')
    # avg_results.drop('Time_[s]',axis=1, inplace=True)

    return outFiles