def main(test=False): # --- Reading a existing AD file, just as a template, we'll replace things in it templateADFile = os.path.join(MyDir,'../../../data/NREL5MW/data/Airfoils/Cylinder1.dat') ADpol = weio.read(templateADFile) # --- Creating a Polar object from Cl-Cd data polarFile = os.path.join(MyDir,'../data/DU21_A17.csv') p=weio.read(polarFile).toDataFrame().values polar= Polar(np.nan, p[:,0],p[:,1],p[:,2],p[:,3]) (alpha0,alpha1,alpha2,cnSlope,cn1,cn2,cd0,cm0)=polar.unsteadyParams() # --- Updating the AD polar file # Setting unsteady parameters ADpol['Re'] = 1.0000 # TODO UNKNOWN if np.isnan(alpha0): ADpol['alpha0'] = 0 else: ADpol['alpha0'] = np.around(alpha0, 4) ADpol['alpha1'] = np.around(alpha1, 4) # TODO approximate ADpol['alpha2'] = np.around(alpha2, 4) # TODO approximate ADpol['C_nalpha'] = np.around(cnSlope ,4) ADpol['Cn1'] = np.around(cn1, 4) # TODO verify ADpol['Cn2'] = np.around(cn2, 4) ADpol['Cd0'] = np.around(cd0, 4) ADpol['Cm0'] = np.around(cm0, 4) # Setting polar PolarTable = np.column_stack((polar.alpha,polar.cl,polar.cd,polar.cm)) ADpol['NumAlf'] = polar.cl.shape[0] ADpol['AFCoeff'] = np.around(PolarTable, 5) if not test: filename='_Polar_out.dat.ignore' ADpol.write(filename) #print('Writing polar to file:',filename,' thick={}'.format(t)) return ADpol, polar
def FLEX2SteadyBEM(BladeFile, ProfileFile, cone, tilt, r_hub, rho=1.225, nB=3): import welib.weio as weio pro = weio.read(ProfileFile) bld = weio.read(BladeFile).toDataFrame() bld.columns = [v.split('_[')[0] for v in bld.columns.values] # TODO #rho=1.225; #r_hub=2.253; #nB = 3 #cone = 5 #tilt = 6; # TODO r = bld['r'].values + r_hub chord = bld['Chord'].values twist = bld['AeroTwist'].values thick = bld['RelThickness'].values polars = [] ProfileSet = bld['ProfileSet'].astype(int) for iset, thick in zip(ProfileSet, thick): PSet = pro.ProfileSets[iset - 1] AOA = PSet.polars[0][:, 0] CL = np.column_stack([v[:, 1] for v in PSet.polars]) CD = np.column_stack([v[:, 2] for v in PSet.polars]) CM = np.column_stack([v[:, 3] for v in PSet.polars]) fCL = interp1d(PSet.thickness, CL, axis=1) fCD = interp1d(PSet.thickness, CD, axis=1) fCM = interp1d(PSet.thickness, CM, axis=1) myCL = fCL(thick) myCD = fCD(thick) myCM = fCM(thick) polar = np.zeros(PSet.polars[0].shape) polar[:, 0] = AOA polar[:, 1] = myCL polar[:, 2] = myCD polar[:, 3] = myCM polars.append(polar) return nB, cone, r, chord, twist, polars, rho
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 simulate_py(self, model, p, time=None, refOut=None): """ Perform non-linear simulation based on a model (python package) generated by yams_sympy """ from welib.system.mech_system import MechSystem # --- Reference simulation if refOut is not None: df = weio.read(fstFilename.replace('.fst', '.out')).toDataFrame() #time = np.linspace(0,50,5000) time = df['Time_[s]'].values elif time is None: raise Exception( 'Time vector must be provided if no reference simulation is given' ) # --- Checking package info info = model.info() nDOFExpected = info['nq'] print('DOFs:', self.DOFname, 'Model:', info['name'], 'nDOF:', nDOFExpected) if len(self.DOFname) != nDOFExpected: raise Exception('Inconsistency in number of DOFs') # --- Initial conditions q0 = self.q0 qd0 = self.qd0 print('q0 :', q0) print('qd0:', qd0) # --- Non linear u = dict() for key in info['su']: # TODO get it from ref simulation u[key] = lambda t: 0 t = 0 MM = model.mass_matrix(q0, p) forcing = model.forcing(t, q0, qd0, p, u) # --- integrate non-linear system fM = lambda x: model.mass_matrix(x, p) fF = lambda t, x, xd: model.forcing(t, x, xd, p=p, u=u) sysNL = MechSystem(fM, F=fF, x0=q0, xdot0=qd0) resNL = sysNL.integrate(time, method='RK45') return resNL, sysNL
def initFromSimulation(KF, measFile, nUnderSamp=1, tRange=None, colMap=None, timeCol='Time_[s]', dataDict=None): """" - Open a simulation result file - Use dt to discretize the KF - Define clean values of measurements and states based on simulation - Define sigmas from the std of the clean signals dataDict: additional data provided for ColMap """ import welib.fast.fastlib as fastlib import welib.weio as weio # --- Loading "Measurements" df = weio.read(measFile).toDataFrame() df = df.iloc[::nUnderSamp, :] # reducing sampling if tRange is not None: df = df[(df[timeCol] >= tRange[0]) & (df[timeCol] <= tRange[1])] # reducing time range time = df[timeCol].values dt = (time[-1] - time[0]) / (len(time) - 1) KF.df = fastlib.remap_df(df, colMap, bColKeepNewOnly=False, dataDict=dataDict) # --- KF.discretize(dt, method='exponential') KF.setTimeVec(time) KF.setCleanValues(KF.df) # --- Estimate sigmas from measurements KF.sigX_c, KF.sigY_c = KF.sigmasFromClean(factor=1)
def main(runSim=True, runFAST=False): model = get_model( 'F2T1RNA', mergeFndTwr=False, linRot=False, yaw='zero', tilt='fixed', tiltShaft=True, rot_elastic_type= 'SmallRot', #rot_elastic_type='Body', 'Body' or 'SmallRot' orderMM=1, orderH=1, twrDOFDir=['x', 'y', 'x', 'y' ], # Order in which the flexible DOF of the tower are set ) # --- Compute Kane's equations of motion model.kaneEquations(Mform='TaylorExpanded') EOM = model.to_EOM() # --- Additional substitution extraSubs = model.shapeNormSubs # shape functions normalized to unity EOM.subs(extraSubs) # --- Small angle approximations EOM.smallAngleApprox(model.twr.vcList, order=2) EOM.smallAngleApprox([theta_tilt, phi_y], order=1) EOM.simplify() # --- Separate EOM into mass matrix and forcing EOM.mass_forcing_form() # EOM.M and EOM.F # --- Linearize equation of motions EOM.linearize(noAcc=True) # EOM.M0, EOM.K0, EOM.C0, EOM.B0 # --- Export equations replaceDict = {'theta_tilt': ('tilt', None)} EOM.savePython(folder='./', prefix='_', replaceDict=replaceDict) EOM.saveTex(folder='./', prefix='_', variables=['M', 'F', 'M0', 'K0', 'C0', 'B0']) # --- Run non linear and linear simulation using a FAST model as input if runSim: # --- Import the python module that was generated model_pkg = importlib.import_module('_F2T1RNA') # --- Load the wind turbine model, and extract relevant parameters "p" MyDir = os.path.dirname(__file__) #fstFilename = os.path.join(MyDir, '../../../data/NREL5MW/Main_Onshore_OF2.fst') fstFilename = os.path.join( MyDir, '../examples/_F2T1RNA_SmallAngle/Main_Spar_ED.fst') from welib.yams.windturbine import FASTWindTurbine WT = FASTWindTurbine(fstFilename, twrShapes=[0, 2], nSpanTwr=50) p = WT.yams_parameters() # --- Perform time integration if os.path.exists(fstFilename.replace('.fst', '.out')): import welib.weio as weio dfFS = weio.read(fstFilename.replace('.fst', '.out')).toDataFrame() time = dfFS['Time_[s]'].values else: time = np.linspace(0, 50, 1000) dfFS = None resNL, sysNL = WT.simulate_py(model_pkg, p, time) resLI, sysLI = WT.simulate_py_lin(model_pkg, p, time) dfNL = sysNL.toDataFrame(WT.channels, WT.FASTDOFScales) dfLI = sysLI.toDataFrame(WT.channels, WT.FASTDOFScales) # --- Simple Plot fig, axes = plt.subplots(3, 1, sharey=False, figsize=(6.4, 4.8)) # (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) for i, ax in enumerate(axes): chan = dfNL.columns[i + 1] ax.plot(dfNL['Time_[s]'], dfNL[chan], '-', label='non-linear') ax.plot(dfLI['Time_[s]'], dfLI[chan], '--', label='linear') if dfFS is not None: ax.plot(dfFS['Time_[s]'], dfFS[chan], 'k:', label='OpenFAST') ax.set_xlabel('Time [s]') ax.set_ylabel(chan) ax.tick_params(direction='in') ax.legend()
def main(test=False): """ Performs BEM simulations for different Pitch, RPM and Wind Speed. The wind turbine is initialized using a FAST input file (.fst) """ OutDir = os.path.join(MyDir, './') MainFASTFile = os.path.join(MyDir, '../../../data/NREL5MW/Main_Onshore_OF2.fst') OperatingConditionFile = os.path.join( MyDir, '../../../data/NREL5MW/NREL5MW_Oper.csv') # --- Reading turbine operating conditions, Pitch, RPM, WS (From FAST) df = weio.read(OperatingConditionFile).toDataFrame() Pitch = df['BldPitch_[deg]'].values Omega = df['RotSpeed_[rpm]'].values WS = df['WS_[m/s]'].values # -- Extracting information from a FAST main file and sub files nB, cone, r, chord, twist, polars, rho, KinVisc = FASTFile2SteadyBEM( MainFASTFile) BladeData = np.column_stack((r, chord, twist)) # --- Running BEM simulations for each operating conditions a0, ap0 = None, None # Initial guess for inductions, to speed up BEM dfOut = None if not os.path.exists(OutDir): os.makedirs(OutDir) # Loop on operating conditions for i, (V0, RPM, pitch), in enumerate(zip(WS, Omega, Pitch)): xdot = 0 # [m/s] u_turb = 0 # [m/s] BEM = SteadyBEM(RPM, pitch, V0, xdot, u_turb, nB, cone, r, chord, twist, polars, rho=rho, KinVisc=KinVisc, bTIDrag=False, bAIDrag=True, a_init=a0, ap_init=ap0) a0, ap0 = BEM.a, BEM.aprime # Export radial data to file if not test: filenameRadial = os.path.join( OutDir, '_BEM_ws{:02.0f}_radial.csv'.format(V0)) BEM.WriteRadialFile(filenameRadial) print('>>>', filenameRadial) dfOut = BEM.StoreIntegratedValues(dfOut) # --- Export integrated values to file filenameOut = os.path.join(OutDir, '_BEM_IntegratedValues.csv') if not test: dfOut.to_csv(filenameOut, sep='\t', index=False) print('>>>', filenameOut) print(dfOut.keys()) # --- Plot fig, ax = plt.subplots(1, 1, sharey=False, figsize=(6.4, 4.8)) # (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) ax.plot(dfOut['WS_[m/s]'].values, dfOut['AeroPower_[kW]'].values, label='Power') ax.set_xlabel('Wind speed [m/s]') ax.set_ylabel('[-]') ax.legend() ax.set_title('Steady BEM - Performance curve') ax.tick_params(direction='in') return dfOut
bAIDrag=True, a_init=a0, ap_init=ap0) a0, ap0 = BEM.a, BEM.aprime # Export radial data to file filenameRadial = os.path.join(OutDir, '_BEM_ws{:02.0f}_radial.csv'.format(V0)) BEM.WriteRadialFile(filenameRadial) print('>>>', filenameRadial) dfOut = BEM.StoreIntegratedValues(dfOut) # --- Export integrated values to file filenameOut = os.path.join(OutDir, '_BEM_IntegratedValues.csv') dfOut.to_csv(filenameOut, sep='\t', index=False) print('>>>', filenameOut) return dfOut if __name__ == "__main__": OutDir = './' MainFASTFile = '../../../_data/NREL5MW/Main_Onshore_OF2.fst' OperatingConditionFile = '../../../_data/NREL5MW/NREL5MW_Oper.csv' # --- Reading turbine operating conditions, Pitch, RPM, WS (From FAST) df = weio.read(OperatingConditionFile).toDataFrame() Pitch = df['BldPitch_[deg]'].values Omega = df['RotSpeed_[rpm]'].values WS = df['WS_[m/s]'].values dfOut = runParametricBEM_FAST(MainFASTFile, Pitch, Omega, WS)
def simulate(fstFilename, model_name, sims, sim_name): # --- p, WT = FAST2StructureInputs(fstFilename, model_name) print('>>>>>',p['tilt']) import importlib model= importlib.import_module('_py.{}'.format(model_name)) MM_ED = readEDSummaryFile(fstFilename.replace('.fst','.ED.sum')) lin = readFSTLin(fstFilename.replace('.fst','.1.lin')) # freq_d2, zeta2, Q2, freq02 = eigA(lin['A']) # --- Reference simulation df=weio.read(fstFilename.replace('.fst','.out')).toDataFrame() #time = np.linspace(0,50,5000) time = df['Time_[s]'].values # --- Initial conditions nDOFExpected=np.sum([int(s) for s in model_name if s.isdigit()]) print('DOFs:', WT.DOFname, 'Model:',model_name, 'nDOF:',nDOFExpected ) if len(WT.DOFname)!=nDOFExpected: raise Exception('Inconsistency in number of DOFs') q0 = WT.q0 qd0 = WT.qd0 print('q0 :',q0) print('qd0:',qd0) q0l = WT.q0*0 qd0l= WT.qd0*0 DOFs = WT.activeDOFs print('q0l :',q0l) print('qd0l:',qd0l) # --- Evaluate linear structural model u0=dict() # Inputs at operating points u0['T_a']= 0 # thrust at operating point # TODO u0['M_y_a']= 0 # aerodynamic tilting moment at operating point u0['M_z_a']= 0 #+0*np.sin(0.1*t) # aerodynamic "yawing" moment u0['F_B']= 0 # Buoyancy t(at operating point M_lin = model.M_lin(q0l,p) C_lin = model.C_lin(q0l,qd0l,p,u0) K_lin = model.K_lin(q0l,qd0l,p,u0) B_lin = model.B_lin(q0l,qd0l,p,u0) # --- Print linearized mass damping # print('--------------------') # print('Linear Mass Matrix: ') # print(M_lin) # print('--------------------') # print('Linear Damping Matrix: ') # print(C_lin) # print('--------------------') # print('Linear Stifness Matrix: ') # print(K_lin) # print('--------------------') # print('Linear RHS: ') # print(B_lin) # --- Non linear u=dict() u['T_a']= lambda t: 0 #+0*np.sin(0.1*t) # Thrust as function of time # TODO u['M_y_a']= lambda t: 0 #+0*np.sin(0.1*t) # aerodynamic tilting moment u['M_z_a']= lambda t: 0 #+0*np.sin(0.1*t) # aerodynamic "yawing" moment u['F_B']= lambda t: 0 #+0*np.sin(0.1*t) # Thrust as function of time # TODO #u['F_B']= lambda t: (p['M_F'] + p['M_RNA'] + p['MM_T'][0,0])*p['g'] t=0 MM = model.mass_matrix(q0,p) forcing = model.forcing(t,q0,qd0,p,u) #print(WT.WT_rigid) print('--------------------') print('Mass Matrix: ') print(MM) print(MM_ED) M_Ref=MM_ED.copy() print(' Rel Error') MM [np.abs(MM )<1e-1]=1 MM_ED[np.abs(MM_ED)<1e-1]=1 M_Ref[np.abs(M_Ref)<1e-1]=1 print(np.around(np.abs((MM_ED-MM))/M_Ref*100,1)) print('--------------------') print('Forcing: ') print(forcing) if sims is False: return p, WT, None, None, None, None # --- integrate non-linear system fM = lambda x: model.mass_matrix(x, p) fF = lambda t,x,xd: model.forcing(t, x, xd, p=p, u=u) sysNL = MechSystem(fM, F=fF, x0=q0, xdot0=qd0 ) resNL=sysNL.integrate(time, method='RK45') # --- integrate linear system fF = lambda t,x,xd: np.array([0]*len(q0)) sysLI = MechSystem(M=M_lin, K=K_lin, C=C_lin, F=fF, x0=q0, xdot0=qd0) resLI=sysLI.integrate(time, method='RK45') # **options): # --- Convert results to dataframe and save to file channels = WT.channels DOFscales= WT.FASTDOFScales dfNL = sysNL.toDataFrame(WT.channels, DOFscales) dfLI = sysLI.toDataFrame(WT.channels, DOFscales) sysNL.save(fstFilename.replace('.fst','_NonLinear.csv'), WT.channels, DOFscales) sysLI.save(fstFilename.replace('.fst','_Linear.csv'), WT.channels, DOFscales) try: print('>>>>> A') print(sysLI.A) print('>>>>> A') print(lin['A']) freq_d, zeta, Q, freq0 = eigA(sysLI.A) freq_d2, zeta2, Q2, freq02 = eigA(lin['A']) print(freq_d) print(freq_d2) print(zeta) print(zeta2) except: print('>> No lin') for c in dfNL.columns: if c.find('[deg]')>1: if np.max(dfNL[c])>90: dfNL[c]= np.mod(dfNL[c],360) dfLI[c]= np.mod(dfLI[c],360) # --- Plot # sys.plot() legDone=False nDOF=sysNL.nDOF fig,axes = plt.subplots(nDOF, 2, sharey=False, sharex=True, figsize=(12.0,8.0)) # (6.4,4.8) axes = axes.reshape(nDOF,2) fig.subplots_adjust(left=0.08, right=0.99, top=0.98, bottom=0.06, hspace=0.07, wspace=0.18) for idof, dof in enumerate(WT.activeDOFs): # Positions chan=channels[idof] axes[idof,0].plot(dfNL['Time_[s]'], dfNL[chan], '-' , label='non-linear') axes[idof,0].plot(dfLI['Time_[s]'], dfLI[chan], '--' , label='linear') if chan in df.columns: axes[idof,0].plot(df['Time_[s]'], df[chan], 'k:' , label='OpenFAST') if not legDone: legDone=True axes[idof,0].legend(loc='upper right') axes[idof,0].tick_params(direction='in') axes[idof,0].set_ylabel(chan.replace('_',' ')) # Velocities vdof = idof+nDOF chan=channels[vdof] axes[idof,1].plot(dfNL['Time_[s]'], dfNL[chan], '-' , label='non-linear') axes[idof,1].plot(dfLI['Time_[s]'], dfLI[chan], '--' , label='linear') if chan in df.columns: axes[idof,1].plot(df['Time_[s]'], df[chan], 'k:' , label='OpenFAST') axes[idof,1].tick_params(direction='in') axes[idof,1].set_ylabel(chan.replace('_',' ')) if idof==nDOF-1: axes[idof,0].set_xlabel('Time [s]') axes[idof,1].set_xlabel('Time [s]') fig.savefig('_figs/{}.png'.format(sim_name)) plt.show() return p, WT, sysNL, dfNL, sysLI, dfLI
def test_BladeBeam_SID(self): """ In this test we use: - Beam properties from the Blade of the NREL5MW - Shape functions determined using a FEM beam representation (see welib/yams/tests/test_sid.py) - We test for the gyroscopic and centrifugal matrix Gr, Ge, Oe - Beam along z axis """ np.set_printoptions(linewidth=300, precision=9) # --- Read data from NREL5MW Blade edFile = os.path.join(MyDir, './../../../data/NREL5MW/data/NREL5MW_ED.dat') parentDir = os.path.dirname(edFile) ed = weio.FASTInputFile(edFile) TipRad = ed['TipRad'] HubRad = ed['HubRad'] BldLen = TipRad - HubRad BldFile = ed['BldFile(1)'].replace('"', '') BldFile = os.path.join(parentDir, BldFile) bld = weio.FASTInputFile(BldFile).toDataFrame() z = bld['BlFract_[-]'] * BldLen + HubRad m = bld['BMassDen_[kg/m]'] nSpan = len(z) # --- Shape function taken from FEM shapeFile = os.path.join( MyDir, '../../../data/NREL5MW/NREL5MW_Blade_FEM_Modes.csv') shapes = weio.read(shapeFile).toDataFrame() nShapes = 2 PhiU = np.zeros((nShapes, 3, nSpan)) # Shape s_G = np.zeros((3, nSpan)) main_axis = 'z' if main_axis == 'z': # Mode 1 PhiU[0, 0, :] = shapes['U1x'] PhiU[0, 1, :] = shapes['U1y'] # Mode 2 PhiU[1, 0, :] = shapes['U2x'] PhiU[1, 1, :] = shapes['U2y'] s_G[2, :] = z # --- Testing for straight COG s_span = z jxxG = z * 0 + m # NOTE: unknown MM, Gr, Ge, Oe, Oe6 = GMBeam(s_G, s_span, m, PhiU, jxxG=jxxG, bUseIW=True, main_axis=main_axis, split_outputs=False, rot_terms=True) MM_ref = np.array( [[ 1.684475202e+04, 0.000000000e+00, 0.000000000e+00, 0.000000000e+00, 3.707069074e+05, -0.000000000e+00, 1.976263092e+03, -6.366476591e+00 ], [ 0.000000000e+00, 1.684475202e+04, 0.000000000e+00, -3.707069074e+05, 0.000000000e+00, 0.000000000e+00, 4.082717323e+00, 2.915664880e+03 ], [ 0.000000000e+00, 0.000000000e+00, 1.684475202e+04, 0.000000000e+00, -0.000000000e+00, 0.000000000e+00, 0.000000000e+00, 0.000000000e+00 ], [ 0.000000000e+00, -3.707069074e+05, 0.000000000e+00, 1.225603507e+07, -0.000000000e+00, -0.000000000e+00, -1.817970390e+02, -1.200295386e+05 ], [ 3.707069074e+05, 0.000000000e+00, -0.000000000e+00, -0.000000000e+00, 1.225603507e+07, -0.000000000e+00, 8.737268222e+04, -2.574073558e+02 ], [ -0.000000000e+00, 0.000000000e+00, 0.000000000e+00, -0.000000000e+00, -0.000000000e+00, 1.684475202e+04, 0.000000000e+00, 0.000000000e+00 ], [ 1.976263092e+03, 4.082717323e+00, 0.000000000e+00, -1.817970390e+02, 8.737268222e+04, 0.000000000e+00, 8.364646779e+02, -9.586291225e-04 ], [ -6.366476591e+00, 2.915664880e+03, 0.000000000e+00, -1.200295386e+05, -2.574073558e+02, 0.000000000e+00, -9.586291225e-04, 1.351321852e+03 ]]) np.testing.assert_allclose(MM[0:3, 0:3], MM_ref[0:3, 0:3], rtol=1e-4) np.testing.assert_allclose(MM[0:3, 3:6], MM_ref[0:3, 3:6], rtol=1e-4) np.testing.assert_allclose(MM[0:3, 6:], MM_ref[0:3, 6::], rtol=1e-4) np.testing.assert_allclose(MM[3:6, 3:6], MM_ref[3:6, 3:6], rtol=1e-4) np.testing.assert_allclose(MM[3:6, 6:], MM_ref[3:6, 6:], rtol=1e-4) np.testing.assert_allclose(MM[6::, 6::], MM_ref[6:, 6:], rtol=1e-4) np.testing.assert_allclose(MM, MM.T, rtol=1e-4) np.testing.assert_allclose(Gr[0][0, :], [0, 0, -174817], rtol=1e-5) np.testing.assert_allclose(Gr[0][1, :], [0, 0, -363.7477], rtol=1e-5) np.testing.assert_allclose(Gr[1][0, :], [0, 0, 514.944], rtol=1e-5) np.testing.assert_allclose(Gr[1][1, :], [0, 0, -240127], rtol=1e-5) np.testing.assert_allclose(Ge[0][1, :], [0, 0, 2087.767], rtol=1e-5) np.testing.assert_allclose(Ge[1][0, :], [0, 0, -2087.767], rtol=1e-5) np.testing.assert_allclose(Oe6[0][:], [0, 0, 0, 0, 181.8739, 87408.6], rtol=1e-5) np.testing.assert_allclose(Oe6[1][:], [0, 0, 0, 0, 120063, -257.472], rtol=1e-5)
def test_TowerBeam_SID(self): """ In this test we use: - Beam properties from the Tower of the NREL5MW - Shape functions determined using a FEM beam representation (see welib/FEM/tests/test_beam_linear_element.py) - We test for the gyroscopic and centrifugal matrix Gr, Ge, Oe - Beam along z axis """ np.set_printoptions(linewidth=300, precision=9) # --- Read data from NREL5MW tower TowerHt = 87.6 TowerBs = 0 TwrFile = os.path.join( MyDir, './../../../data/NREL5MW/data/NREL5MW_ED_Tower_Onshore.dat') twr = weio.FASTInputFile(TwrFile).toDataFrame() z = twr['HtFract_[-]'] * (TowerHt - TowerBs) m = twr['TMassDen_[kg/m]'] nSpan = len(z) # --- Shape function taken from FEM shapeFile = os.path.join( MyDir, '../../../data/NREL5MW/NREL5MW_Tower_Onshore_FEM_Modes.csv') shapes = weio.read(shapeFile).toDataFrame() nShapes = 2 PhiU = np.zeros((nShapes, 3, nSpan)) # Shape PhiV = np.zeros((nShapes, 3, nSpan)) # Slope s_G = np.zeros((3, nSpan)) main_axis = 'z' if main_axis == 'x': PhiU[0, 1, :] = shapes['U1'] # along y PhiU[1, 2, :] = shapes['U2'] # along z PhiV[0, 1, :] = shapes['V1'] # along y PhiV[1, 2, :] = shapes['V2'] # along z s_G[0, :] = z elif main_axis == 'z': PhiU[0, 0, :] = shapes['U1'] # along x PhiU[1, 1, :] = shapes['U2'] # along y PhiV[0, 0, :] = shapes[ 'V1'] # along x (around theta y) # TODO double check convention PhiV[1, 1, :] = shapes[ 'V2'] # along y (around theta x # TODO double check convention s_G[2, :] = z # --- Testing for straight COG s_span = z jxxG = z * 0 + m # NOTE: unknown #MM = GMBeam(s_G, s_span, m, PhiU, jxxG=jxxG, bUseIW=True, main_axis='x') # Ref uses IW_xm Mxx, Mtt, Mxt, Mtg, Mxg, Mgg, Gr, Ge, Oe, Oe6 = GMBeam( s_G, s_span, m, PhiU, jxxG=jxxG, bUseIW=True, main_axis=main_axis, split_outputs=True, rot_terms=True) MM, Gr, Ge, Oe, Oe6 = GMBeam(s_G, s_span, m, PhiU, jxxG=jxxG, bUseIW=True, main_axis=main_axis, split_outputs=False, rot_terms=True) MM_ref = np.array( # NOTE: this is onshore tower [[ 3.474602316e+05, 0.000000000e+00, 0.000000000e+00, 0.000000000e+00, 1.322633773e+07, -0.000000000e+00, 1.046938838e+05, 0.000000000e+00 ], [ 0.000000000e+00, 3.474602316e+05, 0.000000000e+00, -1.322633773e+07, 0.000000000e+00, 0.000000000e+00, 0.000000000e+00, 1.046938838e+05 ], [ 0.000000000e+00, 0.000000000e+00, 3.474602316e+05, 0.000000000e+00, -0.000000000e+00, 0.000000000e+00, 0.000000000e+00, 0.000000000e+00 ], [ 0.000000000e+00, -1.322633773e+07, 0.000000000e+00, 7.182575651e+08, -0.000000000e+00, -0.000000000e+00, 0.000000000e+00, -6.440479129e+06 ], [ 1.322633773e+07, 0.000000000e+00, -0.000000000e+00, -0.000000000e+00, 7.182575651e+08, -0.000000000e+00, 6.440479129e+06, 0.000000000e+00 ], [ -0.000000000e+00, 0.000000000e+00, 0.000000000e+00, -0.000000000e+00, -0.000000000e+00, 3.474602316e+05, 0.000000000e+00, 0.000000000e+00 ], [ 1.046938838e+05, 0.000000000e+00, 0.000000000e+00, 0.000000000e+00, 6.440479129e+06, 0.000000000e+00, 6.145588498e+04, 0.000000000e+00 ], [ 0.000000000e+00, 1.046938838e+05, 0.000000000e+00, -6.440479129e+06, 0.000000000e+00, 0.000000000e+00, 0.000000000e+00, 6.145588498e+04 ]]) np.testing.assert_allclose(Mxx, MM_ref[0:3, 0:3], rtol=1e-4) np.testing.assert_allclose(Mxt, MM_ref[0:3, 3:6], rtol=1e-4) np.testing.assert_allclose(Mxg, MM_ref[0:3, 6::], rtol=1e-4) np.testing.assert_allclose(Mtt, MM_ref[3:6, 3:6], rtol=1e-4) np.testing.assert_allclose(Mtg, MM_ref[3:6, 6:], rtol=1e-4) np.testing.assert_allclose(Mgg, MM_ref[6:, 6:], rtol=1e-4) np.testing.assert_allclose(Gr[0][0, :], [0, 0, -12945834], rtol=1e-5) np.testing.assert_allclose(Gr[1][1, :], [0, 0, -12945834], rtol=1e-5) np.testing.assert_allclose(Ge[0][1, :], [0, 0, 122911], rtol=1e-5) np.testing.assert_allclose(Ge[1][0, :], [0, 0, -122911], rtol=1e-5) np.testing.assert_allclose(Oe6[0][:], [0, 0, 0, 0, 0, 6472917], rtol=1e-5) np.testing.assert_allclose(Oe6[1][:], [0, 0, 0, 0, 6472917, 0], rtol=1e-5)
def FAST2SID(ed_file, Imodes_twr=None, Imodes_bld=None): import welib.weio as weio import os # --- Read data from ElastoDyn file parentDir = os.path.dirname(ed_file) ed = weio.read(ed_file) # edfile = # if twr: twr_sid = None if Imodes_twr is not None: TowerHt = ed['TowerHt'] TowerBs = ed['TowerBsHt'] TwrFile = ed['TwrFile'].replace('"', '') TwrFile = os.path.join(parentDir, TwrFile) twr = weio.FASTInputFile(TwrFile).toDataFrame() z = twr['HtFract_[-]'] * (TowerHt - TowerBs) m = twr['TMassDen_[kg/m]'] # mu EIy = twr['TwFAStif_[Nm^2]'] EIz = twr[ 'TwSSStif_[Nm^2]'] # TODO actually EIx, but FEM beams along x # --- Create Beam FEM model # Derived parameters A = m * 0 + 100 # Area Kv = m * 0 + 100 # Saint Venant torsion E = 214e9 # Young modulus Iy = EIy / E Iz = EIz / E xNodes = np.zeros((3, len(m))) xNodes[2, :] = z twr_sid = Beam2SID(xNodes, Imodes_twr, m, Iy, Iz, Kv=Kv, A=A, E=E) bld_sid = None if Imodes_bld is not None: TipRad = ed['TipRad'] HubRad = ed['HubRad'] BldLen = TipRad - HubRad BldFile = ed['BldFile(1)'].replace('"', '') BldFile = os.path.join(parentDir, BldFile) bld = weio.FASTInputFile(BldFile).toDataFrame() z = bld['BlFract_[-]'] * BldLen + HubRad m = bld['BMassDen_[kg/m]'] # mu EIy = bld['FlpStff_[Nm^2]'] EIz = bld['EdgStff_[Nm^2]'] # TODO actually EIx, but FEM beams along x phi = bld['PitchAxis_[-]'] / (180) * np.pi # --- Derived parameters phi = np.concatenate( ([0], np.diff(phi))) #% add phi_abs(1) to pitch angle A = m * 0 + 100 # Area Kv = m * 0 + 100 # Saint Venant torsion E = 214e9 # Young modulus Iy = EIy / E Iz = EIz / E xNodes = np.zeros((3, len(m))) xNodes[2, :] = z bld_sid = Beam2SID(xNodes, Imodes_bld, m, Iy, Iz, Kv=Kv, A=A, E=E, phi=phi) return twr_sid, bld_sid
def FASTWindTurbine(fstFilename, main_axis='z', nSpanTwr=None, twrShapes=None, nSpanBld=None, algo=''): """ """ # TODO TODO TODO Harmonize with TNSB.py # --- Reading main OpenFAST files ext = os.path.splitext(fstFilename)[1] FST = weio.read(fstFilename) rootdir = os.path.dirname(fstFilename) EDfile = os.path.join(rootdir, FST['EDFile'].strip('"')).replace('\\', '/') 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('\\', '/') # TODO SubDyn, MoorDyn, BeamDyn r_ET_inE = np.array([0, 0, ED['TowerBsHt']]) r_TN_inT = np.array([0, 0, ED['TowerHt'] - ED['TowerBsHt']]) # Basic geometries for nacelle theta_tilt_y = -ED[ 'ShftTilt'] * np.pi / 180 # NOTE: tilt has wrong orientation in FAST R_NS = R_y(theta_tilt_y) # Rotation fromShaft to Nacelle r_NS_inN = np.array([0, 0, ED['Twr2Shft']]) # Shaft start in N r_SR_inS = np.array([ED['OverHang'], 0, 0]) # Rotor center in S r_SGhub_inS = np.array([ED['HubCM'], 0, 0]) + r_SR_inS # Hub G in S r_NR_inN = r_NS_inN + R_NS.dot(r_SR_inS) # Rotor center in N r_NGnac_inN = np.array([ED['NacCMxn'], 0, ED['NacCMzn']]) # Nacelle G in N r_RGhub_inS = -r_SR_inS + r_SGhub_inS if main_axis == 'x': raise NotImplementedError() # --- Hub (defined using point N and nacelle coord as ref) M_hub = ED['HubMass'] JxxHub_atR = ED['HubIner'] hub = RigidBody('Hub', M_hub, (JxxHub_atR, 0, 0), s_OG=r_SGhub_inS, R_b2g=R_NS, s_OP=r_SR_inS, r_O=r_NS_inN) # --- Generator (Low speed shaft) (defined using point N and nacelle coord as ref) gen = RigidBody('Gen', 0, (ED['GenIner'] * ED['GBRatio']**2, 0, 0), s_OG=[0, 0, 0], R_b2g=R_NS, r_O=r_NS_inN) # --- Nacelle (defined using point N and nacelle coord as ref) M_nac = ED['NacMass'] JyyNac_atN = ED['NacYIner'] # Inertia of nacelle at N in N nac = RigidBody('Nac', M_nac, (0, JyyNac_atN, 0), r_NGnac_inN, s_OP=[0, 0, 0]) # --- Blades bldFile = weio.read(bldfile) m = bldFile['BldProp'][:, 3] jxxG = m # NOTE: unknown nB = ED['NumBl'] bld = np.zeros(nB, dtype=object) bld[0] = FASTBeamBody(ED, bldFile, Mtop=0, main_axis=main_axis, jxxG=jxxG, spanFrom0=False, nSpan=nSpanBld) for iB in range(nB - 1): bld[iB + 1] = copy.deepcopy(bld[0]) bld[iB + 1].R_b2g for iB, B in enumerate(bld): B.name = 'bld' + str(iB + 1) psi_B = -iB * 2 * np.pi / len(bld) if main_axis == 'x': R_SB = R_z(0 * np.pi + psi_B) # TODO psi offset and psi0 elif main_axis == 'z': R_SB = R_x(0 * np.pi + psi_B) # TODO psi0 R_SB = np.dot(R_SB, R_y(ED['PreCone({})'.format(iB + 1)] * np.pi / 180)) # blade2shaft B.R_b2g = R_SB # --- Blades (with origin R, using N as "global" ref) blades = rigidBlades(bld, r_O=[0, 0, 0]) blades.pos_global = r_NR_inN blades.R_b2g = R_NS # --- Rotor = Hub + Blades (with origin R, using N as global ref) rot = blades.combine(hub, R_b2g=R_NS, r_O=blades.pos_global) rot.name = 'rotor' rotgen = rot.combine(gen, R_b2g=R_NS, r_O=blades.pos_global) #print(rotgen) # --- RNA RNA = rot.combine(gen).combine(nac, r_O=[0, 0, 0]) RNA.name = 'RNA' #print(RNA) M_RNA = RNA.mass # --- Fnd (defined wrt ground/MSL "E") # print(FST.keys()) M_fnd = ED['PtfmMass'] r_OGfnd_inF = np.array([ED['PtfmCMxt'], ED['PtfmCMyt'], ED['PtfmCMzt']]) r_OT_inF = np.array([0, 0, ED['PtfmRefzt']]) r_TGfnd_inF = -r_OT_inF + r_OGfnd_inF fnd = RigidBody('fnd', M_fnd, (ED['PtfmRIner'], ED['PtfmPIner'], ED['PtfmYIner']), s_OG=r_TGfnd_inF, r_O=r_OT_inF) # --- Twr twrFile = weio.read(twrfile) twr = FASTBeamBody(ED, twrFile, Mtop=M_RNA, main_axis='z', bAxialCorr=False, bStiffening=True, shapes=twrShapes, nSpan=nSpanTwr, algo=algo) # TODO options twr_rigid = twr.toRigidBody() twr_rigid.pos_global = r_ET_inE # --- Full WT rigid body equivalent, with point T as ref RNAb = copy.deepcopy(RNA) RNAb.pos_global = r_TN_inT + r_ET_inE RNAb.R_b2g = np.eye(3) WT_rigid = RNAb.combine(twr_rigid, r_O=r_ET_inE).combine(fnd, r_O=r_ET_inE) # --- Degrees of freedom DOFs = [] DOFs += [{ 'name': 'x', 'active': ED['PtfmSgDOF'][0] in ['t', 'T'], 'q0': ED['PtfmSurge'], 'qd0': 0, 'q_channel': 'PtfmSurge_[m]', 'qd_channel': 'QD_Sg_[m/s]' }] DOFs += [{ 'name': 'y', 'active': ED['PtfmSwDOF'][0] in ['t', 'T'], 'q0': ED['PtfmSway'], 'qd0': 0, 'q_channel': 'PtfmSway_[m]', 'qd_channel': 'QD_Sw_[m/s]' }] DOFs += [{ 'name': 'z', 'active': ED['PtfmHvDOF'][0] in ['t', 'T'], 'q0': ED['PtfmHeave'], 'qd0': 0, 'q_channel': 'PtfmHeave_[m]', 'qd_channel': 'QD_Hv_[m/s]' }] DOFs += [{ 'name': '\phi_x', 'active': ED['PtfmRDOF'][0] in ['t', 'T'], 'q0': ED['PtfmRoll'] * np.pi / 180, 'qd0': 0, 'q_channel': 'PtfmRoll_[deg]', 'qd_channel': 'QD_R_[rad/s]' }] DOFs += [{ 'name': '\phi_y', 'active': ED['PtfmPDOF'][0] in ['t', 'T'], 'q0': ED['PtfmPitch'] * np.pi / 180, 'qd0': 0, 'q_channel': 'PtfmPitch_[deg]', 'qd_channel': 'QD_P_[rad/s]' }] DOFs += [{ 'name': '\phi_z', 'active': ED['PtfmYDOF'][0] in ['t', 'T'], 'q0': ED['PtfmYaw'] * np.pi / 180, 'qd0': 0, 'q_channel': 'PtfmYaw_[deg]', 'qd_channel': 'QD_Y_[rad/s]' }] DOFs += [{ 'name': 'q_FA1', 'active': ED['TwFADOF1'][0] in ['t', 'T'], 'q0': ED['TTDspFA'], 'qd0': 0, 'q_channel': 'Q_TFA1_[m]', 'qd_channel': 'QD_TFA1_[m/s]' }] DOFs += [{ 'name': 'q_SS1', 'active': ED['TwSSDOF1'][0] in ['t', 'T'], 'q0': ED['TTDspSS'], 'qd0': 0, 'q_channel': 'Q_TSS1_[m]', 'qd_channel': 'QD_TSS1_[m/s]' }] DOFs += [{ 'name': 'q_FA2', 'active': ED['TwFADOF2'][0] in ['t', 'T'], 'q0': ED['TTDspFA'], 'qd0': 0, 'q_channel': 'Q_TFA2_[m]', 'qd_channel': 'QD_TFA2_[m/s]' }] DOFs += [{ 'name': 'q_SS2', 'active': ED['TwSSDOF2'][0] in ['t', 'T'], 'q0': ED['TTDspSS'], 'qd0': 0, 'q_channel': 'Q_TSS1_[m]', 'qd_channel': 'QD_TSS1_[m/s]' }] DOFs += [{ 'name': '\\theta_y', 'active': ED['YawDOF'][0] in ['t', 'T'], 'q0': ED['NacYaw'] * np.pi / 180, 'qd0': 0, 'q_channel': 'NacYaw_[deg]', 'qd_channel': 'QD_Yaw_[rad/s]' }] DOFs += [{ 'name': '\\psi', 'active': ED['GenDOF'][0] in ['t', 'T'], 'q0': ED['Azimuth'] * np.pi / 180, 'qd0': ED['RotSpeed'] * 2 * np.pi / 60, 'q_channel': 'Azimuth_[deg]', 'qd_channel': 'RotSpeed_[rpm]' }] DOFs += [{ 'name': '\\nu', 'active': ED['DrTrDOF'][0] in ['t', 'T'], 'q0': 0, 'qd0': 0, 'q_channel': 'Q_DrTr_[rad]', 'qd_channel': 'QD_DrTr_[rad/s]' }] DOFs += [{ 'name': 'q_Fl1', 'active': ED['FlapDOF1'][0] in ['t', 'T'], 'q0': ED['OOPDefl'], 'qd0': 0, 'q_channel': 'Q_B1F1_[m]', 'qd_channel': 'QD_B1F1_[m/s]' }] DOFs += [{ 'name': 'q_Ed1', 'active': ED['EdgeDOF'][0] in ['t', 'T'], 'q0': ED['IPDefl'], 'qd0': 0, 'q_channel': 'Q_B1E1_[m]', 'qd_channel': 'QD_B1E1_[m/s]' }] # ---------------------- DEGREES OF FREEDOM -------------------------------------- # False FlapDOF1 - First flapwise blade mode DOF (flag) # False FlapDOF2 - Second flapwise blade mode DOF (flag) # False EdgeDOF - First edgewise blade mode DOF (flag) # ---------------------- INITIAL CONDITIONS -------------------------------------- # 0 OoPDefl - Initial out-of-plane blade-tip displacement (meters) # 0 IPDefl - Initial in-plane blade-tip deflection (meters) # --- Return WT = WindTurbineStructure() WT.bld = bld # origin at R WT.hub = hub # origin at S WT.rot = rot # origin at R, rigid body bld+hub WT.rotgen = rotgen # origin at R, rigid body bld+hub+genLSS WT.gen = gen # origin at S WT.nac = nac # origin at N WT.twr = twr # origin at T WT.fnd = fnd # origin at T WT.RNA = RNA # origin at N, rigid body bld+hub+gen+nac WT.WT_rigid = WT_rigid # rigid wind turbine body, origin at MSL WT.DOF = DOFs #WT.r_ET_inE = #WT.r_TN_inT WT.r_NS_inN = r_NS_inN WT.r_NR_inN = r_NR_inN WT.r_SR_inS = r_SR_inS WT.ED = ED return WT
def simulate_py_lin(self, model, p, time=None, refOut=None, qop=None, qdop=None): """ Perform linear simulation based on a model (python package) generated by yams_sympy """ from welib.system.mech_system import MechSystem # --- Reference simulation if refOut is not None: df = weio.read(fstFilename.replace('.fst', '.out')).toDataFrame() #time = np.linspace(0,50,5000) time = df['Time_[s]'].values elif time is None: raise Exception( 'Time vector must be provided if no reference simulation is given' ) # --- Checking package info info = model.info() nDOFExpected = info['nq'] print('DOFs:', self.DOFname, 'Model:', info['name'], 'nDOF:', nDOFExpected) if len(self.DOFname) != nDOFExpected: raise Exception('Inconsistency in number of DOFs') # --- Operating point if qop is None: qop = self.q0 * 0 # TODO if qdop is None: qdop = self.qd0 * 0 # TODO uop = dict() # Inputs at operating points for key in info['su']: # TODO get it from ref simulation uop[key] = 0 # --- Initial conditions (of pertubations) q0 = self.q0 - qop qd0 = self.qd0 - qdop # --- Evaluate linear structural model at operating point M_lin = model.M_lin(qop, p) C_lin = model.C_lin(qop, qdop, p, uop) K_lin = model.K_lin(qop, qdop, p, uop) B_lin = model.B_lin(qop, qdop, p, uop) # --- Integrate linear system fF = lambda t, x, xd: np.array([0] * len(qop)) # TODO sysLI = MechSystem(M=M_lin, K=K_lin, C=C_lin, F=fF, x0=q0, xdot0=qd0) resLI = sysLI.integrate(time, method='RK45') # **options): # --- Print linearized mass damping # print('--------------------') # print('Linear Mass Matrix: ') # print(M_lin) # print('--------------------') # print('Linear Damping Matrix: ') # print(C_lin) # print('--------------------') # print('Linear Stifness Matrix: ') # print(K_lin) # print('--------------------') # print('Linear RHS: ') # print(B_lin) return resLI, sysLI
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): """ Returns the following structure Twr : BeamBody Shft: RigiBody Nac : RigidBody Blds: List of BeamBodies MM, KK, DD : mass, stiffness and damping matrix of full system """ 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 = translateInertiaMatrix(IR_hub, M_hub, np.array([0, 0, 0]), r_RGhub_inS) IG_nac = translateInertiaMatrixToCOG(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 main(): # --- Parameters InFile = '../../../data/NREL5MW/Main_Onshore_OF2_DriveTrainTorsion.fst' model = 2 # 1=1DOF (theta_r), 2= 2DOF (theta_r, nu), 22: (theta_r, theta_g_LSS), 3: (theta_r, theta_g) # --- Turbine parameters OutFile = InFile.replace('.fst', '.outb') WT = FASTWindTurbine(InFile) nGear = WT.ED['GBRatio'] K_DT = WT.ED['DTTorSpr'] D_DT = WT.ED['DTTorDmp'] Jr_LSS = WT.rot.inertia[0, 0] * 1.000 # bld + hub, LSS Jg_LSS = WT.gen.inertia[0, 0] # gen, LSS Jg_HSS = WT.gen.inertia[0, 0] / nGear**2 print('Jr', Jr_LSS) print('Jg', Jg_HSS) print('N ', nGear) # --- Read output from openfast df = weio.read(OutFile).toDataFrame() time = df['Time_[s]'].values Q_r = df['RtAeroMxh_[N-m]'].values Q_g = df['GenTq_[kN-m]'].values * 1000 Q_LSS = df['RotTorq_[kN-m]'].values * 1000 q_r = df['Q_GeAz_[rad]'] # NOTE: weird initial condition q_DT = df['Q_DrTr_[rad]'] qd_r = df['QD_GeAz_[rad/s]'] qd_DT = df['QD_DrTr_[rad/s]'] # qdd_r = df['QD2_GeAz_[rad/s^2]'] # qdd_DT = df['QD2_DrTr_[rad/s^2]'] GenSpeed = df['GenSpeed_[rpm]'] * 2 * np.pi / 60 GenSpeed_LSS = GenSpeed / nGear RotSpeed = df['RotSpeed_[rpm]'] * 2 * np.pi / 60 NuSpeed = qd_DT # Generator from rotor + drivetrain torsion (note: jumps due to modulo) q_g = (q_r + q_DT) * nGear qd_g = (qd_r + qd_DT) * nGear q_g_LSS = (q_r + q_DT) qd_g_LSS = (qd_r + qd_DT) # Drive train torsion torque Q_DT = K_DT * q_DT + D_DT * qd_DT # --- Drivetrain speed # theta_gen = theta_rot - Nu # fig,ax = plt.subplots(1, 1, sharey=False, figsize=(6.4,4.8)) # (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) # ax.plot(GenSpeed_LSS , label='Gen') # ax.plot(RotSpeed , label='Rot') # # ax.plot(NuSpeed , label='Nu') # ax.plot(RotSpeed-NuSpeed ,'--' , label='Gen=Nu+Rot') # ax.set_xlabel('') # ax.set_ylabel('') # ax.legend() # --- Setup Model if model == 1: # One degree of freedom on LSS (torsion omitted): theta F = Q_r - Q_g * nGear F = F.reshape((1, -1)) M = np.array([[Jr_LSS + Jg_LSS]]) K = K_DT * np.array([[0]]) D = D_DT * np.array([[0]]) elif model == 2: # Everything on LSS: theta, and nu = theta_r-theta_g_LSS # [Jr + Jg N^2, -Jg N^2 ][theta_r]_dd + [0, 0][theta_r] + [0, 0][theta_r]_d = Q_r - Q_g N # [ -Jg N^2 , Jg N^2][ nu ]_dd + [0, K][ nu ] + [0, B][ nu ]_d = Q_g N F = np.column_stack((Q_r - Q_g * nGear, Q_g * nGear)).T M = np.array([[Jr_LSS + Jg_LSS, -Jg_LSS], [-Jg_LSS, Jg_LSS]]) K = K_DT * np.array([[0, 0], [0, 1]]) D = D_DT * np.array([[0, 0], [0, 1]]) elif model == 22: # Everything on LSS: theta_r, and theta_g_LSS # [Jr , 0 ][theta_r ]_dd + [ K,-K][theta_r ] + [ B,-B][theta_r ]_d = Q_r # [0 ,Jg N^2][theta_g_LSS]_dd + [-K, K][theta_g_LSS] + [-B, B][theta_g_LSS]_d = Q_g N F = np.column_stack((Q_r, -Q_g * nGear)).T M = np.array([[Jr_LSS, 0], [0, Jg_LSS]]) K = K_DT * np.array([[1, -1], [-1, 1]]) D = D_DT * np.array([[1, -1], [-1, 1]]) elif model == 3: # theta_r on LSS, theta_g on HSS # [Jr , 0 ][theta_r]_dd + [ K ,-K/N ][theta_r] + [ B, -B/N ][theta_r]_d = Q_r # [0, Jg ][theta_g]_dd + [-K/N, K/N^2][theta_g] + [-B/N, B/N^2][theta_g]_d = Q_g F = np.column_stack((Q_r, -Q_g)).T M = np.array([[Jr_LSS, 0], [0, Jg_HSS]]) K = K_DT * np.array([[1, -1 / nGear], [-1 / nGear, 1 / nGear**2]]) D = D_DT * np.array([[1, -1 / nGear], [-1 / nGear, 1 / nGear**2]]) # --- Define a system and perform time integration q0 = np.zeros(2) qd0 = np.zeros(2) q0[0] = q_r[0] qd0[0] = qd_r[0] if model == 22: q0[1] = q_g_LSS[0] qd0[1] = qd_g_LSS[0] if model == 3: q0[1] = q_g[0] qd0[1] = qd_g[0] if model == 1: sys = MechSystem(M, D, K, F=(time, F), x0=q0[0:1], xdot0=qd0[0:1]) else: sys = MechSystem(M, D, K, F=(time, F), x0=q0, xdot0=qd0) print(sys) res = sys.integrate(time, method='LSODA') # **options): # res=sys.integrate(time, method='RK45') # **options): # sys.plot() # --- Plot states fig, axes = plt.subplots(sys.nStates, 1, sharey=False, figsize=(6.4, 4.8)) # (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) axes[0].plot(sys.res.t, np.mod(sys.res.y[0, :], 2 * np.pi)) axes[0].plot(sys.res.t, q_r) axes[0].set_ylabel(r'$\theta_r$ [rad]') if model == 1: axes[1].plot(sys.res.t, sys.res.y[1, :]) axes[1].plot(sys.res.t, qd_r) axes[1].set_ylabel(r'$\dot{\theta}_r$ [rad/s]') else: axes[2].plot(sys.res.t, sys.res.y[2, :]) axes[2].plot(sys.res.t, RotSpeed, '--') # qd_r is not good here... axes[2].set_ylabel(r'$\dot{\theta}_r$ [rad/s]') if model == 2: axes[1].plot(sys.res.t, sys.res.y[1, :]) axes[1].plot(sys.res.t, q_DT) axes[1].set_ylabel(r'$\nu$ [rad]') axes[3].plot(sys.res.t, sys.res.y[3, :]) axes[3].plot(sys.res.t, qd_DT) axes[3].set_ylabel(r'$\dot{\nu}$ [rad/s]') elif model == 22: axes[1].plot(sys.res.t, np.mod(sys.res.y[1, :], 2 * np.pi)) axes[1].plot(sys.res.t, q_g_LSS) axes[3].plot(sys.res.t, sys.res.y[3, :]) axes[3].plot(sys.res.t, GenSpeed_LSS) elif model == 3: axes[1].plot(sys.res.t, np.mod(sys.res.y[1, :], 2 * np.pi)) axes[1].plot(sys.res.t, q_g) axes[3].plot(sys.res.t, sys.res.y[3, :]) axes[3].plot(sys.res.t, GenSpeed) axes[-1].set_xlabel('Time [s]')
def FASTmodel2FNSB( FST_file, shapes_sub=[0, 4], nShapes_bld=0, nSpan_sub=101, nSpan_bld=61, bHubMass=1, bNacMass=1, bBldMass=1, DEBUG=False, main_axis='x', bStiffening=True, assembly='manual', q=None, bTiltBeforeNac=False, spanFrom0=True, # TODO for legacy, we keep this for now.. bladeMassExpected=None): """ Returns the following structure Fnd : BeamBody Shft: RigiBody Nac : RigidBody Blds: List of BeamBodies MM, KK, DD : mass, stiffness and damping matrix of full system shapes_sub: 0:ux, 1:uy, 2:uz, 3:vx, 4:vy, 5:vz E.g. for surge and pitch (ux, vy): [0,4] NOTE/TODO: compare this with "windturbine.py" """ # --- Read fst file ext = os.path.splitext(FST_file)[1] if ext.lower() != '.fst': raise Exception('FNSB requires a fst file as input') FST = weio.read(FST_file) rootdir = os.path.dirname(FST_file) EDfile = os.path.join(rootdir, FST['EDFile'].strip('"')).replace('\\', '/') subfile = os.path.join(rootdir, FST['SubFile'].strip('"')).replace('\\', '/') # 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) # Reading SubDyn file sub = weio.read(subfile) nShapes_sub = len(shapes_sub) graph = sub.toGraph() # NOTE: this is repeated in bodies.py... graph.divideElements(sub['NDiv']) graph.sortNodesBy('z') df = graph.nodalDataFrame() zBot = np.min(df['z']) zTop = np.max(df['z']) RayleighCoeff = None DampMat = None if sub['GuyanDampMod'] == 1: # Rayleigh Damping RayleighCoeff = sub['RayleighDamp'] #if RayleighCoeff[0]==0: # damp_zeta=omega*RayleighCoeff[1]/2. elif sub['GuyanDampMod'] == 2: # Full matrix DampMat = sub['GuyanDampMatrix'] DampMat = DampMat[np.ix_(shapes, shapes)] nB = ED['NumBl'] nDOF = 1 + nShapes_sub + nShapes_bld * nB # +1 for Shaft if q is None: q = np.zeros((nDOF, 1)) # TODO, full account of q not done ## --- 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_EF_inE = np.array([[zBot], [0], [0]]) r_ET_inE = np.array([[ED['TowerBsHt']], [0], [0]]) r_FT_inF = np.array([[ED['TowerBsHt'] - zBot], [0], [0]]) 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_EF_inE = np.array([[0], [0], [zBot]]) r_ET_inE = np.array([[0], [0], [ED['TowerBsHt']]]) r_FT_inF = np.array([[0], [0], [ED['TowerBsHt'] - zBot]]) 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 = translateInertiaMatrix(IR_hub, M_hub, np.array([0, 0, 0]), r_RGhub_inS) IG_nac = translateInertiaMatrixToCOG(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, spanFrom0=spanFrom0, massExpected=bladeMassExpected)) # NOTE: legacy spanfrom0 Blds[0].MM *= bBldMass for iB in range(nB - 1): Blds.append(copy.deepcopy(Blds[0])) # IMPORTANT FOR RNA set R_b2g for iB, B in enumerate(Blds): B.name = 'bld' + str(iB + 1) psi_B = -iB * 2 * np.pi / len(Blds) if main_axis == 'x': R_SB = R_z(0 * np.pi + psi_B) # TODO psi offset and psi0 elif main_axis == 'z': R_SB = R_x(0 * np.pi + psi_B) # TODO psi0 R_SB = np.dot(R_SB, R_y(ED['PreCone({})'.format(iB + 1)] * np.pi / 180)) # blade2shaft B.R_b2g = R_SB # 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 # Substructure Body Fnd = FASTBeamBody('substructure', ED, sub, Mtop=M_RNA, shapes=shapes_sub, nSpan=nSpan_sub, main_axis=main_axis, bStiffening=bStiffening) #print(Fnd) #print('Fnd MM\n',Fnd.MM[6:,6:]) #print('Fnd KK\n',Fnd.KK[6:,6:]) # HACK here because doesn't handle this for now if sub['GuyanDampMod'] == 1: Fnd.DD[6:, 6:] = Fnd.MM[6:, 6:] * RayleighCoeff[0] + Fnd.KK[ 6:, 6:] * RayleighCoeff[1] #print('Stiffnening', bStiffening) #print('Ttw.KKg \n', Twr.KKg[6:,6:]) if DEBUG: print('HubMass', Sft.Mass) print('NacMass', Nac.Mass) print('RotMass', M_rot) print('RNAMass', M_RNA) 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 # --------------------------------------------------------------------------------{ r_ET_inE = r_EF_inE r_TN_inT = r_FT_inF + r_TN_inT # assume that F and T are in system E here if assembly == 'manual': Struct = manual_assembly(Fnd, 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(Fnd, 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'] Surge_init = ED['PtfmSurge'] Sway_init = ED['PtfmSway'] Heave_init = ED['PtfmHeave'] Roll_init = ED['PtfmRoll'] * np.pi / 180 Pitch_init = ED['PtfmPitch'] * np.pi / 180 Yaw_init = ED['PtfmYaw'] * np.pi / 180 iPsi = Struct.iPsi nDOFMech = len(Struct.MM) q_init = np.zeros(2 * nDOFMech) # x2, state space if nShapes_sub > 0: sub_init = np.array([ Surge_init, Sway_init, Heave_init, Roll_init, Pitch_init, Yaw_init ]) for iDOF, iDOFfull in enumerate(shapes_sub): q_init[iDOF] = sub_init[iDOFfull] 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 Struct.DampMat = DampMat Struct.RayleighCoeff = RayleighCoeff return Struct
def BeamDyn2Hawc2(BD_mainfile, BD_bladefile, H2_htcfile, H2_stfile, bodyname, A=None, E=None, G=None, FPM=False): """ FPM: fully populated matrix, if True, use the FPM format of hawc2 """ # --- Read BeamDyn files bdLine = weio.read(BD_mainfile).toDataFrame() bd = weio.read(BD_bladefile).toDataFrame() # --- Extract relevant info prop = bd['BeamProperties'] kp_x = bdLine['kp_xr_[m]'].values kp_y = bdLine['kp_yr_[m]'].values kp_z = bdLine['kp_zr_[m]'].values twist = bdLine['initial_twist_[deg]'].values r_bar = prop['Span'].values K = np.zeros((6,6),dtype='object') M = np.zeros((6,6),dtype='object') for i in np.arange(6): for j in np.arange(6): K[i,j]=prop['K{}{}'.format(i+1,j+1)].values M[i,j]=prop['M{}{}'.format(i+1,j+1)].values # Map 6x6 data to "beam" data EA, EIx, EIy, kxsGA, kysGA, GKt, x_C, y_C, x_S, y_S, theta_p, theta_s = K66toProps(K) m, Ixi, Iyi, Ip, x_G, y_G, theta_i = M66toProps(M) print('kxGA {:e}'.format(np.mean(kxsGA))) print('kyGA {:e}'.format(np.mean(kysGA))) print('EA {:e}'.format(np.mean(EA))) print('EIx {:e}'.format(np.mean(EIx))) print('EIy {:e}'.format(np.mean(EIy))) print('GKt {:e}'.format(np.mean(GKt))) print('xC ',np.mean(x_C)) print('yC ',np.mean(y_C)) print('xS ',np.mean(x_S)) print('yS ',np.mean(y_S)) print('thetap',np.mean(theta_p)) print('thetas',np.mean(theta_s)) print('m ',np.mean(m)) print('Ixi ',np.mean(Ixi)) print('Iyi ',np.mean(Iyi)) print('Ip ',np.mean(Ip)) print('x_G ',np.mean(x_G)) print('y_G ',np.mean(y_G)) print('thetai',np.mean(theta_i)) # Convert to Hawc2 system if FPM: dfMeanLine , dfStructure = BeamDyn2Hawc2FPM_raw(r_bar, kp_x, kp_y, kp_z, twist, m, Ixi, Iyi, x_G, y_G, theta_i, x_C, y_C, theta_p, K) print(dfStructure.shape) else: dfMeanLine , dfStructure = BeamDyn2Hawc2_raw(r_bar, kp_x, kp_y, kp_z, twist, m, Ixi, Iyi, x_G, y_G, theta_i, EA, EIx, EIy, GKt, kxsGA, kysGA, x_C, y_C, theta_p, x_S, y_S, theta_s, A=None, E=None, G=None) # --- Rewrite st file with open(H2_stfile, 'w') as f: f.write('%i ; number of sets, Nset\n' % 1) f.write('-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n') f.write('#%i ; set number\n' % 1) if FPM: cols=['r','m_[kg/m]','x_cg_[m]','y_cg_[m]','ri_x_[m]','ri_y_[m]','pitch_[deg]','x_e_[m]','y_e_[m]','K11','K12','K13','K14','K15','K16','K22','K23','K24','K25','K26','K33','K34','K35','K36','K44','K45','K46','K55','K56','K66'] else: cols=['r','m','x_cg','y_cg','ri_x','ri_y','x_sh','y_sh','E','G','I_x','I_y','I_p','k_x','k_y','A','pitch','x_e','y_e'] f.write('\t'.join(['{:20s}'.format(s) for s in cols])+'\n') f.write('$%i %i\n' % (1, dfStructure.shape[0])) f.write('\n'.join('\t'.join('%19.13e' %x for x in y) for y in dfStructure.values)) # --- Rewrite st file def readToMarker(lines, marker, i, nMax=None, noException=False): l_sel=[] if nMax is None: nMax=len(lines) while i<nMax: line=lines[i] if line.replace(' ','').lower().find(marker)>=0: break l_sel.append(line.strip()) i+=1 if line.strip().replace(' ','').lower().find(marker)<0: if noException: return None, None, None else: raise Exception('Marker not found '+ marker) return l_sel, line, i with open(H2_htcfile, 'r') as f: lines_in = f.readlines() lines_out = [] bodyNotFound=True iBodyEnd=0 nBodies=0 while bodyNotFound and nBodies<10: _, line, iBodyStart = readToMarker(lines_in, 'beginmain_body',iBodyEnd) _, line, iBodyEnd = readToMarker(lines_in, 'endmain_body', iBodyStart) _, line, iBody = readToMarker(lines_in, 'name'+bodyname, iBodyStart, iBodyEnd, True) nBodies+=1 if line is None: iBody=-1 else: print('Body {} found between lines {} and {} '.format(bodyname, iBodyStart+1, iBodyEnd+1)) bodyNotFound=False if nBodies>=10: raise Exception('Body {} not found in file'.format(bodyname)) _, line, iC2Start = readToMarker(lines_in, 'beginc2_def', iBodyStart, iBodyEnd) _, line, iC2End = readToMarker(lines_in, 'endc2_def' , iC2Start, iBodyEnd) _, line, iTIStart = readToMarker(lines_in, 'begintimoschenko_input', iBodyStart, iBodyEnd) _, line, iTIEnd = readToMarker(lines_in, 'endtimoschenko_input' , iTIStart, iBodyEnd) simdir = os.path.dirname(H2_htcfile) H2_stfile_rel = os.path.relpath(H2_stfile, simdir) lines_out = lines_in[:iTIStart+1] lines_out += [' filename {};\n'.format(H2_stfile_rel)] if FPM: lines_out += [' FPM 1 ;\n'] # lines_out += [' FPM 0 ;\n'] lines_out += [' set 1 1 ;\n'] lines_out += lines_in[iTIEnd:iC2Start+1] lines_out += [' nsec {} ;\n'.format(dfMeanLine.shape[0])] for i, row in dfMeanLine.iterrows(): lines_out += [' sec {:4d}\t{:13.6e}\t{:13.6e}\t{:13.6e}\t{:13.6e};\n'.format(i+1, row['X_[m]'],row['Y_[m]'],row['Z_[m]'],row['Twist_[deg]'])] lines_out += lines_in[iC2End:] with open(H2_htcfile, 'w') as f: f.write(''.join(lines_out))