def test_HAWCStab2(self): # power file F = weio.read(os.path.join(MyDir, 'HAWCStab2.pwr')) DF = F.toDataFrame() self.assertAlmostEqual(DF.values[-1, 1], 0.1553480512E+05) self.assertAlmostEqual(DF.values[-1, -1], 0.3181950053E+09) self.assertEqual(DF.columns[0], 'V_[m/s]') self.assertEqual(DF.columns[1], 'P_[kW]') # induction files F = weio.read(os.path.join( MyDir, 'HAWCStab2_u3000.ind')) # mult files, normal .ind DFS = F.toDataFrame() self.assertAlmostEqual(DFS[3.0].values[-1, 1], 0.517961E+00) self.assertAlmostEqual(DFS[3.5].values[-1, -1], -0.509335E+00) self.assertEqual(DFS[3.0].columns[0], 's_[m]') self.assertEqual(DFS[3.5].columns[1], 'A_[-]') F = weio.read(os.path.join(MyDir, 'HAWCStab2_defl_u3000.ind')) # defl .ind DFS = F.toDataFrame() self.assertAlmostEqual(DFS[3.0].values[-1, 1], 19) self.assertAlmostEqual(DFS[3.0].values[-1, -1], 0.242932E-05) self.assertEqual(DFS[3.0].columns[0], 's_[m]') self.assertEqual(DFS[3.0].columns[1], 'Element_no_[-]') F = weio.read(os.path.join(MyDir, 'HAWCStab2_fext_u3000.ind')) # fext .ind DFS = F.toDataFrame() self.assertAlmostEqual(DFS[3.0].values[-1, 1], 20) self.assertAlmostEqual(DFS[3.0].values[-1, -1], -0.170519E+03) self.assertEqual(DFS[3.0].columns[0], 's_[m]') self.assertEqual(DFS[3.0].columns[1], 'Node_[-]')
def test_Bladed(self): F = weio.read(os.path.join(MyDir, 'Bladed_out_binary.$41')) #F = BladedFile(os.path.join(MyDir,'Bladed_out_binary.$41')) DF = F.toDataFrame() self.assertAlmostEqual( DF['0.0m-Blade 1 Fx (Root axes)[0.0m-N]'].values[0], 146245.984375) F = weio.read(os.path.join(MyDir, 'Bladed_out_ascii.$41'))
def doCompare(file_ref, file_cur, precision, smallOK=False): filebase = os.path.splitext(os.path.basename(file_cur))[0] print('Comparing: ', file_cur) print(' against: ', file_ref) df_ref = weio.read(file_ref).toDataFrame() try: df_cur = weio.read(file_cur).toDataFrame() except: print('>>> File not found:', df_cur) printFAIL() return False return compare_df(df_ref, df_cur, precision, filebase, smallOK=smallOK)
def test_Bladed_case2_project(self): F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$PJ')) DF = F.toDataFrame() #print(DFS.keys()) #DF=DFS['Misc'] #print(DF.shape) #print(DF.columns) #print(DF.columns[0]) #print(DF.columns[50]) self.assertEqual(DF.shape, (10, 89)) self.assertEqual(DF.columns[0], 'Time [s]') self.assertEqual(DF.columns[1], 'Time from start of simulation [s]') self.assertEqual(DF.columns[27], '26.41m-DPMOM1 [Nm/m]') self.assertEqual(DF.columns[69], '38.75m-Blade 1 y-position [m]') self.assertEqual(DF.columns[88], 'Foundation Fz [N]') self.assertAlmostEqual(DF['Time from start of simulation [s]'][0], 7.0) self.assertAlmostEqual(DF['26.41m-DPMOM1 [Nm/m]'][0], -226.85083, 5) self.assertAlmostEqual(DF['38.75m-Blade 1 y-position [m]'].values[0], -27.949090957, 5) self.assertAlmostEqual(DF['38.75m-Blade 1 y-position [m]'].values[-1], -39.96076965, 5) self.assertAlmostEqual(DF['Foundation Fz [N]'][0], -1092165.5) self.assertAlmostEqual(DF['Foundation Fz [N]'].values[-1], -1093664.75) self.assertFalse(DF.isnull().values.any())
def test_Bladed(self): ## check for binary F = weio.read(os.path.join(MyDir, 'Bladed_out_binary.$41')) #F = BladedFile(os.path.join(MyDir,'Bladed_out_binary.$41')) DF = F.toDataFrame() self.assertAlmostEqual(DF['0.0m-Blade 1 Fx (Root axes) [N]'].values[0], 146245.984375) self.assertAlmostEqual( DF['0.0m-Blade 1 Fx (Root axes) [N]'].values[-1], 156967.484375) ## check for ASCII F = weio.read(os.path.join(MyDir, 'Bladed_out_ascii.$41')) DF = F.toDataFrame() self.assertAlmostEqual(DF['0.0m-Blade 1 Fx (Root axes) [N]'].values[0], 146363.8) self.assertAlmostEqual( DF['0.0m-Blade 1 Fx (Root axes) [N]'].values[-1], 156967.22)
def test_BHAWC(self): F = weio.read(os.path.join(MyDir, 'BHAWC_out_ascii.sel')) DF = F.toDataFrame() self.assertEqual(DF.values[-1, 1], 147.85) self.assertEqual(DF.columns[0], 't_[s]') self.assertEqual(DF.columns[1], 'ang_azi_[deg]') # Testing that "exported" sel files are the same F.test_ascii(bCompareWritesOnly=True, bDelete=True) os.remove(os.path.join(MyDir, 'BHAWC_out_ascii_TMP.dat')) os.remove(os.path.join(MyDir, 'BHAWC_out_ascii_TMP2.dat')) # Testing that "exported" dat files are the same F = weio.read(os.path.join(MyDir, 'BHAWC_out_ascii.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) os.remove(os.path.join(MyDir, 'BHAWC_out_ascii_TMP.sel')) os.remove(os.path.join(MyDir, 'BHAWC_out_ascii_TMP2.sel'))
def _load_file_tabs(self, filename, fileformat=None): """ load a single file, adds table, and potentially trigger plotting """ if not os.path.isfile(filename): raise Exception('Error: File not found: `' + filename + '`') try: F = weio.read(filename, fileformat=fileformat) dfs = F.toDataFrame() except weio.FileNotFoundError as e: raise Exception( 'Error: A file was not found!\n\n While opening:\n\n {}\n\n the following file was not found:\n\n {}' .format(filename, e.filename)) except IOError: raise Exception('Error: IO Error thrown while opening file: ' + filename) except MemoryError: raise Exception( 'Error: Insufficient memory!\n\nFile: ' + filename + '\n\nTry closing and reopening the program, or use a 64 bit version of this program (i.e. of python).' ) except weio.EmptyFileError: raise Exception('Error: File empty!\n\nFile is empty: ' + filename + '\n\nOpen a different file.') except weio.FormatNotDetectedError: raise Exception('Error: File format not detected!\n\nFile: ' + filename + '\n\nUse an explicit file-format from the list') except weio.WrongFormatError as e: raise Exception('Error: Wrong file format!\n\nFile: '+filename+'\n\n' \ 'The file parser for the selected format failed to open the file.\n\n'+ \ 'The reported error was:\n'+e.args[0]+'\n\n' + \ 'Double-check your file format and report this error if you think it''s a bug.') except weio.BrokenFormatError as e: raise Exception('Error: Inconsistency in the file format!\n\nFile: '+filename+'\n\n' \ 'The reported error was:\n'+e.args[0]+'\n\n' + \ 'Double-check your file format and report this error if you think it''s a bug.') except: raise # Returning a list of tables tabs = [] if dfs is None: pass elif not isinstance(dfs, dict): if len(dfs) > 0: tabs = [ Table(data=dfs, filename=filename, fileformat=F.formatName()) ] else: for k in list(dfs.keys()): if len(dfs[k]) > 0: tabs.append( Table(data=dfs[k], name=str(k), filename=filename, fileformat=F.formatName())) return tabs
def __init__(self, ED_or_FST_file, StateFile=None, nShapes_twr=1, nShapes_bld=0, DEBUG=False): # --- Input data from fst and ED file ext = os.path.splitext(ED_or_FST_file)[1] if ext.lower() == '.fst': FST = weio.read(ED_or_FST_file) rootdir = os.path.dirname(ED_or_FST_file) EDfile = os.path.join(rootdir, FST['EDFile'].strip('"')).replace('\\', '/') else: EDfile = ED_or_FST_file self.ED = weio.read(EDfile) # --- Loading linear model if StateFile is not None: self.A, self.B, self.C, self.D, self.M = loadLinStateMatModel( StateFile) else: raise NotImplementedError() self.sX = self.A.columns self.nGear = self.ED['GBRatio'] self.theta_tilt = -self.ED[ 'ShftTilt'] * np.pi / 180 # NOTE: tilt has wrong orientation in FAST # --- Initial conditions omega_init = self.ED['RotSpeed'] * 2 * np.pi / 60 # rad/s psi_init = self.ED['Azimuth'] * np.pi / 180 # rad FA_init = self.ED['TTDspFA'] iPsi = list(self.sX).index('psi_rot_[rad]') nDOFMech = int(len(self.A) / 2) q_init = np.zeros(2 * nDOFMech) # x2, state space if nShapes_twr > 0: q_init[0] = FA_init q_init[iPsi] = psi_init q_init[nDOFMech + iPsi] = omega_init self.q_init = q_init
def test_FASTIn(self): F = weio.read(os.path.join(MyDir, 'FASTIn_BD.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['PitchK'], 2.0e+07) self.assertEqual(F['MemberGeom'][-1, 2], 61.5) self.assertEqual(F['MemberGeom'][-2, 3], 0.023000) F = weio.read(os.path.join(MyDir, 'FASTIn_ED.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['RotSpeed'], 0.2) F = weio.read(os.path.join(MyDir, 'FASTIn_ED_bld.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['BldEdgSh(6)'], -0.6952) F = weio.read(os.path.join(MyDir, 'FASTIn_ED_twr.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['AdjFASt'], 1) F = weio.read(os.path.join(MyDir, 'FASTIn_AD15.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['TipLoss'], 'True') F = weio.read(os.path.join(MyDir, 'FASTIn_ExtPtfm_SubSef.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['StiffnessMatrix'][2, 2], 1.96653266e+09) F = weio.read(os.path.join(MyDir, 'FASTIn_HD.dat')) #F.test_ascii(bCompareWritesOnly=True,bDelete=True) # TODO self.assertEqual(F['RdtnDT'], 0.0125) F = weio.read(os.path.join(MyDir, 'FASTIn_IF_NoHead.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['Z0'], 0.03) F = weio.read(os.path.join(MyDir, 'FASTIn_SbD.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['Joints'][0, 3], -100) self.assertEqual(int(F['Members'][0, 1]), 1) self.assertEqual(int(F['Members'][0, 2]), 2) F = weio.read(os.path.join(MyDir, 'FASTIn_SD.dat')) F.test_ascii(bCompareWritesOnly=True, bDelete=True) self.assertEqual(F['PitManRat(1)'], 2)
def test_HAWC2_pc(self): F = weio.read(os.path.join(MyDir, 'HAWC2_pc.dat')) self.assertEqual(len(F.data.pc_sets), 1) thicknesses = F.data.pc_sets[1][0] firstPolar = F.data.pc_sets[1][1][0] np.testing.assert_almost_equal(thicknesses, [24.1, 30.1, 36, 48, 60, 100]) self.assertEqual(firstPolar.shape, (105, 4)) np.testing.assert_almost_equal(firstPolar[0, 0], -180) np.testing.assert_almost_equal(firstPolar[-1, 0], 180)
def setFastFarmOutputs(fastFarmFile, OutListT1): """ Duplicate the output list, by replacing "T1" with T1->Tn """ fst = weio.read(fastFarmFile) nWTOut = min(fst['NumTurbines'],9) # Limited to 9 turbines OutList=[''] for s in OutListT1: s=s.strip('"') if s.find('T1'): OutList+=['"'+s.replace('T1','T{:d}'.format(iWT+1))+'"' for iWT in np.arange(nWTOut) ] else: OutList+='"'+s+'"' fst['OutList']=OutList fst.write(fastFarmFile)
def test_HAWC2(self): F = weio.read(os.path.join(MyDir, 'HAWC2_out_ascii.dat')) DF = F.toDataFrame() self.assertEqual(DF.values[-1, 1], -1.72572E+03) self.assertEqual(DF.values[-1, -1], 3.63349E+03) self.assertEqual(DF.columns[0], 'Time_[s]') self.assertEqual(DF.columns[1], 'WSP gl. coo.,Vy_[m/s]') # Test that "exported dat files" are the same # NOTE: cannot do comparison of sel files since names are different F.test_ascii(bCompareWritesOnly=True, bDelete=True) os.remove(os.path.join(MyDir, 'HAWC2_out_ascii_TMP.sel')) os.remove(os.path.join(MyDir, 'HAWC2_out_ascii_TMP2.sel'))
def __init__(self,prefix,nLin=None): if nLin is None: linfiles= glob.glob(prefix + '.*.lin') # TODO we want a more rigorous regexp self.nLinTimes = len(linfiles) else: self.nLinTimes = nLin #print(prefix, self.nLinTimes) self.prefix = prefix self.Data = [] self.vAzim = [] self.vWS = [] self.vPitch = [] self.vRotSpeed = [] self.vBu = [] for i in np.arange(self.nLinTimes): linfilename= prefix+'.'+str(i+1)+'.lin' print(linfilename) if not os.path.exists(linfilename): print('Linearization file missing: ',linfilename) linfile=weio.read(linfilename) df=linfile.toDataFrame() self.Data.append(linfile) #self.A=lin['A'] #B=linfile['B'] #u=linfile['u'] #self.C=lin['C'] #self.D=lin['D'] if linfile['WindSpeed'] is not None: self.vWS.append(linfile['WindSpeed']) else: try: self.vWS.append(df['u']['WS_[m/s]'][0]) except: print('Wind speed not found in input, assuming 0m/s') self.vWS.append(0) self.vRotSpeed.append(linfile['RotSpeed']) self.vAzim.append(linfile['Azimuth']) self.vPitch.append(df['u']['B1pitch_[rad]'][0]*180/np.pi) self.WS = np.mean(self.vWS) self.Pitch = np.mean(self.vPitch) self.RotSpeed = np.mean(self.vRotSpeed) self.x = df['x'] self.y = df['y'] self.u = df['u'] try: self.EDdescr = linfile['EDDOF'] except: self.EDdescr = None
def writeFastFarm(outputFile, templateFile, xWT, yWT, zWT, FFTS=None, OutListT1=None): """ Write FastFarm input file based on a template, a TurbSimFile and the Layout outputFile: .fstf file to be written templateFile: .fstf file that will be used to generate the output_file XWT,YWT,ZWT: positions of turbines FFTS: FastFarm TurbSim parameters as returned by fastFarmTurbSimExtent """ # --- Read template fast farm file fst=weio.read(templateFile) # --- Replace box extent values if FFTS is not None: fst['Mod_AmbWind'] = 2 for k in ['DT', 'DT_High', 'NX_Low', 'NY_Low', 'NZ_Low', 'X0_Low', 'Y0_Low', 'Z0_Low', 'dX_Low', 'dY_Low', 'dZ_Low', 'NX_High', 'NY_High', 'NZ_High']: if isinstance(FFTS[k],int): fst[k] = FFTS[k] else: fst[k] = np.around(FFTS[k],3) fst['WrDisDT'] = FFTS['DT'] # --- Set turbine names, position, and box extent nWT = len(xWT) fst['NumTurbines'] = nWT if FFTS is not None: nCol= 10 else: nCol = 4 ref_path = fst['WindTurbines'][0,3] WT = np.array(['']*nWT*nCol,dtype='object').reshape((nWT,nCol)) for iWT,(x,y,z) in enumerate(zip(xWT,yWT,zWT)): WT[iWT,0]=x WT[iWT,1]=y WT[iWT,2]=z WT[iWT,3]=insertTN(ref_path,iWT+1,nWT) if FFTS is not None: WT[iWT,4]=FFTS['X0_High'][iWT] WT[iWT,5]=FFTS['Y0_High'][iWT] WT[iWT,6]=FFTS['Z0_High'] WT[iWT,7]=FFTS['dX_High'] WT[iWT,8]=FFTS['dY_High'] WT[iWT,9]=FFTS['dZ_High'] fst['WindTurbines']=WT fst.write(outputFile) if OutListT1 is not None: setFastFarmOutputs(outputFile, OutListT1)
def loadMeasurements(KF, MeasFile, nUnderSamp=1, tRange=None, ColMap=DEFAULT_COL_MAP): # --- Loading "Measurements" nGear = KF.WT.ED['GBRatio'] df=weio.read(MeasFile).toDataFrame() df=df.iloc[::nUnderSamp,:] # reducing sampling if tRange is not None: df=df[(df['Time_[s]']>= tRange[0]) & (df['Time_[s]']<= tRange[1])] # reducing time range time = df['Time_[s]'].values dt = (time[-1] - time[0])/(len(time)-1) KF.df = fastlib.remap_df(df, ColMap, bColKeepNewOnly=False) # --- KF.discretize(dt, method='exponential') KF.setTimeVec(time) KF.setCleanValues(KF.df) # --- Estimate sigmas from measurements sigX_c,sigY_c = KF.sigmasFromClean(factor=1)
def radial_avg(): import fastlib import weio avgMethod='constantwindow' avgParam=5 filename='test.outb' rho=1.225 R=63 r_FST_aero=[ TODO ] df = weio.read(filename).toDataFrame() dfAvg = fastlib.averageDF(df, avgMethod=avgMethod ,avgParam=avgParam) dfAeroRad = fastlib.spanwiseAD(dfAvg.iloc[0], r_FST_aero/R, rho , R, nB=3)
def loadMeasurements(KF, MeasFile, nUnderSamp=1, tRange=None): # --- Loading "Measurements" ColMap = { 'ut1': 'TTDspFA', 'psi': 'Azimuth', 'ut1dot': 'NcIMUTVxs', 'omega': 'RotSpeed', 'Thrust': 'RtAeroFxh', 'Qaero': 'RtAeroMxh', 'Qgen': 'GenTq', 'WS': 'RtVAvgxh', 'pitch': 'BldPitch1', 'TTacc': 'NcIMUTAxs' } # 'WS':'Wind1VelX', 'pitch':'BldPitch1','TTacc':'NcIMUTAxs'} # 'Thrust':'RotThrust','Qaero':'RtAeroMxh','Qgen':'GenTq', # NOTE: RotThrust contain gravity and inertia # TODO nGear = KF.WT.ED['GBRatio'] df = weio.read(MeasFile).toDataFrame() df.columns = [v.split('_[')[0] for v in df.columns.values] if tRange is not None: df = df[(df['Time'] >= tRange[0]) & (df['Time'] <= tRange[1])] # reducing time range df = df.iloc[::nUnderSamp, :] # reducing sampling time = df['Time'].values dt = (time[-1] - time[0]) / (len(time) - 1) df['GenSpeed'] *= 2 * np.pi / 60 # converted to rad/s df['RotSpeed'] *= 2 * np.pi / 60 # converted to rad/s df['GenTq'] *= 1000 * nGear # Convert to rot torque df['Azimuth'] *= np.pi / 180 # rad df['RotTorq'] *= 1000 # [kNm]->[Nm] df['RotThrust'] *= 1000 # [kN]->[N] KF.df = df # --- KF.discretize(dt, method='exponential') KF.setTimeVec(time) KF.setCleanValues(df, ColMap) # --- Estimate sigmas from measurements sigX_c, sigY_c = KF.sigmasFromClean(factor=1)
def spanwisePostPro(FST_In,suffix=None): # --- Init fst = weio.FASTInputDeck(FST_In) ED = fst.ED AD = fst.Aero R = ED ['TipRad'] r_FST_struct = fastlib.ED_BldGag(ED) r_FST_aero = fastlib.AD_BldGag(AD,AD.Bld1) + ED['HubRad'] df = weio.read(FST_In.replace('.fst','.outb')).toDataFrame() dfAvg = fastlib.averageDF(df,avgMethod = 'constantwindow',avgParam=5) Ct=dfAvg['RtAeroCt_[-]'].values if suffix is None: suffix='Ct{:.2f}'.format(Ct[0]) spanwiseAD(dfAvg.iloc[0], r_FST_aero/R, AD['AirDens'], R, 3, suffix)
def create_polar_files(polars, templatePolFile, outputPolDir='./'): pol = weio.read(templatePolFile) for i, p in enumerate(polars): # Creating a polar object to compute unsteady params polar = Polar(np.nan, p[:, 0], p[:, 1], p[:, 2], p[:, 3]) (alpha0, alpha1, alpha2, cnSlope, cn1, cn2, cd0, cm0) = polar.unsteadyParams() #print(cnSlope,cn1,cn2) # Setting unsteady parameters pol['Re'] = 1.0000 # TODO UNKNOWN and not used if np.isnan(alpha0): pol['alpha0'] = 0 else: pol['alpha0'] = np.around(alpha0, 4) pol['alpha1'] = np.around(alpha1, 4) # TODO approximate pol['alpha2'] = np.around(alpha2, 4) # TODO approximate pol['C_nalpha'] = np.around(cnSlope, 4) pol['Cn1'] = np.around(cn1, 4) # TODO verify pol['Cn2'] = np.around(cn2, 4) pol['Cd0'] = np.around(cd0, 4) pol['Cm0'] = np.around(cm0, 4) # Setting pol['NumAlf'] = p.shape[0] pol['AFCoeff'] = np.around(p, 5) filename = os.path.join(outputPolDir, '{}_{}.dat'.format('Airfoil', i)) pol.write(filename) #print('Writing polar to file:',filename) #if i==3: # import matplotlib.pyplot as plt # alpha=polar.alpha # cn=polar.cn # plt.plot(alpha, cn,label='cn') # plt.plot(alpha , np.pi/180*cnSlope*(alpha-alpha0),label='cn lin') # plt.plot(alpha1, np.pi/180*cnSlope*(alpha1-alpha0),'o',label='cn Stall') # plt.plot(alpha2, np.pi/180*cnSlope*(alpha2-alpha0),'o',label='cn Stall') # plt.show() print('{} polars written to {}'.format(len(polars), outputPolDir))
def plotFastFarmSetup(fastFarmFile): """ """ import matplotlib.pyplot as plt fst=weio.read(fastFarmFile) fig = plt.figure(figsize=(13.5,10)) ax = fig.add_subplot(111,aspect="equal") WT=fst['WindTurbines'] x = WT[:,0].astype(float) y = WT[:,1].astype(float) if fst['Mod_AmbWind'] == 2: xmax_low = fst['X0_Low']+fst['DX_Low']*fst['NX_Low'] ymax_low = fst['Y0_Low']+fst['DY_Low']*fst['NY_Low'] # low-res box ax.plot([fst['X0_Low'],xmax_low,xmax_low,fst['X0_Low'],fst['X0_Low']], [fst['Y0_Low'],fst['Y0_Low'],ymax_low,ymax_low,fst['Y0_Low']],'--k',lw=2,label='Low') X0_High = WT[:,4].astype(float) Y0_High = WT[:,5].astype(float) dX_High = WT[:,7].astype(float)[0] dY_High = WT[:,8].astype(float)[0] nX_High = fst['NX_High'] nY_High = fst['NY_High'] # high-res boxes for wt in range(len(x)): xmax_high = X0_High[wt]+dX_High*nX_High ymax_high = Y0_High[wt]+dY_High*nY_High ax.plot([X0_High[wt],xmax_high,xmax_high,X0_High[wt],X0_High[wt]], [Y0_High[wt],Y0_High[wt],ymax_high,ymax_high,Y0_High[wt]], '-', label="HighT{0}".format(wt+1)) ax.plot(x[wt],y[wt],'x',ms=8,mew=2,label="WT{0}".format(wt+1)) else: for wt in range(len(x)): ax.plot(x[wt],y[wt],'x',ms=8,mew=2,label="WT{0}".format(wt+1)) # plt.legend(bbox_to_anchor=(1.05,1.015),frameon=False) ax.set_xlabel("x-location [m]") ax.set_ylabel("y-location [m]") fig.tight_layout
def load_files(self, LambdaFile=None, PitchFile=None, CPFile=None, CTFile=None, OperFile=None, base=None, suffix=''): if base is not None: LambdaFile = base + '_Lambda' + suffix + '.csv' PitchFile = base + '_Pitch' + suffix + '.csv' CPFile = base + '_CP' + suffix + '.csv' CTFile = base + '_CT' + suffix + '.csv' OperFile = base + '_Oper' + suffix + '.csv' self.PITCH = pd.read_csv(PitchFile, header=None).values.ravel() self.LAMBDA = pd.read_csv(LambdaFile, header=None).values.ravel() self.CP = pd.read_csv(CPFile, header=None).values self.CP[self.CP <= 0] = 0 if CTFile is not None: self.CT = pd.read_csv(CTFile, header=None).values self.CT[self.CT <= 0] = 0 else: self.CT = None if OperFile is not None: import weio self.Oper = weio.read(OperFile).toDataFrame() #print(self.Oper) self.WS = self.Oper['WS_[m/s]'].values self.Omega = self.Oper['RotSpeed_[rpm]'].values * 2 * np.pi / 60 self.RtAeroMxh = self.Oper['RtAeroMxh_[kN-m]'].values * 1000 self.OmegaRated = np.max(self.Omega) self.OmegaLow = 0.4 * self.OmegaRated self.WSRated = np.interp(self.OmegaRated * 0.98, self.Omega, self.WS) self.WSCutOff = 28 else: self.Oper = None self.computeWeights()
import weio # install from https://github.com/ebranlard/weio import matplotlib import matplotlib.pyplot as plt # Font size matplotlib.rcParams['font.size'] = 14 # Reading data and converting to a pandas DataFrame (table) df = weio.read('../weio/tests/example_files/FASTOutBin.outb').toDataFrame() print('Available columns:', df.columns) # Creating figure, setting figure size, and spacing/margins fig, ax = plt.subplots(1, 1, sharey=False, figsize=(6.4, 4.8)) fig.subplots_adjust(left=0.12, right=0.95, top=0.95, bottom=0.11, hspace=0.20, wspace=0.20) # Plotting with different styling and legend labels ax.plot(df['Time_[s]'], df['RtAeroCp_[-]'], '-', color='k', marker='', markersize=6, label='Cp') ax.plot(df['Time_[s]'], df['RtAeroCt_[-]'], '--',
def DF(self, FN): """ Reads a file with weio and return a dataframe """ return weio.read(os.path.join(MyDir, FN)).toDataFrame()
def FASTmodel2TNSB(ED_or_FST_file, nB=3, nShapes_twr=2, nShapes_bld=0, nSpan_twr=101, nSpan_bld=61, bHubMass=1, bNacMass=1, bBldMass=1, DEBUG=False, main_axis='x', bStiffening=True, assembly='manual', q=None, bTiltBeforeNac=False): nDOF = 1 + nShapes_twr + nShapes_bld * nB # +1 for Shaft if q is None: q = np.zeros((nDOF, 1)) # TODO, full account of q not done # --- Input data from ED file ext = os.path.splitext(ED_or_FST_file)[1] if ext.lower() == '.fst': FST = weio.read(ED_or_FST_file) rootdir = os.path.dirname(ED_or_FST_file) EDfile = os.path.join(rootdir, FST['EDFile'].strip('"')).replace('\\', '/') else: EDfile = ED_or_FST_file # Reading elastodyn file ED = weio.read(EDfile) rootdir = os.path.dirname(EDfile) bldfile = os.path.join(rootdir, ED['BldFile(1)'].strip('"')).replace('\\', '/') twrfile = os.path.join(rootdir, ED['TwrFile'].strip('"')).replace('\\', '/') twr = weio.read(twrfile) bld = weio.read(bldfile) ## --- Strucural and geometrical Inputs if main_axis == 'x': theta_tilt_y = ED[ 'ShftTilt'] * np.pi / 180 # NOTE: tilt has wrong orientation in FAST theta_cone_y = -ED['Precone(1)'] * np.pi / 180 r_ET_inE = np.array([[ED['TowerBsHt']], [0], [0]]) # NOTE: could be used to get hub height r_TN_inT = np.array([[ED['TowerHt'] - ED['TowerBsHt']], [0], [0]]) if bTiltBeforeNac: raise NotImplementedError() R_NS0 = np.eye(3) R_TN0 = R_y(theta_tilt_y) else: R_NS0 = R_y(theta_tilt_y) R_TN0 = np.eye(3) r_NGnac_inN = np.array([[ED['NacCMzn']], [0], [ED['NacCMxn']]]) r_NS_inN = np.array([[ED['Twr2Shft']], [0], [0]]) # S on tower axis r_SR_inS = np.array([[0], [0], [ED['OverHang']]]) # S and R r_SGhub_inS = np.array([[0], [0], [ED['OverHang'] + ED['HubCM']]]) # elif main_axis == 'z': theta_tilt_y = -ED[ 'ShftTilt'] * np.pi / 180 # NOTE: tilt has wrong orientation in FAST theta_cone_y = ED['Precone(1)'] * np.pi / 180 r_ET_inE = np.array([[0], [0], [ED['TowerBsHt']] ]) # NOTE: could be used to get hub height r_TN_inT = np.array([[0], [0], [ED['TowerHt'] - ED['TowerBsHt']]]) if bTiltBeforeNac: raise NotImplementedError() R_NS0 = np.eye(3) R_TN0 = R_y(theta_tilt_y) else: R_NS0 = R_y(theta_tilt_y) R_TN0 = np.eye(3) r_NGnac_inN = np.array([[ED['NacCMxn']], [0], [ED['NacCMzn']]]) r_NS_inN = np.array([[0], [0], [ED['Twr2Shft']]]) # S on tower axis r_SR_inS = np.array([[ED['OverHang']], [0], [0]]) # S and R r_SGhub_inS = np.array([[ED['OverHang'] + ED['HubCM']], [0], [0]]) # r_RGhub_inS = -r_SR_inS + r_SGhub_inS M_hub = ED['HubMass'] * bHubMass M_nac = ED['NacMass'] * bNacMass IR_hub = np.zeros((3, 3)) I0_nac = np.zeros((3, 3)) if main_axis == 'x': IR_hub[2, 2] = ED['HubIner'] + ED['GenIner'] * ED['GBRatio']**2 I0_nac[0, 0] = ED['NacYIner'] elif main_axis == 'z': IR_hub[0, 0] = ED['HubIner'] + ED['GenIner'] * ED['GBRatio']**2 I0_nac[2, 2] = ED['NacYIner'] IR_hub = IR_hub * bHubMass I0_nac = I0_nac * bNacMass # Inertias not at COG... IG_hub = fTranslateInertiaMatrix(IR_hub, M_hub, np.array([0, 0, 0]), r_RGhub_inS) IG_nac = fTranslateInertiaMatrixToCOG(I0_nac, M_nac, -r_NGnac_inN) # --------------------------------------------------------------------------------} ## --- Creating bodies # --------------------------------------------------------------------------------{ # Bld Blds = [] Blds.append( FASTBeamBody('blade', ED, bld, Mtop=0, nShapes=nShapes_bld, nSpan=nSpan_bld, main_axis=main_axis)) Blds[0].MM *= bBldMass for iB in range(nB - 1): Blds.append(copy.deepcopy(Blds[0])) # ShaftHub Body Sft = RigidBody('ShaftHub', M_hub, IG_hub, r_SGhub_inS) # Nacelle Body Nac = RigidBody('Nacelle', M_nac, IG_nac, r_NGnac_inN) M_rot = sum([B.Mass for B in Blds]) M_RNA = M_rot + Sft.Mass + Nac.Mass # Tower Body Twr = FASTBeamBody('tower', ED, twr, Mtop=M_RNA, nShapes=nShapes_twr, nSpan=nSpan_twr, main_axis=main_axis, bStiffening=bStiffening) #print('Stiffnening', bStiffening) #print('Ttw.KKg \n', Twr.KKg[6:,6:]) if DEBUG: print('IG_hub') print(IG_hub) print('IG_nac') print(IG_nac) print('I_gen_LSS', ED['GenIner'] * ED['GBRatio']**2) print('I_hub_LSS', ED['hubIner']) print('I_rot_LSS', nB * Blds[0].MM[5, 5]) print( 'I_tot_LSS', nB * Blds[0].MM[5, 5] + ED['hubIner'] + ED['GenIner'] * ED['GBRatio']**2) print('r_NGnac_inN', r_NGnac_inN.T) print('r_SGhub_inS', r_SGhub_inS.T) # --------------------------------------------------------------------------------} # --- Assembly # --------------------------------------------------------------------------------{ if assembly == 'manual': Struct = manual_assembly(Twr, Nac, Sft, Blds, q, r_ET_inE, r_TN_inT, r_NS_inN, r_SR_inS, main_axis=main_axis, theta_tilt_y=theta_tilt_y, theta_cone_y=theta_cone_y, DEBUG=DEBUG, bTiltBeforeNac=bTiltBeforeNac) else: Struct = auto_assembly(Twr, Nac, Sft, Blds, q, r_ET_inE, r_TN_inT, r_NS_inN, r_SR_inS, main_axis=main_axis, theta_tilt_y=theta_tilt_y, theta_cone_y=theta_cone_y, DEBUG=DEBUG, bTiltBeforeNac=bTiltBeforeNac) # --- Initial conditions omega_init = ED['RotSpeed'] * 2 * np.pi / 60 # rad/s psi_init = ED['Azimuth'] * np.pi / 180 # rad FA_init = ED['TTDspFA'] iPsi = Struct.iPsi nDOFMech = len(Struct.MM) q_init = np.zeros(2 * nDOFMech) # x2, state space if nShapes_twr > 0: q_init[0] = FA_init q_init[iPsi] = psi_init q_init[nDOFMech + iPsi] = omega_init Struct.q_init = q_init if DEBUG: print('Initial conditions:') print(q_init) # --- Useful data Struct.ED = ED return Struct
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.FASTInputFile(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
def writeFSTandDLL(FstT1Name, nWT): """ Write FST files for each turbine, with different ServoDyn files and DLL FST files, ServoFiles, and DLL files will be written next to their turbine 1 files, with name Ti. FstT1Name: absolute or relative path to the Turbine FST file """ FstT1Full = os.path.abspath(FstT1Name).replace('\\', '/') FstDir = os.path.dirname(FstT1Full) fst = weio.read(FstT1Name) SrvT1Name = fst['ServoFile'].strip('"') SrvT1Full = os.path.join(FstDir, SrvT1Name).replace('\\', '/') SrvDir = os.path.dirname(SrvT1Full) SrvT1RelFst = os.path.relpath(SrvT1Full, FstDir) if os.path.exists(SrvT1Full): srv = weio.read(SrvT1Full) DLLT1Name = srv['DLL_FileName'].strip('"') DLLT1Full = os.path.join(SrvDir, DLLT1Name) if os.path.exists(DLLT1Full): servo = True else: print( '[Info] DLL file not found, not copying servo and dll files ({})' .format(DLLT1Full)) servo = False else: print( '[Info] ServoDyn file not found, not copying servo and dll files ({})' .format(SrvT1Full)) servo = False #print(FstDir) #print(FstT1Full) #print(SrvT1Name) #print(SrvT1Full) #print(SrvT1RelFst) for i in np.arange(2, nWT + 1): FstName = insertTN(FstT1Name, i, nWT) if servo: # TODO handle the case where T1 not present SrvName = insertTN(SrvT1Name, i, nWT) DLLName = insertTN(DLLT1Name, i, nWT) DLLFullName = os.path.join(SrvDir, DLLName) print('') print('FstName: ', FstName) if servo: print('SrvName: ', SrvName) print('DLLName: ', DLLName) print('DLLFull: ', DLLFullName) # Changing main file if servo: fst['ServoFile'] = '"' + SrvName + '"' fst.write(FstName) if servo: # Changing servo file srv['DLL_FileName'] = '"' + DLLName + '"' srv.write(SrvName) # Copying dll forceCopyFile(DLLT1Full, DLLFullName)
def fastFarmTurbSimExtent(TurbSimFile, HubHeight, D, xWT, yWT, Cmeander=1.9, Chord_max=3, extent_X=1.2, extent_Y=1.2): """ Determines "Ambient Wind" box parametesr for FastFarm, based on a TurbSimFile ('bts') """ # --- TurbSim data ts = weio.read(TurbSimFile) iy, iz = ts.closestPoint(y=0, z=HubHeight) meanU = ts['u'][0, :, iy, iz].mean() dY_High = ts['y'][1] - ts['y'][0] dZ_High = ts['z'][1] - ts['z'][0] Z0_Low = ts['z'][0] Z0_High = ts['z'][0] # we start at lowest to include tower Width = ts['y'][-1] - ts['y'][0] Height = ts['z'][-1] - ts['z'][0] dT_High = ts['dt'] effSimLength = ts['t'][-1] - ts['t'][0] + Width / meanU # Desired resolution, rule of thumbs dX_High_desired = Chord_max dX_Low_desired = Cmeander * D * meanU / 150.0 dt_des = Cmeander * D / (10.0 * meanU) # --- High domain ZMax_High = HubHeight + extent_Y * D / 2.0 # high-box extent in x and y [D] Xdist_High = extent_X * D Ydist_High = extent_Y * D Zdist_High = ZMax_High - Z0_High # we include the tower X0_rel = Xdist_High / 2.0 Y0_rel = Ydist_High / 2.0 Length = effSimLength * meanU nx = int(round(effSimLength / dT_High)) dx_TS = Length / (nx - 1) dX_High = round(dX_High_desired / dx_TS) * dx_TS nX_High = int(round(Xdist_High / dX_High) + 1) nY_High = int(round(Ydist_High / dY_High) + 1) nZ_High = int(round(Zdist_High / dZ_High) + 1) # --- High extent per turbine nTurbs = len(xWT) X0_des = np.asarray(xWT) - X0_rel Y0_des = np.asarray(yWT) - Y0_rel X0_High = np.around(np.round(X0_des / dX_High) * dX_High, 3) Y0_High = np.around(np.round(Y0_des / dY_High) * dY_High, 3) # --- Low domain dT_Low = round(dt_des / dT_High) * dT_High dx_des = dX_Low_desired dy_des = dX_Low_desired dz_des = dX_Low_desired X0_Low = min(xWT) - 2 * D Y0_Low = -Width / 2 dX_Low = round(dx_des / dX_High) * dX_High dY_Low = round(dy_des / dY_High) * dY_High dZ_Low = round(dz_des / dZ_High) * dZ_High Xdist = max(xWT) + 8.0 * D - X0_Low # Maximum extent Ydist = Width Zdist = Height nX_Low = int(Xdist / dX_Low) + 1 nY_Low = int(Ydist / dY_Low) + 1 nZ_Low = int(Zdist / dZ_Low) + 1 if (nX_Low * dX_Low > Xdist): nX_Low = nX_Low - 1 if (nY_Low * dY_Low > Ydist): nY_Low = nY_Low - 1 if (nZ_Low * dZ_Low > Zdist): nZ_Low = nZ_Low - 1 d = dict() d['DT'] = np.around(dT_Low, 3) d['DT_High'] = np.around(dT_High, 3) d['NX_Low'] = int(nX_Low) d['NY_Low'] = int(nY_Low) d['NZ_Low'] = int(nZ_Low) d['X0_Low'] = np.around(X0_Low, 3) d['Y0_Low'] = np.around(Y0_Low, 3) d['Z0_Low'] = np.around(Z0_Low, 3) d['dX_Low'] = np.around(dX_Low, 3) d['dY_Low'] = np.around(dY_Low, 3) d['dZ_Low'] = np.around(dZ_Low, 3) d['NX_High'] = int(nX_High) d['NY_High'] = int(nY_High) d['NZ_High'] = int(nZ_High) # --- High extent info for turbine outputs d['dX_High'] = np.around(dX_High, 3) d['dY_High'] = np.around(dY_High, 3) d['dZ_High'] = np.around(dZ_High, 3) d['X0_High'] = X0_High d['Y0_High'] = Y0_High d['Z0_High'] = np.around(Z0_High, 3) return d
def test_FASTWnd(self): F = weio.read(os.path.join(MyDir, 'FASTWnd.wnd')) F.test_ascii(bCompareWritesOnly=True, bDelete=True)
def test_Bladed_case2_indiv(self): F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$12')) DF = F.toDataFrame() self.assertEqual(DF.shape, (10, 14)) self.assertEqual(DF.columns[0], 'Time [s]') self.assertEqual(DF.columns[1], 'POW2 [W]') self.assertAlmostEqual(DF['POW2 [W]'].values[-1], 1940463.0) F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$25')) DF = F.toDataFrame() self.assertEqual(DF.shape, (10, 17)) self.assertEqual(DF.columns[0], 'Time [s]') self.assertEqual(DF.columns[1], '-15.0m-MXT [Nm]') self.assertAlmostEqual(DF['-15.0m-MXT [Nm]'].values[-1], 1587526.625) F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$69')) DF = F.toDataFrame() self.assertEqual(DF.shape, (10, 7)) self.assertEqual(DF.columns[0], 'Time [s]') self.assertEqual(DF.columns[1], 'Foundation Mx [Nm]') self.assertAlmostEqual(DF['Foundation Mx [Nm]'].values[-1], 1587236.375) F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$37')) DF = F.toDataFrame() self.assertEqual(DF.shape, (522, 6)) self.assertEqual(DF.columns[0], 'Time [s]') self.assertEqual(DF.columns[1], 'Simulation Time [s]') self.assertAlmostEqual(DF['State with largest error [N]'].values[-1], 9.0) # NOTE: this binary file is detected as ascii, and the reading fails.. F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2_fail.$55')) DF = F.toDataFrame() self.assertEqual(DF.shape, (50, 1)) self.assertEqual(DF.columns[0], 'Step size histogram [N]') #self.assertTrue(np.isnan(DF['Step size histogram [N]'].values[-1])) self.assertEqual(DF['Step size histogram [N]'].values[-1], 0.0) # NOTE: this one is properly dected as binary F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$55')) DF = F.toDataFrame() self.assertEqual(DF.shape, (50, 1)) self.assertEqual(DF.columns[0], 'Step size histogram [N]') self.assertEqual(DF['Step size histogram [N]'].values[-1], 0) F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$46')) DF = F.toDataFrame() self.assertEqual(DF.shape, (10, 13)) self.assertEqual(DF.columns[1], '5.0m-Water particle velocity in X direction [m/s]') self.assertEqual( DF['5.0m-Water particle velocity in X direction [m/s]'].values[-1], -0.25) F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$06')) DF = F.toDataFrame() self.assertEqual(DF.shape, (10, 5)) self.assertEqual(DF.columns[1], 'Generator torque [Nm]') self.assertEqual(DF['Generator torque [Nm]'].values[-1], 12852.1953125) F = weio.read(os.path.join(MyDir, 'Bladed_out_binary_case2.$23')) DF = F.toDataFrame() self.assertEqual(DF.shape, (10, 9)) self.assertEqual(DF.columns[1], 'Stationary hub Mx [Nm]') self.assertEqual(DF['Stationary hub Mx [Nm]'].values[-1], 1112279.375)
turbine['R'] = 63 turbine['rho_air'] = 1.225 g = 9.81 # --- Reading aerodynamic data for the turbine PITCH = pd.read_csv('PITCH_data.csv', header=-1).values LAMBDA = pd.read_csv('LAMBDA_data.csv', header=-1).values CP = pd.read_csv('CP_data.csv', header=-1).values CT = pd.read_csv('CT_data.csv', header=-1).values # Create the interpolant for CP and CT, CP(pitch,lambda) (same interface interp2d) turbine['fCP'] = interp2d_pairs(PITCH, LAMBDA, CP, kind='cubic') turbine['fCT'] = interp2d_pairs(PITCH, LAMBDA, CT, kind='cubic') # --- Reading in some "measuremensts" or "simulation" # --- Removing units from columns df = weio.read(InputFile).toDataFrame() df.columns = [v.split('_[')[0] for v in df.columns.values] time = df['Time'].values genspeed = df['GenSpeed'].values * 2 * np.pi / 60 # converted to rad/s rotspeed = df['RotSpeed'].values * 2 * np.pi / 60 # converted to rad/s thrust = df['RotThrust'] * 1000 gentq = df['GenTq'] * 1000 * 97 # Convert to rot torque azimuth = df['Azimuth'] # deg windspeed = df['Wind1VelX'] pitch = df['BldPitch3'] rottorq = df['RotTorq'] * 1000 rottorq2 = df['RtAeroMxh'] thrust2 = df['RtAeroFxh'] # --- Evaluate the interpolant on each pairs of x and y values F = Taero(windspeed, pitch, rotspeed, turbine['R'], turbine['rho_air'],