def read_airfoils(airfoilFileNames, workdir=''): airfoils = [] for f in airfoilFileNames: AF = weio.FASTInFile(os.path.join(workdir, f)) af = dict() af['name'] = os.path.splitext(os.path.basename(f))[0] af['polar'] = AF['AFCoeff'] Tab = af['polar'] # Total range of alpha values, and window where we focus aRange = [-180, 180] aWindow = [-40, 40] # Finding min/max Cl Cd in windows range of alpha values iStart = np.argmin(abs(Tab[:, 0] - aWindow[0])) iEnd = np.argmin(abs(Tab[:, 0] - aWindow[1])) af['Clmin'] = np.min(Tab[iStart:iEnd, 1]) af['Clmax'] = np.max(Tab[iStart:iEnd, 1]) af['Cdmin'] = np.min(Tab[iStart:iEnd, 2]) af['iClmin'] = np.argmin(Tab[iStart:iEnd, 1]) + iStart af['iClmax'] = np.argmax(Tab[iStart:iEnd, 1]) + iStart af['iCdmin'] = np.argmin(Tab[iStart:iEnd, 2]) + iStart # For convenience aBefore = [aWindow[0] - 2, aWindow[0] - 1, aWindow[0]] aAfter = [aWindow[1], aWindow[1] + 1, aWindow[1] + 2] af['aClDelta'] = aBefore + [ Tab[af['iClmin'], 0], Tab[af['iClmax'], 0] ] + aAfter af['aCdDelta'] = aBefore + [Tab[af['iCdmin'], 0]] + aAfter af['ClDeltaMax'] = np.array([0, 0, 0, 0, 1, 0, 0, 0]) af['ClDeltaMin'] = np.array([0, 0, 0, -1, 0, 0, 0, 0]) af['CdDelta'] = np.array([0, 0, 0, 1, 0, 0, 0]) airfoils.append(af) return airfoils
def genotype_to_FASTphenotype(chromosome): """ Given a chromosome, create a FAST simulation folder Uses the global variables: RefValues, CH_MAP, ref_dir """ if not hasattr(chromosome,'data'): raise NotImplementedError('') def naming(p): return '_{:02.0f}'.format(p['InflowFile|HWindSpeed']) if len(chromosome)!=CH_MAP.nBases: raise Exception('Chromosome length ({}) not compatible with CH_MAP length ({})'.format(len(chromosome),CH_MAP.nBases)) #print('') #for gm,gene in zip(CH_MAP, CH_MAP.split(chromosome)): # print(gm.show_full_raw(gene)) PARAMS=[] for wsp,rpm,pit in zip(RefValues['WS'],RefValues['RPM'],RefValues['Pitch']): p=dict() p['FAST|TMax'] = 30 p['FAST|DT'] = 0.01 p['FAST|DT_Out'] = 0.1 p['FAST|OutFileFmt'] = 1 # TODO p['EDFile|RotSpeed'] = rpm p['EDFile|BlPitch(1)'] = pit p['EDFile|BlPitch(2)'] = pit p['EDFile|BlPitch(3)'] = pit p['InflowFile|HWindSpeed'] = wsp p['InflowFile|WindType'] = 1 # Setting steady wind for gm,gene in zip(CH_MAP, CH_MAP.split(chromosome)): if gm.kind=='fast_param': p[gm.name]=gm.decode(gene) elif gm.kind=='builtin': if gm.name=='pitch': p['EDFile|BlPitch(1)'] = gm.decode(gene) p['EDFile|BlPitch(2)'] = gm.decode(gene) p['EDFile|BlPitch(3)'] = gm.decode(gene) #print(gm.name, '->',p[gm.name],gene) PARAMS.append(p) sim_dir=chromosome.data['dir'] fastlib.templateReplace(ref_dir,PARAMS,workdir=sim_dir,name_function=naming,RemoveRefSubFiles=True) # --- Patching the airfoil files for gm,gene in zip(CH_MAP, CH_MAP.split(chromosome)): if gm.kind=='airfoil': af_mut = copy.deepcopy(gm.meta) af_mut = patch_airfoil(gene,af_mut) AF = weio.FASTInFile(os.path.join(sim_dir,gm.name)) AF['AFCoeff'] = af_mut['polar'] AF.write()
def spanwisePostProFF(fastfarm_input, avgMethod='constantwindow', avgParam=30, D=1, df=None, fastfarm_out=None): """ Opens a FASTFarm output file, extract the radial data, average them and returns spanwise data D: diameter TODO, extract it from the main file See faslibt.averageDF for `avgMethod` and `avgParam`. """ # --- Opening input file and extracting inportant variables main = weio.FASTInFile(fastfarm_input) iOut = main['OutRadii'] dr = main['dr'] # Radial increment of radial finite-difference grid (m) OutDist = main[ 'OutDist'] # List of downstream distances for wake output for an individual rotor WT = main['WindTurbines'] nWT = len(WT) vr_bar = dr * np.array(iOut) / (D / 2) vD = np.array(OutDist) / D nr = len(vr_bar) nD = len(vD) # --- Opening ouputfile if df is None: df = weio.read(fastfarm_out).toDataFrame() # --- Extracting time series of radial data only colRadial = SensorsFARMRadial(nWT=nWT, nD=nD, nR=nr, signals=df.columns.values) colRadial = ['Time_[s]'] + colRadial dfRadialTime = df[ colRadial] # TODO try to do some magic with it, display it with a slider # --- Averaging data dfAvg = fastlib.averageDF(df, avgMethod='constantwindow', avgParam=30) # --- Brute force storing of radial data Columns = [vr_bar] if D == 1: ColumnNames = ['r_[m]'] else: ColumnNames = ['r/R_[-]'] for iWT in range(nWT): Values = np.zeros((len(vr_bar), 1)) nCount = 0 col_out = 'CtT{:d}_[-]'.format(iWT + 1) for ir in range(nr): col = 'CtT{:d}N{:02d}_[-]'.format(iWT + 1, ir + 1) if col in dfAvg.columns.values: Values[ir, 0] = dfAvg[col] nCount += 1 if nCount != nr and nCount > 0: print('[WARN] Not all values found for {}, found {}/{}'.format( col_out, nCount, nr)) if nCount > 0: Columns.append(Values) ColumnNames.append('CtT{:d}'.format(iWT + 1)) for iWT in range(nWT): for iD in range(nD): Values = np.zeros((len(vr_bar), 1)) nCount = 0 col_out = 'WkDfVxT{:d}D{:d}_[m/s]'.format(iWT + 1, iD + 1) for ir in range(nr): col = 'WkDfVxT{:d}N{:02d}D{:d}_[m/s]'.format( iWT + 1, ir + 1, iD + 1) if col in dfAvg.columns.values: Values[ir, 0] = dfAvg[col] nCount += 1 if nCount != nr and nCount > 0: print('[WARN] Not all values found for {}, found {}/{}'.format( col_out, nCount, nr)) if nCount > 0: Columns.append(Values) ColumnNames.append(col_out) for iWT in range(nWT): for iD in range(nD): Values = np.zeros((len(vr_bar), 1)) nCount = 0 col_out = 'WkDfVrT{:d}D{:d}_[m/s]'.format(iWT + 1, iD + 1) for ir in range(nr): col = 'WkDfVrT{:d}N{:02d}D{:d}_[m/s]'.format( iWT + 1, ir + 1, iD + 1) if col in dfAvg.columns.values: Values[ir, 0] = dfAvg[col] nCount += 1 if nCount != nr and nCount > 0: print('[WARN] Not all values found for {}, found {}/{}'.format( col_out, nCount, nr)) if nCount > 0: Columns.append(Values) ColumnNames.append(col_out) data = np.column_stack(Columns) dfRad = pd.DataFrame(data=data, columns=ColumnNames) return dfRad, dfRadialTime
def generateParametricInputs(template_dir,workdir=None,main_file=None,OPER=None,RemoveAllowed=False,bStiff=False,bSteadyAero=False,TMax=None): if template_dir[-1]=='/' or template_dir[-1]=='\\' : template_dir=template_dir[0:-1] if workdir is None: workdir=template_dir+'_Parametric' # Copying template folder print(template_dir, ' ',workdir) if os.path.exists(workdir) and RemoveAllowed: rmtree(workdir) #distutils.dir_util.copy_tree(template_dir, workdir) copytree(template_dir, workdir, ignore=ignore_patterns('.git')) fastlib.removeFASTOuputs(workdir) print('Generating fast input files...') # --- Fast main file use as "master" if main_file is None: FstFiles=set(glob.glob(os.path.join(template_dir,'*.fst'))+glob.glob(os.path.join(template_dir,'*.FST'))) print(FstFiles) if len(FstFiles)>1: raise Exception('More than one fst file found in template folder') main_file=FstFiles.pop() main_file=os.path.join(workdir, main_file) # --- Reading Master File print('Reading template file: '+main_file) FST = weio.FASTInFile(main_file); windfilename_ref = os.path.join(workdir,FST['InflowFile'].strip('"')) edfilename_ref = os.path.join(workdir,FST['EDFile'].strip('"')) adfilename_ref = os.path.join(workdir,FST['AeroFile'].strip('"')) sdfilename_ref = os.path.join(workdir,FST['ServoFile'].strip('"')) # Wnd = weio.FASTInFile(windfilename_ref); ED = weio.FASTInFile(edfilename_ref); AD = weio.FASTInFile(adfilename_ref); windbasename_ref = os.path.basename(windfilename_ref) edbasename_ref = os.path.basename(edfilename_ref) adbasename_ref = os.path.basename(adfilename_ref) sdbasename_ref = os.path.basename(sdfilename_ref) fastbasename_ref = os.path.basename(main_file) # Rewritting SD file, making sure the controller is off SD = weio.FASTInFile(sdfilename_ref); SD['PCMode']=0; SD['VSContrl']=0; SD['YCMode']=0; SD.write (os.path.join(workdir,sdbasename_ref)) # # --- Generating inputs fastfiles=[] if OPER is None: raise Exception('Please provide OPER') Keys = list(OPER.keys()) nValues = len(OPER[Keys[0]]) print('Number of values ',nValues) for i in range(nValues): Params = [(k,OPER[k][i]) for k in Keys] strID = '_{:03d}'.format(i) print(Params) for k in Keys: val = OPER[k][i] if k.lower()=='ws': strID += '_ws{:04.1f}'.format(val) elif k.lower()=='omega': strID += '_om{:04.2f}'.format(val) elif k.lower()=='pitch': strID += '_pt{:04.2f}'.format(val) else: raise Exception('Not supported {}'.format(k)) fastfilename = fastbasename_ref.replace('.fst',strID+'.fst') windfilename = windbasename_ref.replace('.dat',strID+'.dat') edfilename = edbasename_ref.replace('.dat',strID+'.dat') adfilename = adbasename_ref.replace('.dat',strID+'.dat') sdfilename = sdbasename_ref.replace('.dat',strID+'.dat') for k in Keys: val = OPER[k][i] if k.lower()=='ws': Wnd['WindType'] = 1 Wnd['HWindSpeed'] = val FST['InflowFile'] = '"'+windfilename+'"' Wnd.write(os.path.join(workdir,windfilename)) elif k.lower()=='omega': ED['RotSpeed'] = val elif k.lower()=='pitch': ED['BlPitch(1)'] = val ED['BlPitch(2)'] = val ED['BlPitch(3)'] = val if TMax is not None: FST['TMax'] = TMax if bSteadyAero: AD['AFAeroMod']=1 # remove dynamic effects dynamic # ED['GenDOF'] = 'False' # important to prescribe rot speed if bStiff: ED['FlapDOF1']='False' ED['FlapDOF2']='False' ED['EdgeDOF' ]='False' ED['TeetDOF' ]='False' ED['DrTrDOF' ]='False' ED['YawDOF' ]='False' ED['TwFADOF1']='False' ED['TwFADOF2']='False' ED['TwSSDOF1']='False' ED['TwSSDOF2']='False' FST['EDFile'] = '"'+edfilename+'"' FST['AeroFile'] = '"'+adfilename+'"' #FST['ServoFile'] = '"'+sdfilename+'"' ED.write (os.path.join(workdir,edfilename)) AD.write (os.path.join(workdir,adfilename)) FST.write(os.path.join(workdir,fastfilename)) fastfiles.append(os.path.join(workdir,fastfilename)) # os.remove(os.path.join(workdir,fastbasename_ref)) # os.remove(edfilename_ref) return fastfiles,workdir
# --- Main Parameters refdir = '../OF2-SimpleGen/' workdir ='Model_Parametric_Focus/' fastfile = 'SWT-2.3-93OpenFAST2_R2.fst'; TMax=10 ColKeep=['RotSpeed_[rpm]','BldPitch1_[deg]','RtAeroCp_[-]','RtAeroCt_[-]','Wind1VelX_[m/s]','Time_[s]'] #fastlib.FAST_EXE='OpenFAST2_x64d_ebra.exe' fastlib.FAST_EXE='OpenFAST2_win32d_ebra.exe' # --- Parametric fst = weio.FASTInFile(os.path.join(refdir,fastfile)) print(fst['EDFile']) ed = weio.FASTInFile(os.path.join(refdir,fst['EDFile'].replace('"',''))) print('R=', ed['TipRad']) U0=5 R=ed['TipRad'] Lambda = np.linspace(7.5,8.5,11); Pitch = np.linspace(-1.0,1.0,22); Omega = U0 * Lambda/R*60/(2*np.pi) # TODO, use more realistic combinations of WS and Omega OPER=dict() OPER['WS'] = [] OPER['Pitch'] = [] OPER['Omega'] = [] for p in Pitch:
def spanwisePostProFF(fastfarm_input, avgMethod='constantwindow', avgParam=30, D=1, df=None, fastfarm_out=None): """ Opens a FASTFarm output file, extract the radial data, average them and returns spanwise data D: diameter TODO, extract it from the main file See faslibt.averageDF for `avgMethod` and `avgParam`. """ # --- Opening ouputfile if df is None: df = weio.read(fastfarm_out).toDataFrame() # --- Opening input file and extracting inportant variables if fastfarm_input is None: # We don't have an input file, guess numbers of turbine, diameters, Nodes... cols, sIdx = fastlib.find_matching_pattern(df.columns.values, 'T(\d+)') nWT = np.array(sIdx).astype(int).max() cols, sIdx = fastlib.find_matching_pattern(df.columns.values, 'D(\d+)') nD = np.array(sIdx).astype(int).max() cols, sIdx = fastlib.find_matching_pattern(df.columns.values, 'N(\d+)') nr = np.array(sIdx).astype(int).max() vr = None vD = None D = 0 else: main = weio.FASTInFile(fastfarm_input) iOut = main['OutRadii'] dr = main[ 'dr'] # Radial increment of radial finite-difference grid (m) OutDist = main[ 'OutDist'] # List of downstream distances for wake output for an individual rotor WT = main['WindTurbines'] nWT = len(WT) vr = dr * np.array(iOut) vD = np.array(OutDist) nr = len(iOut) nD = len(vD) # --- Extracting time series of radial data only colRadial = SensorsFARMRadial(nWT=nWT, nD=nD, nR=nr, signals=df.columns.values) colRadial = ['Time_[s]'] + colRadial dfRadialTime = df[ colRadial] # TODO try to do some magic with it, display it with a slider # --- Averaging data dfAvg = fastlib.averageDF(df, avgMethod=avgMethod, avgParam=avgParam) # --- Extract radial data ColsInfo, nrMax = spanwiseColFastFarm(df.columns.values, nWT=nWT, nD=nD) dfRad = fastlib.extract_spanwise_data(ColsInfo, nrMax, df=None, ts=dfAvg.iloc[0]) #dfRad = fastlib.insert_radial_columns(dfRad, vr) if vr is None: dfRad.insert(0, 'i_[#]', np.arange(nrMax) + 1) else: dfRad.insert(0, 'r_[m]', vr[:nrMax]) dfRad['i/n_[-]'] = np.arange(nrMax) / nrMax # --- Extract downstream data ColsInfo, nDMax = diameterwiseColFastFarm(df.columns.values, nWT=nWT) dfDiam = fastlib.extract_spanwise_data(ColsInfo, nDMax, df=None, ts=dfAvg.iloc[0]) #dfDiam = fastlib.insert_radial_columns(dfDiam) if vD is None: dfDiam.insert(0, 'i_[#]', np.arange(nDMax) + 1) else: dfDiam.insert(0, 'x_[m]', vD[:nDMax]) dfDiam['i/n_[-]'] = np.arange(nDMax) / nDMax return dfRad, dfRadialTime, dfDiam