Exemple #1
0
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
Exemple #2
0
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
Exemple #3
0
    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)
Exemple #4
0
    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
Exemple #5
0
    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()
Exemple #7
0
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
Exemple #8
0
                      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
Exemple #10
0
    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)
Exemple #11
0
    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)
Exemple #12
0
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
Exemple #13
0
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
Exemple #14
0
    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
Exemple #15
0
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
Exemple #16
0
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]')
Exemple #17
0
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
Exemple #18
0
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))