def init_from_refBlade(self, refBlade): # Code take from rotor_geometry.py (RotorSE). It computes layup properties, independent of which turbine it is # Setup paths strucpath = refBlade.getStructPath() self.materials = Orthotropic2DMaterial.listFromPreCompFile(os.path.join(strucpath, 'materials.inp')) npts = refBlade.npts self.upperCS = [0]*npts self.lowerCS = [0]*npts self.websCS = [0]*npts self.profile = [0]*npts for i in range(npts): webLoc = [] istr = str(i) if refBlade.name == '3_35MW' or refBlade.name == '10MW' else str(i+1) self.upperCS[i], self.lowerCS[i], self.websCS[i] = CompositeSection.initFromPreCompLayupFile(os.path.join(strucpath, 'layup_' + istr + '.inp'), webLoc, self.materials , readLocW=True) self.profile[i] = Profile.initFromPreCompFile(os.path.join(strucpath, 'shape_' + istr + '.inp')) self.name = refBlade.name self.bladeLength = refBlade.bladeLength # self.eta = refBlade.r self.r = refBlade.r * refBlade.bladeLength self.chord = refBlade.chord_ref self.le_location = refBlade.le_location
# --- aero and structural analysis options --- rotor.nSector = 4 # (Int): number of sectors to divide rotor face into in computing thrust and power rotor.npts_coarse_power_curve = 20 # (Int): number of points to evaluate aero analysis at rotor.npts_spline_power_curve = 200 # (Int): number of points to use in fitting spline to power curve rotor.AEP_loss_factor = 1.0 # (Float): availability and other losses (soiling, array, etc.) rotor.drivetrainType = 'geared' # (Enum) rotor.nF = 5 # (Int): number of natural frequencies to compute rotor.dynamic_amplication_tip_deflection = 1.35 # (Float): a dynamic amplification factor to adjust the static deflection calculation # ---------------------- # --- materials and composite layup --- basepath = os.path.join(os.path.dirname(rotorse.__file__), '5MW_PreCompFiles') # basepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), '5MW_PrecompFiles') materials = Orthotropic2DMaterial.listFromPreCompFile( os.path.join(basepath, 'materials.inp')) ncomp = len(rotor.initial_str_grid) upper = [0] * ncomp lower = [0] * ncomp webs = [0] * ncomp profile = [0] * ncomp rotor.leLoc = np.array( [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.498, 0.497, 0.465, 0.447, 0.43, 0.411, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4 ] ) # (Array): array of leading-edge positions from a reference blade axis (usually blade pitch axis). locations are normalized by the local chord length. e.g. leLoc[i] = 0.2 means leading edge is 0.2*chord[i] from reference axis. positive in -x direction for airfoil-aligned coordinate system rotor.sector_idx_strain_spar = [
def configure_nrel5mw_turbine(turbine, wind_class='I', sea_depth=0.0): """ Inputs: rotor = RotorSE() nacelle = DriveSE() tower = TowerSE() wind_class : str ('I', 'III', 'Offshore' - selected wind class for project) sea_depth : float (sea depth if an offshore wind plant) """ # === Turbine === turbine.rho = 1.225 # (Float, kg/m**3): density of air turbine.mu = 1.81206e-5 # (Float, kg/m/s): dynamic viscosity of air turbine.shear_exponent = 0.2 # (Float): shear exponent turbine.hub_height = 90.0 # (Float, m): hub height turbine.turbine_class = 'I' # (Enum): IEC turbine class turbine.turbulence_class = 'B' # (Enum): IEC turbulence class class turbine.cdf_reference_height_wind_speed = 90.0 # (Float): reference hub height for IEC wind speed (used in CDF calculation) turbine.g = 9.81 # (Float, m/s**2): acceleration of gravity # ====================== # === rotor === # --- blade grid --- turbine.rotor.initial_aero_grid = np.array([ 0.02222276, 0.06666667, 0.11111057, 0.16666667, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.7, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724 ]) # (Array): initial aerodynamic grid on unit radius turbine.rotor.initial_str_grid = np.array([ 0.0, 0.00492790457512, 0.00652942887106, 0.00813095316699, 0.00983257273154, 0.0114340970275, 0.0130356213234, 0.02222276, 0.024446481932, 0.026048006228, 0.06666667, 0.089508406455, 0.11111057, 0.146462614229, 0.16666667, 0.195309105255, 0.23333333, 0.276686558545, 0.3, 0.333640766319, 0.36666667, 0.400404310407, 0.43333333, 0.5, 0.520818918408, 0.56666667, 0.602196371696, 0.63333333, 0.667358391486, 0.683573824984, 0.7, 0.73242031601, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724, 1.0 ]) # (Array): initial structural grid on unit radius turbine.rotor.idx_cylinder_aero = 3 # (Int): first idx in r_aero_unit of non-cylindrical section, constant twist inboard of here turbine.rotor.idx_cylinder_str = 14 # (Int): first idx in r_str_unit of non-cylindrical section turbine.rotor.hubFraction = 0.025 # (Float): hub location as fraction of radius # ------------------ # --- blade geometry --- turbine.rotor.r_aero = np.array([ 0.02222276, 0.06666667, 0.11111057, 0.2, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.64, 0.7, 0.83333333, 0.88888943, 0.93333333, 0.97777724 ]) # (Array): new aerodynamic grid on unit radius turbine.rotor.r_max_chord = 0.23577 # (Float): location of max chord on unit radius turbine.rotor.chord_sub = [ 3.2612, 4.5709, 3.3178, 1.4621 ] # (Array, m): chord at control points. defined at hub, then at linearly spaced locations from r_max_chord to tip turbine.rotor.theta_sub = [ 13.2783, 7.46036, 2.89317, -0.0878099 ] # (Array, deg): twist at control points. defined at linearly spaced locations from r[idx_cylinder] to tip turbine.rotor.precurve_sub = [ 0.0, 0.0, 0.0 ] # (Array, m): precurve at control points. defined at same locations at chord, starting at 2nd control point (root must be zero precurve) turbine.rotor.delta_precurve_sub = [ 0.0, 0.0, 0.0 ] # (Array, m): adjustment to precurve to account for curvature from loading turbine.rotor.sparT = [0.05, 0.047754, 0.045376, 0.031085, 0.0061398 ] # (Array, m): spar cap thickness parameters turbine.rotor.teT = [0.1, 0.09569, 0.06569, 0.02569, 0.00569 ] # (Array, m): trailing-edge thickness parameters turbine.rotor.bladeLength = 61.5 # (Float, m): blade length (if not precurved or swept) otherwise length of blade before curvature turbine.rotor.delta_bladeLength = 0.0 # (Float, m): adjustment to blade length to account for curvature from loading turbine.rotor.precone = 2.5 # (Float, deg): precone angle turbine.rotor.tilt = 5.0 # (Float, deg): shaft tilt turbine.rotor.yaw = 0.0 # (Float, deg): yaw error turbine.rotor.nBlades = 3 # (Int): number of blades # ------------------ # --- airfoil files --- import rotorse #basepath = os.path.join('5MW_files', '5MW_AFFiles') basepath = os.path.join('..', 'reference_turbines', 'nrel5mw', 'airfoils') # load all airfoils airfoil_types = [0] * 8 airfoil_types[0] = os.path.join(basepath, 'Cylinder1.dat') airfoil_types[1] = os.path.join(basepath, 'Cylinder2.dat') airfoil_types[2] = os.path.join(basepath, 'DU40_A17.dat') airfoil_types[3] = os.path.join(basepath, 'DU35_A17.dat') airfoil_types[4] = os.path.join(basepath, 'DU30_A17.dat') airfoil_types[5] = os.path.join(basepath, 'DU25_A17.dat') airfoil_types[6] = os.path.join(basepath, 'DU21_A17.dat') airfoil_types[7] = os.path.join(basepath, 'NACA64_A17.dat') # place at appropriate radial stations af_idx = [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7] n = len(af_idx) af = [0] * n for i in range(n): af[i] = airfoil_types[af_idx[i]] turbine.rotor.airfoil_files = af # (List): names of airfoil file # ---------------------- # --- control --- turbine.rotor.control.Vin = 3.0 # (Float, m/s): cut-in wind speed turbine.rotor.control.Vout = 25.0 # (Float, m/s): cut-out wind speed turbine.rotor.control.ratedPower = 5e6 # (Float, W): rated power turbine.rotor.control.minOmega = 0.0 # (Float, rpm): minimum allowed rotor rotation speed turbine.rotor.control.maxOmega = 12.0 # (Float, rpm): maximum allowed rotor rotation speed turbine.rotor.control.tsr = 7.55 # (Float): tip-speed ratio in Region 2 (should be optimized externally) turbine.rotor.control.pitch = 0.0 # (Float, deg): pitch angle in region 2 (and region 3 for fixed pitch machines) turbine.rotor.pitch_extreme = 0.0 # (Float, deg): worst-case pitch at survival wind condition turbine.rotor.azimuth_extreme = 0.0 # (Float, deg): worst-case azimuth at survival wind condition turbine.rotor.VfactorPC = 0.7 # (Float): fraction of rated speed at which the deflection is assumed to representative throughout the power curve calculation # ---------------------- # --- aero and structural analysis options --- turbine.rotor.nSector = 4 # (Int): number of sectors to divide rotor face into in computing thrust and power turbine.rotor.npts_coarse_power_curve = 20 # (Int): number of points to evaluate aero analysis at turbine.rotor.npts_spline_power_curve = 200 # (Int): number of points to use in fitting spline to power curve turbine.rotor.AEP_loss_factor = 1.0 # (Float): availability and other losses (soiling, array, etc.) turbine.rotor.drivetrainType = 'geared' # (Enum) turbine.rotor.nF = 5 # (Int): number of natural frequencies to compute turbine.rotor.dynamic_amplication_tip_deflection = 1.35 # (Float): a dynamic amplification factor to adjust the static deflection calculation # ---------------------- # --- materials and composite layup --- #basepath = os.path.join('5MW_files', '5MW_PrecompFiles') basepath = os.path.join('..', 'reference_turbines', 'nrel5mw', 'blade') materials = Orthotropic2DMaterial.listFromPreCompFile( os.path.join(basepath, 'materials.inp')) ncomp = len(turbine.rotor.initial_str_grid) upper = [0] * ncomp lower = [0] * ncomp webs = [0] * ncomp profile = [0] * ncomp turbine.rotor.leLoc = np.array( [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.498, 0.497, 0.465, 0.447, 0.43, 0.411, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4 ] ) # (Array): array of leading-edge positions from a reference blade axis (usually blade pitch axis). locations are normalized by the local chord length. e.g. leLoc[i] = 0.2 means leading edge is 0.2*chord[i] from reference axis. positive in -x direction for airfoil-aligned coordinate system turbine.rotor.sector_idx_strain_spar = [ 2 ] * ncomp # (Array): index of sector for spar (PreComp definition of sector) turbine.rotor.sector_idx_strain_te = [ 3 ] * ncomp # (Array): index of sector for trailing-edge (PreComp definition of sector) web1 = np.array([ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.4114, 0.4102, 0.4094, 0.3876, 0.3755, 0.3639, 0.345, 0.3342, 0.3313, 0.3274, 0.323, 0.3206, 0.3172, 0.3138, 0.3104, 0.307, 0.3003, 0.2982, 0.2935, 0.2899, 0.2867, 0.2833, 0.2817, 0.2799, 0.2767, 0.2731, 0.2664, 0.2607, 0.2562, 0.1886, -1.0 ]) web2 = np.array([ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.5886, 0.5868, 0.5854, 0.5508, 0.5315, 0.5131, 0.4831, 0.4658, 0.4687, 0.4726, 0.477, 0.4794, 0.4828, 0.4862, 0.4896, 0.493, 0.4997, 0.5018, 0.5065, 0.5101, 0.5133, 0.5167, 0.5183, 0.5201, 0.5233, 0.5269, 0.5336, 0.5393, 0.5438, 0.6114, -1.0 ]) web3 = np.array([ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0 ]) turbine.rotor.chord_str_ref = np.array( [ 3.2612, 3.3100915356, 3.32587052924, 3.34159388653, 3.35823798667, 3.37384375335, 3.38939112914, 3.4774055542, 3.49839685, 3.51343645709, 3.87017220335, 4.04645623801, 4.19408216643, 4.47641008477, 4.55844487985, 4.57383098262, 4.57285771934, 4.51914315648, 4.47677655262, 4.40075650022, 4.31069949379, 4.20483735936, 4.08985563932, 3.82931757126, 3.74220276467, 3.54415796922, 3.38732428502, 3.24931446473, 3.23421422609, 3.22701537997, 3.21972125648, 3.08979310611, 2.95152261813, 2.330753331, 2.05553464181, 1.82577817774, 1.5860853279, 1.4621 ] ) # (Array, m): chord distribution for reference section, thickness of structural layup scaled with reference thickness (fixed t/c for this case) for i in range(ncomp): webLoc = [] if web1[i] != -1: webLoc.append(web1[i]) if web2[i] != -1: webLoc.append(web2[i]) if web3[i] != -1: webLoc.append(web3[i]) upper[i], lower[i], webs[ i] = CompositeSection.initFromPreCompLayupFile( os.path.join(basepath, 'layup_' + str(i + 1) + '.inp'), webLoc, materials) profile[i] = Profile.initFromPreCompFile( os.path.join(basepath, 'shape_' + str(i + 1) + '.inp')) turbine.rotor.materials = materials # (List): list of all Orthotropic2DMaterial objects used in defining the geometry turbine.rotor.upperCS = upper # (List): list of CompositeSection objections defining the properties for upper surface turbine.rotor.lowerCS = lower # (List): list of CompositeSection objections defining the properties for lower surface turbine.rotor.websCS = webs # (List): list of CompositeSection objections defining the properties for shear webs turbine.rotor.profile = profile # (List): airfoil shape at each radial position # -------------------------------------- strain_ult_spar = 1.0e-2 strain_ult_te = 2500 * 1e-6 # --- fatigue --- turbine.rotor.rstar_damage = np.array([ 0.000, 0.022, 0.067, 0.111, 0.167, 0.233, 0.300, 0.367, 0.433, 0.500, 0.567, 0.633, 0.700, 0.767, 0.833, 0.889, 0.933, 0.978 ]) # (Array): nondimensional radial locations of damage equivalent moments turbine.rotor.Mxb_damage = 1e3 * np.array([ 2.3743E+003, 2.0834E+003, 1.8108E+003, 1.5705E+003, 1.3104E+003, 1.0488E+003, 8.2367E+002, 6.3407E+002, 4.7727E+002, 3.4804E+002, 2.4458E+002, 1.6339E+002, 1.0252E+002, 5.7842E+001, 2.7349E+001, 1.1262E+001, 3.8549E+000, 4.4738E-001 ]) # (Array, N*m): damage equivalent moments about blade c.s. x-direction turbine.rotor.Myb_damage = 1e3 * np.array([ 2.7732E+003, 2.8155E+003, 2.6004E+003, 2.3933E+003, 2.1371E+003, 1.8459E+003, 1.5582E+003, 1.2896E+003, 1.0427E+003, 8.2015E+002, 6.2449E+002, 4.5229E+002, 3.0658E+002, 1.8746E+002, 9.6475E+001, 4.2677E+001, 1.5409E+001, 1.8426E+000 ]) # (Array, N*m): damage equivalent moments about blade c.s. y-direction turbine.rotor.strain_ult_spar = 1.0e-2 # (Float): ultimate strain in spar cap turbine.rotor.strain_ult_te = 2500 * 1e-6 * 2 # (Float): uptimate strain in trailing-edge panels, note that I am putting a factor of two for the damage part only. turbine.rotor.eta_damage = 1.35 * 1.3 * 1.0 # (Float): safety factor for fatigue turbine.rotor.m_damage = 10.0 # (Float): slope of S-N curve for fatigue analysis turbine.rotor.N_damage = 365 * 24 * 3600 * 20.0 # (Float): number of cycles used in fatigue analysis TODO: make function of rotation speed # ---------------- # ================= # === nacelle ====== turbine.nacelle.L_ms = 1.0 # (Float, m): main shaft length downwind of main bearing in low-speed shaft turbine.nacelle.L_mb = 2.5 # (Float, m): main shaft length in low-speed shaft turbine.nacelle.h0_front = 1.7 # (Float, m): height of Ibeam in bedplate front turbine.nacelle.h0_rear = 1.35 # (Float, m): height of Ibeam in bedplate rear turbine.nacelle.drivetrain_design = 'geared' turbine.nacelle.crane = True # (Bool): flag for presence of crane turbine.nacelle.bevel = 0 # (Int): Flag for the presence of a bevel stage - 1 if present, 0 if not turbine.nacelle.gear_configuration = 'eep' # (Str): tring that represents the configuration of the gearbox (stage number and types) turbine.nacelle.Np = [3, 3, 1] # (Array): number of planets in each stage turbine.nacelle.ratio_type = 'optimal' # (Str): optimal or empirical stage ratios turbine.nacelle.shaft_type = 'normal' # (Str): normal or short shaft length #turbine.nacelle.shaft_angle = 5.0 # (Float, deg): Angle of the LSS inclindation with respect to the horizontal turbine.nacelle.shaft_ratio = 0.10 # (Float): Ratio of inner diameter to outer diameter. Leave zero for solid LSS turbine.nacelle.carrier_mass = 8000.0 # estimated for 5 MW turbine.nacelle.mb1Type = 'CARB' # (Str): Main bearing type: CARB, TRB or SRB turbine.nacelle.mb2Type = 'SRB' # (Str): Second bearing type: CARB, TRB or SRB turbine.nacelle.yaw_motors_number = 8.0 # (Float): number of yaw motors turbine.nacelle.uptower_transformer = True turbine.nacelle.flange_length = 0.5 #m turbine.nacelle.gearbox_cm = 0.1 turbine.nacelle.hss_length = 1.5 turbine.nacelle.overhang = 5.0 #TODO - should come from turbine configuration level turbine.nacelle.check_fatigue = 0 #0 if no fatigue check, 1 if parameterized fatigue check, 2 if known loads inputs # TODO: should come from rotor (these are FAST outputs) turbine.nacelle.DrivetrainEfficiency = 0.95 #turbine.nacelle.rotor_bending_moment_x = 330770.0# Nm #turbine.nacelle.rotor_bending_moment_y = -16665000.0 # Nm #turbine.nacelle.rotor_bending_moment_z = 2896300.0 # Nm #turbine.nacelle.rotor_force_x = 599610.0 # N #turbine.nacelle.rotor_force_y = 186780.0 # N #turbine.nacelle.rotor_force_z = -842710.0 # N''' #turbine.nacelle.h0_rear = 1.35 # only used in drive smooth #turbine.nacelle.h0_front = 1.7 # ================= # === tower === # ---- tower ------ turbine.tower.replace('wind1', PowerWind()) turbine.tower.replace('wind2', PowerWind()) # onshore (no waves) if turbine.sea_depth <> 0.0: turbine.tower.replace('wave1', LinearWaves()) turbine.tower.replace('wave2', LinearWaves()) turbine.tower.wave1.Uc = 0.0 turbine.tower.wave1.hs = 8.0 * 1.86 turbine.tower.wave1.T = 10.0 turbine.tower.wave1.z_surface = 0.0 turbine.tower.wave1.z_floor = -sea_depth turbine.tower.wave1.g = 9.81 turbine.tower.wave1.betaWave = 0.0 turbine.tower.wave2.Uc = 0.0 turbine.tower.wave2.hs = 8.0 * 1.86 turbine.tower.wave2.T = 10.0 turbine.tower.wave2.z_surface = 0.0 turbine.tower.wave2.z_floor = -sea_depth turbine.tower.wave2.g = 9.81 turbine.tower.wave2.betaWave = 0.0 if turbine.sea_depth == 0.0: # --- geometry ---- #np.insert(turbine.tower.z_param,0,87.9) #np.insert(turbine.tower.z_param,0,43.8) #np.insert(turbine.tower.z_param,0,0.0) turbine.tower.z_param = [0.0, 43.8, 87.9] turbine.tower_d = [6.0, 4.935, 3.87] turbine.tower.t_param = [0.027 * 1.3, 0.023 * 1.3, 0.019 * 1.3] n = 15 turbine.tower.z_full = np.linspace(0.0, 87.6, n) turbine.tower.L_reinforced = 30.0 * np.ones(n) # [m] buckling length turbine.tower.theta_stress = 0.0 * np.ones(n) turbine.tower.yaw = 0.0 # --- material props --- turbine.tower.E = 210e9 * np.ones(n) turbine.tower.G = 80.8e9 * np.ones(n) turbine.tower.rho = 8500.0 * np.ones(n) turbine.tower.sigma_y = 450.0e6 * np.ones(n) else: # --- geometry ---- #np.insert(turbine.tower.z_param,0,87.9) #np.insert(turbine.tower.z_param,0,43.8) #np.insert(turbine.tower.z_param,0,0.0) #np.insert(turbine.tower.z_param,0,-20.0) turbine.tower.z_param = [-20.0, 0.0, 43.8, 87.9] turbine.tower_d = [6.0, 6.0, 4.935, 3.87] turbine.tower.t_param = [0.06, 0.027 * 1.3, 0.023 * 1.3, 0.019 * 1.3] n = 20 turbine.tower.z_full = np.linspace(-20, 87.6, n) turbine.tower.L_reinforced = 30.0 * np.ones(n) # [m] buckling length turbine.tower.theta_stress = 0.0 * np.ones(n) turbine.tower.yaw = 0.0 # --- material props --- turbine.tower.E = 210e9 * np.ones(n) turbine.tower.G = 80.8e9 * np.ones(n) turbine.tower.rho = 8500.0 * np.ones(n) turbine.tower.sigma_y = 450.0e6 * np.ones(n) # --- spring reaction data. Use float('inf') for rigid constraints. --- turbine.tower.kidx = [0] # applied at base turbine.tower.kx = [float('inf')] turbine.tower.ky = [float('inf')] turbine.tower.kz = [float('inf')] turbine.tower.ktx = [float('inf')] turbine.tower.kty = [float('inf')] turbine.tower.ktz = [float('inf')] # --- extra mass ---- turbine.tower.midx = [n - 1] # RNA mass at top turbine.tower.m = [285598.8] turbine.tower.mIxx = [1.14930678e+08] turbine.tower.mIyy = [2.20354030e+07] turbine.tower.mIzz = [1.87597425e+07] turbine.tower.mIxy = [0.00000000e+00] turbine.tower.mIxz = [5.03710467e+05] turbine.tower.mIyz = [0.00000000e+00] turbine.tower.mrhox = [-1.13197635] turbine.tower.mrhoy = [0.] turbine.tower.mrhoz = [0.50875268] turbine.tower.addGravityLoadForExtraMass = True # ----------- # --- wind --- #turbine.tower.wind_zref = 90.0 turbine.tower.wind_z0 = 0.0 turbine.tower.wind1.shearExp = 0.2 turbine.tower.wind2.shearExp = 0.2 # --------------- # if addGravityLoadForExtraMass=True be sure not to double count by adding those force here also # # --- loading case 1: max Thrust --- #turbine.tower.wind_Uref1 = 11.73732 turbine.tower.plidx1 = [n - 1] # at tower top turbine.tower.Fx1 = [1284744.19620519] turbine.tower.Fy1 = [0.] turbine.tower.Fz1 = [-2914124.84400512] turbine.tower.Mxx1 = [3963732.76208099] turbine.tower.Myy1 = [-2275104.79420872] turbine.tower.Mzz1 = [-346781.68192839] # # --------------- # # --- loading case 2: max wind speed --- #turbine.tower.wind_Uref2 = 70.0 turbine.tower.plidx2 = [n - 1] # at tower top turbine.tower.Fx2 = [930198.60063279] turbine.tower.Fy2 = [0.] turbine.tower.Fz2 = [-2883106.12368949] turbine.tower.Mxx2 = [-1683669.22411597] turbine.tower.Myy2 = [-2522475.34625363] turbine.tower.Mzz2 = [147301.97023764] # # --------------- # --- safety factors --- turbine.tower.gamma_f = 1.35 turbine.tower.gamma_m = 1.3 turbine.tower.gamma_n = 1.0 turbine.tower.gamma_b = 1.1 # --------------- # --- fatigue --- turbine.tower.z_DEL = np.array([ 0.000, 1.327, 3.982, 6.636, 9.291, 11.945, 14.600, 17.255, 19.909, 22.564, 25.218, 27.873, 30.527, 33.182, 35.836, 38.491, 41.145, 43.800, 46.455, 49.109, 51.764, 54.418, 57.073, 59.727, 62.382, 65.036, 67.691, 70.345, 73.000, 75.655, 78.309, 80.964, 83.618, 86.273, 87.600 ]) turbine.tower.M_DEL = 1e3 * np.array([ 8.2940E+003, 8.1518E+003, 7.8831E+003, 7.6099E+003, 7.3359E+003, 7.0577E+003, 6.7821E+003, 6.5119E+003, 6.2391E+003, 5.9707E+003, 5.7070E+003, 5.4500E+003, 5.2015E+003, 4.9588E+003, 4.7202E+003, 4.4884E+003, 4.2577E+003, 4.0246E+003, 3.7942E+003, 3.5664E+003, 3.3406E+003, 3.1184E+003, 2.8977E+003, 2.6811E+003, 2.4719E+003, 2.2663E+003, 2.0673E+003, 1.8769E+003, 1.7017E+003, 1.5479E+003, 1.4207E+003, 1.3304E+003, 1.2780E+003, 1.2673E+003, 1.2761E+003 ]) turbine.tower.gamma_fatigue = 1.35 * 1.3 * 1.0 turbine.tower.life = 20.0 turbine.tower.m_SN = 4 # --------------- # --- constraints --- turbine.tower.min_d_to_t = 120.0 turbine.tower.min_taper = 0.4 # --------------- # ==== Other options if wind_class == 'I': turbine.rotor.turbine_class = 'I' elif wind_class == 'III': turbine.rotor.turbine_class = 'III' # for fatigue based analysis of class III wind turbine turbine.tower.M_DEL = 1.028713178 * 1e3 * np.array([ 7.8792E+003, 7.7507E+003, 7.4918E+003, 7.2389E+003, 6.9815E+003, 6.7262E+003, 6.4730E+003, 6.2174E+003, 5.9615E+003, 5.7073E+003, 5.4591E+003, 5.2141E+003, 4.9741E+003, 4.7399E+003, 4.5117E+003, 4.2840E+003, 4.0606E+003, 3.8360E+003, 3.6118E+003, 3.3911E+003, 3.1723E+003, 2.9568E+003, 2.7391E+003, 2.5294E+003, 2.3229E+003, 2.1246E+003, 1.9321E+003, 1.7475E+003, 1.5790E+003, 1.4286E+003, 1.3101E+003, 1.2257E+003, 1.1787E+003, 1.1727E+003, 1.1821E+003 ]) turbine.rotor.Mxb_damage = 1e3 * np.array([ 2.3617E+003, 2.0751E+003, 1.8051E+003, 1.5631E+003, 1.2994E+003, 1.0388E+003, 8.1384E+002, 6.2492E+002, 4.6916E+002, 3.4078E+002, 2.3916E+002, 1.5916E+002, 9.9752E+001, 5.6139E+001, 2.6492E+001, 1.0886E+001, 3.7210E+000, 4.3206E-001 ]) turbine.rotor.Myb_damage = 1e3 * np.array([ 2.5492E+003, 2.6261E+003, 2.4265E+003, 2.2308E+003, 1.9882E+003, 1.7184E+003, 1.4438E+003, 1.1925E+003, 9.6251E+002, 7.5564E+002, 5.7332E+002, 4.1435E+002, 2.8036E+002, 1.7106E+002, 8.7732E+001, 3.8678E+001, 1.3942E+001, 1.6600E+000 ]) elif wind_class == 'Offshore': turbine.rotor.turbine_class = 'I' # TODO: these should be specified at the turbine level and connected to other system inputs if turbine.sea_depth == 0.0: turbine.tower_d = [6.0, 4.935, 3.87] # (Array, m): diameters along tower else: turbine.tower_d = [6.0, 6.0, 4.935, 3.87] turbine.generator_speed = 1173.7 # (Float, rpm) # generator speed
# --- aero and structural analysis options --- rotor.nSector = 4 # (Int): number of sectors to divide rotor face into in computing thrust and power rotor.npts_coarse_power_curve = 20 # (Int): number of points to evaluate aero analysis at rotor.npts_spline_power_curve = 200 # (Int): number of points to use in fitting spline to power curve rotor.AEP_loss_factor = 1.0 # (Float): availability and other losses (soiling, array, etc.) rotor.drivetrainType = 'geared' # (Enum) rotor.nF = 5 # (Int): number of natural frequencies to compute rotor.dynamic_amplication_tip_deflection = 1.35 # (Float): a dynamic amplification factor to adjust the static deflection calculation # ---------------------- # --- materials and composite layup --- basepath = os.path.join(os.path.dirname(rotorse.__file__), '5MW_PreCompFiles') # basepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), '5MW_PrecompFiles') materials = Orthotropic2DMaterial.listFromPreCompFile(os.path.join(basepath, 'materials.inp')) ncomp = len(rotor.initial_str_grid) upper = [0]*ncomp lower = [0]*ncomp webs = [0]*ncomp profile = [0]*ncomp rotor.leLoc = np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.498, 0.497, 0.465, 0.447, 0.43, 0.411, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4]) # (Array): array of leading-edge positions from a reference blade axis (usually blade pitch axis). locations are normalized by the local chord length. e.g. leLoc[i] = 0.2 means leading edge is 0.2*chord[i] from reference axis. positive in -x direction for airfoil-aligned coordinate system rotor.sector_idx_strain_spar = [2]*ncomp # (Array): index of sector for spar (PreComp definition of sector) rotor.sector_idx_strain_te = [3]*ncomp # (Array): index of sector for trailing-edge (PreComp definition of sector) web1 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.4114, 0.4102, 0.4094, 0.3876, 0.3755, 0.3639, 0.345, 0.3342, 0.3313, 0.3274, 0.323, 0.3206, 0.3172, 0.3138, 0.3104, 0.307, 0.3003, 0.2982, 0.2935, 0.2899, 0.2867, 0.2833, 0.2817, 0.2799, 0.2767, 0.2731, 0.2664, 0.2607, 0.2562, 0.1886, -1.0]) web2 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.5886, 0.5868, 0.5854, 0.5508, 0.5315, 0.5131, 0.4831, 0.4658, 0.4687, 0.4726, 0.477, 0.4794, 0.4828, 0.4862, 0.4896, 0.493, 0.4997, 0.5018, 0.5065, 0.5101, 0.5133, 0.5167, 0.5183, 0.5201, 0.5233, 0.5269, 0.5336, 0.5393, 0.5438, 0.6114, -1.0]) web3 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0])
def EvaluateLCOE(BladeLength, HubHeight, MaximumRotSpeed,Verbose=False): ############################################################################ # Define baseline paremeters used for scaling ReferenceBladeLength = 35; ReferenceTowerHeight = 95 WindReferenceHeight = 50 WindReferenceMeanVelocity = 3 WeibullShapeFactor = 2.0 ShearFactor = 0.25 RatedPower = 1.5e6 # Years used for analysis Years = 25 DiscountRate = 0.08 ############################################################################ ############################################################################ ### 1. Aerodynamic and structural performance using RotorSE rotor = RotorSE() # ------------------- # === blade grid === # (Array): initial aerodynamic grid on unit radius rotor.initial_aero_grid = np.array([0.02222276, 0.06666667, 0.11111057, \ 0.16666667, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, \ 0.63333333, 0.7, 0.76666667, 0.83333333, 0.88888943, 0.93333333, \ 0.97777724]) # (Array): initial structural grid on unit radius rotor.initial_str_grid = np.array([0.0, 0.00492790457512, 0.00652942887106, 0.00813095316699, 0.00983257273154, 0.0114340970275, 0.0130356213234, 0.02222276, 0.024446481932, 0.026048006228, 0.06666667, 0.089508406455, 0.11111057, 0.146462614229, 0.16666667, 0.195309105255, 0.23333333, 0.276686558545, 0.3, 0.333640766319,0.36666667, 0.400404310407, 0.43333333, 0.5, 0.520818918408, 0.56666667, 0.602196371696, 0.63333333, 0.667358391486, 0.683573824984, 0.7, 0.73242031601, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724, 1.0]) # (Int): first idx in r_aero_unit of non-cylindrical section, # constant twist inboard of here rotor.idx_cylinder_aero = 3 # (Int): first idx in r_str_unit of non-cylindrical section rotor.idx_cylinder_str = 14 # (Float): hub location as fraction of radius rotor.hubFraction = 0.025 # ------------------ # === blade geometry === # (Array): new aerodynamic grid on unit radius rotor.r_aero = np.array([0.02222276, 0.06666667, 0.11111057, 0.2, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.64, 0.7, 0.83333333, 0.88888943, 0.93333333, 0.97777724]) # (Float): location of max chord on unit radius rotor.r_max_chord = 0.23577 # (Array, m): chord at control points. defined at hub, then at linearly spaced # locations from r_max_chord to tip ReferenceChord = [3.2612, 4.5709, 3.3178, 1.4621] rotor.chord_sub = [x * np.true_divide(BladeLength,ReferenceBladeLength) \ for x in ReferenceChord] # (Array, deg): twist at control points. defined at linearly spaced locations # from r[idx_cylinder] to tip rotor.theta_sub = [13.2783, 7.46036, 2.89317, -0.0878099] # (Array, m): precurve at control points. defined at same locations at chord, # starting at 2nd control point (root must be zero precurve) rotor.precurve_sub = [0.0, 0.0, 0.0] # (Array, m): adjustment to precurve to account for curvature from loading rotor.delta_precurve_sub = [0.0, 0.0, 0.0] # (Array, m): spar cap thickness parameters rotor.sparT = [0.05, 0.047754, 0.045376, 0.031085, 0.0061398] # (Array, m): trailing-edge thickness parameters rotor.teT = [0.1, 0.09569, 0.06569, 0.02569, 0.00569] # (Float, m): blade length (if not precurved or swept) # otherwise length of blade before curvature rotor.bladeLength = BladeLength # (Float, m): adjustment to blade length to account for curvature from # loading rotor.delta_bladeLength = 0.0 rotor.precone = 2.5 # (Float, deg): precone angle rotor.tilt = 5.0 # (Float, deg): shaft tilt rotor.yaw = 0.0 # (Float, deg): yaw error rotor.nBlades = 3 # (Int): number of blades # ------------------ # === airfoil files === basepath = os.path.join(os.path.dirname(\ os.path.realpath(__file__)), '5MW_AFFiles') # load all airfoils airfoil_types = [0]*8 airfoil_types[0] = os.path.join(basepath, 'Cylinder1.dat') airfoil_types[1] = os.path.join(basepath, 'Cylinder2.dat') airfoil_types[2] = os.path.join(basepath, 'DU40_A17.dat') airfoil_types[3] = os.path.join(basepath, 'DU35_A17.dat') airfoil_types[4] = os.path.join(basepath, 'DU30_A17.dat') airfoil_types[5] = os.path.join(basepath, 'DU25_A17.dat') airfoil_types[6] = os.path.join(basepath, 'DU21_A17.dat') airfoil_types[7] = os.path.join(basepath, 'NACA64_A17.dat') # place at appropriate radial stations af_idx = [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7] n = len(af_idx) af = [0]*n for i in range(n): af[i] = airfoil_types[af_idx[i]] rotor.airfoil_files = af # (List): names of airfoil file # ---------------------- # === atmosphere === rotor.rho = 1.225 # (Float, kg/m**3): density of air rotor.mu = 1.81206e-5 # (Float, kg/m/s): dynamic viscosity of air rotor.shearExp = 0.25 # (Float): shear exponent rotor.hubHt = HubHeight # (Float, m): hub height rotor.turbine_class = 'I' # (Enum): IEC turbine class rotor.turbulence_class = 'B' # (Enum): IEC turbulence class class rotor.cdf_reference_height_wind_speed = 30.0 rotor.g = 9.81 # (Float, m/s**2): acceleration of gravity # ---------------------- # === control === rotor.control.Vin = 3.0 # (Float, m/s): cut-in wind speed rotor.control.Vout = 26.0 # (Float, m/s): cut-out wind speed rotor.control.ratedPower = RatedPower # (Float, W): rated power # (Float, rpm): minimum allowed rotor rotation speed # (Float, rpm): maximum allowed rotor rotation speed rotor.control.minOmega = 0.0 rotor.control.maxOmega = MaximumRotSpeed # (Float): tip-speed ratio in Region 2 (should be optimized externally) rotor.control.tsr = 7 # (Float, deg): pitch angle in region 2 (and region 3 for fixed pitch machines) rotor.control.pitch = 0.0 # (Float, deg): worst-case pitch at survival wind condition rotor.pitch_extreme = 0.0 # (Float, deg): worst-case azimuth at survival wind condition rotor.azimuth_extreme = 0.0 # (Float): fraction of rated speed at which the deflection is assumed to # representative throughout the power curve calculation rotor.VfactorPC = 0.7 # ---------------------- # === aero and structural analysis options === # (Int): number of sectors to divide rotor face into in computing thrust and power rotor.nSector = 4 # (Int): number of points to evaluate aero analysis at rotor.npts_coarse_power_curve = 20 # (Int): number of points to use in fitting spline to power curve rotor.npts_spline_power_curve = 200 # (Float): availability and other losses (soiling, array, etc.) rotor.AEP_loss_factor = 1.0 rotor.drivetrainType = 'geared' # (Enum) # (Int): number of natural frequencies to compute rotor.nF = 5 # (Float): a dynamic amplification factor to adjust the static deflection # calculation rotor.dynamic_amplication_tip_deflection = 1.35 # ---------------------- # === materials and composite layup === basepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), \ '5MW_PrecompFiles') materials = Orthotropic2DMaterial.listFromPreCompFile(os.path.join(basepath,\ 'materials.inp')) ncomp = len(rotor.initial_str_grid) upper = [0]*ncomp lower = [0]*ncomp webs = [0]*ncomp profile = [0]*ncomp # (Array): array of leading-edge positions from a reference blade axis # (usually blade pitch axis). locations are normalized by the local chord # length. e.g. leLoc[i] = 0.2 means leading edge is 0.2*chord[i] from reference # axis. positive in -x direction for airfoil-aligned coordinate system rotor.leLoc = np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.498, 0.497, 0.465, 0.447, 0.43, 0.411, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4]) # (Array): index of sector for spar (PreComp definition of sector) rotor.sector_idx_strain_spar = [2]*ncomp # (Array): index of sector for trailing-edge (PreComp definition of sector) rotor.sector_idx_strain_te = [3]*ncomp web1 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.4114, 0.4102, 0.4094, 0.3876, 0.3755, 0.3639, 0.345, 0.3342, 0.3313, 0.3274, 0.323, 0.3206, 0.3172, 0.3138, 0.3104, 0.307, 0.3003, 0.2982, 0.2935, 0.2899, 0.2867, 0.2833, 0.2817, 0.2799, 0.2767, 0.2731, 0.2664, 0.2607, 0.2562, 0.1886, -1.0]) web2 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.5886, 0.5868, 0.5854, 0.5508, 0.5315, 0.5131, 0.4831, 0.4658, 0.4687, 0.4726, 0.477, 0.4794, 0.4828, 0.4862, 0.4896, 0.493, 0.4997, 0.5018, 0.5065, 0.5101, 0.5133, 0.5167, 0.5183, 0.5201, 0.5233, 0.5269, 0.5336, 0.5393, 0.5438, 0.6114, -1.0]) web3 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]) # (Array, m): chord distribution for reference section, thickness of structural # layup scaled with reference thickness (fixed t/c for this case) rotor.chord_str_ref = np.array([3.2612, 3.3100915356, 3.32587052924, 3.34159388653, 3.35823798667, 3.37384375335, 3.38939112914, 3.4774055542, 3.49839685, 3.51343645709, 3.87017220335, 4.04645623801, 4.19408216643, 4.47641008477, 4.55844487985, 4.57383098262, 4.57285771934, 4.51914315648, 4.47677655262, 4.40075650022, 4.31069949379, 4.20483735936, 4.08985563932, 3.82931757126, 3.74220276467, 3.54415796922, 3.38732428502, 3.24931446473, 3.23421422609, 3.22701537997, 3.21972125648, 3.08979310611, 2.95152261813, 2.330753331, 2.05553464181, 1.82577817774, 1.5860853279, 1.4621])* \ np.true_divide(BladeLength,ReferenceBladeLength) for i in range(ncomp): webLoc = [] if web1[i] != -1: webLoc.append(web1[i]) if web2[i] != -1: webLoc.append(web2[i]) if web3[i] != -1: webLoc.append(web3[i]) upper[i], lower[i], webs[i] = CompositeSection.initFromPreCompLayupFile\ (os.path.join(basepath, 'layup_' + str(i+1) + '.inp'), webLoc, materials) profile[i] = Profile.initFromPreCompFile(os.path.join(basepath, 'shape_' \ + str(i+1) + '.inp')) # (List): list of all Orthotropic2DMaterial objects used in # defining the geometry rotor.materials = materials # (List): list of CompositeSection objections defining the properties for # upper surface rotor.upperCS = upper # (List): list of CompositeSection objections defining the properties for # lower surface rotor.lowerCS = lower # (List): list of CompositeSection objections defining the properties for # shear webs rotor.websCS = webs # (List): airfoil shape at each radial position rotor.profile = profile # -------------------------------------- # === fatigue === # (Array): nondimensional radial locations of damage equivalent moments rotor.rstar_damage = np.array([0.000, 0.022, 0.067, 0.111, 0.167, 0.233, 0.300, 0.367, 0.433, 0.500, 0.567, 0.633, 0.700, 0.767, 0.833, 0.889, 0.933, 0.978]) # (Array, N*m): damage equivalent moments about blade c.s. x-direction rotor.Mxb_damage = 1e3*np.array([2.3743E+003, 2.0834E+003, 1.8108E+003, 1.5705E+003, 1.3104E+003, 1.0488E+003, 8.2367E+002, 6.3407E+002, 4.7727E+002, 3.4804E+002, 2.4458E+002, 1.6339E+002, 1.0252E+002, 5.7842E+001, 2.7349E+001, 1.1262E+001, 3.8549E+000, 4.4738E-001]) # (Array, N*m): damage equivalent moments about blade c.s. y-direction rotor.Myb_damage = 1e3*np.array([2.7732E+003, 2.8155E+003, 2.6004E+003, 2.3933E+003, 2.1371E+003, 1.8459E+003, 1.5582E+003, 1.2896E+003, 1.0427E+003, 8.2015E+002, 6.2449E+002, 4.5229E+002, 3.0658E+002, 1.8746E+002, 9.6475E+001, 4.2677E+001, 1.5409E+001, 1.8426E+000]) rotor.strain_ult_spar = 1.0e-2 # (Float): ultimate strain in spar cap # (Float): uptimate strain in trailing-edge panels, note that I am putting a # factor of two for the damage part only. rotor.strain_ult_te = 2500*1e-6 * 2 rotor.eta_damage = 1.35*1.3*1.0 # (Float): safety factor for fatigue rotor.m_damage = 10.0 # (Float): slope of S-N curve for fatigue analysis # (Float): number of cycles used in fatigue analysis rotor.N_damage = 365*24*3600*20.0 # ---------------- # from myutilities import plt # === run and outputs === rotor.run() # Evaluate AEP Using Lewis' Functions # Weibull Wind Parameters WindReferenceHeight = 50 WindReferenceMeanVelocity = 7.5 WeibullShapeFactor = 2.0 ShearFactor = 0.25 PowerCurve = rotor.P/1e6 PowerCurveVelocity = rotor.V HubHeight = rotor.hubHt AEP,WeibullScale = CalculateAEPWeibull(PowerCurve,PowerCurveVelocity, HubHeight, \ BladeLength,WeibullShapeFactor, WindReferenceHeight, \ WindReferenceMeanVelocity, ShearFactor) NamePlateCapacity = EstimateCapacity(PowerCurve,PowerCurveVelocity, \ rotor.ratedConditions.V) # AEP At Constant 7.5m/s Wind used for benchmarking... #AEP = CalculateAEPConstantWind(PowerCurve, PowerCurveVelocity, 7.5) if (Verbose ==True): print '################### ROTORSE ######################' print 'AEP = %d MWH' %(AEP) print 'NamePlateCapacity = %fMW' %(NamePlateCapacity) print 'diameter =', rotor.diameter print 'ratedConditions.V =', rotor.ratedConditions.V print 'ratedConditions.Omega =', rotor.ratedConditions.Omega print 'ratedConditions.pitch =', rotor.ratedConditions.pitch print 'mass_one_blade =', rotor.mass_one_blade print 'mass_all_blades =', rotor.mass_all_blades print 'I_all_blades =', rotor.I_all_blades print 'freq =', rotor.freq print 'tip_deflection =', rotor.tip_deflection print 'root_bending_moment =', rotor.root_bending_moment print '#########################################################' ############################################################################# ### 2. Hub Sizing # Specify hub parameters based off rotor # Load default hub model hubS = HubSE() hubS.rotor_diameter = rotor.Rtip*2 # m hubS.blade_number = rotor.nBlades hubS.blade_root_diameter = rotor.chord_sub[0]*1.25 hubS.L_rb = rotor.hubFraction*rotor.diameter hubS.MB1_location = np.array([-0.5, 0.0, 0.0]) hubS.machine_rating = rotor.control.ratedPower hubS.blade_mass = rotor.mass_one_blade hubS.rotor_bending_moment = rotor.root_bending_moment hubS.run() RotorTotalWeight = rotor.mass_all_blades + hubS.spinner.mass + \ hubS.hub.mass + hubS.pitchSystem.mass if (Verbose==True): print '##################### Hub SE ############################' print "Estimate of Hub Component Sizes:" print "Hub Components" print ' Hub: {0:8.1f} kg'.format(hubS.hub.mass) print ' Pitch system: {0:8.1f} kg'.format(hubS.pitchSystem.mass) print ' Nose cone: {0:8.1f} kg'.format(hubS.spinner.mass) print 'Rotor Total Weight = %d kg' %RotorTotalWeight print '#########################################################' ############################################################################ ### 3. Drive train + Nacelle Mass estimation nace = Drive4pt() nace.rotor_diameter = rotor.Rtip *2 # m nace.rotor_speed = rotor.ratedConditions.Omega # #rpm m/s nace.machine_rating = hubS.machine_rating/1000 nace.DrivetrainEfficiency = 0.95 # 6.35e6 #4365248.74 # Nm nace.rotor_torque = rotor.ratedConditions.Q nace.rotor_thrust = rotor.ratedConditions.T # N nace.rotor_mass = 0.0 #accounted for in F_z # kg nace.rotor_bending_moment_x = rotor.Mxyz_0[0] nace.rotor_bending_moment_y = rotor.Mxyz_0[1] nace.rotor_bending_moment_z = rotor.Mxyz_0[2] nace.rotor_force_x = rotor.Fxyz_0[0] # N nace.rotor_force_y = rotor.Fxyz_0[1] nace.rotor_force_z = rotor.Fxyz_0[2] # N # geared 3-stage Gearbox with induction generator machine nace.drivetrain_design = 'geared' nace.gear_ratio = 96.76 # 97:1 as listed in the 5 MW reference document nace.gear_configuration = 'eep' # epicyclic-epicyclic-parallel nace.crane = True # onboard crane present nace.shaft_angle = 5.0 #deg nace.shaft_ratio = 0.10 nace.Np = [3,3,1] nace.ratio_type = 'optimal' nace.shaft_type = 'normal' nace.uptower_transformer=False nace.shrink_disc_mass = 333.3*nace.machine_rating/1000.0 # estimated nace.mb1Type = 'CARB' nace.mb2Type = 'SRB' nace.flange_length = 0.5 #m nace.overhang = 5.0 nace.gearbox_cm = 0.1 nace.hss_length = 1.5 #0 if no fatigue check, 1 if parameterized fatigue check, #2 if known loads inputs nace.check_fatigue = 0 nace.blade_number=rotor.nBlades nace.cut_in=rotor.control.Vin #cut-in m/s nace.cut_out=rotor.control.Vout #cut-out m/s nace.Vrated=rotor.ratedConditions.V #rated windspeed m/s nace.weibull_k = WeibullShapeFactor # windepeed distribution shape parameter # windspeed distribution scale parameter nace.weibull_A = WeibullScale nace.T_life=20. #design life in years nace.IEC_Class_Letter = 'B' # length from hub center to main bearing, leave zero if unknown nace.L_rb = hubS.L_rb # NREL 5 MW Tower Variables nace.tower_top_diameter = 3.78 # m nace.run() if (Verbose==True): print '##################### Drive SE ############################' print "Estimate of Nacelle Component Sizes" print 'Low speed shaft: {0:8.1f} kg'.format(nace.lowSpeedShaft.mass) print 'Main bearings: {0:8.1f} kg'.format(\ nace.mainBearing.mass + nace.secondBearing.mass) print 'Gearbox: {0:8.1f} kg'.format(nace.gearbox.mass) print 'High speed shaft & brakes: {0:8.1f} kg'.format\ (nace.highSpeedSide.mass) print 'Generator: {0:8.1f} kg'.format(nace.generator.mass) print 'Variable speed electronics: {0:8.1f} kg'.format(\ nace.above_yaw_massAdder.vs_electronics_mass) print 'Overall mainframe:{0:8.1f} kg'.format(\ nace.above_yaw_massAdder.mainframe_mass) print ' Bedplate: {0:8.1f} kg'.format(nace.bedplate.mass) print 'Electrical connections: {0:8.1f} kg'.format(\ nace.above_yaw_massAdder.electrical_mass) print 'HVAC system: {0:8.1f} kg'.format(\ nace.above_yaw_massAdder.hvac_mass ) print 'Nacelle cover: {0:8.1f} kg'.format(\ nace.above_yaw_massAdder.cover_mass) print 'Yaw system: {0:8.1f} kg'.format(nace.yawSystem.mass) print 'Overall nacelle: {0:8.1f} kg'.format(nace.nacelle_mass, \ nace.nacelle_cm[0], nace.nacelle_cm[1], nace.nacelle_cm[2], \ nace.nacelle_I[0], nace.nacelle_I[1], nace.nacelle_I[2]) print '#########################################################' ############################################################################ ### 4. Tower Mass # --- tower setup ------ from commonse.environment import PowerWind tower = set_as_top(TowerSE()) # ---- tower ------ tower.replace('wind1', PowerWind()) tower.replace('wind2', PowerWind()) # onshore (no waves) # --- geometry ---- tower.z_param = [0.0, HubHeight*0.5, HubHeight] TowerRatio = np.true_divide(HubHeight,ReferenceTowerHeight) tower.d_param = [6.0*TowerRatio, 4.935*TowerRatio, 3.87*TowerRatio] tower.t_param = [0.027*1.3*TowerRatio, 0.023*1.3*TowerRatio, \ 0.019*1.3*TowerRatio] n = 10 tower.z_full = np.linspace(0.0, HubHeight, n) tower.L_reinforced = 15.0*np.ones(n) # [m] buckling length tower.theta_stress = 0.0*np.ones(n) tower.yaw = 0.0 # --- material props --- tower.E = 210e9*np.ones(n) tower.G = 80.8e9*np.ones(n) tower.rho = 8500.0*np.ones(n) tower.sigma_y = 450.0e6*np.ones(n) # --- spring reaction data. Use float('inf') for rigid constraints. --- tower.kidx = [0] # applied at base tower.kx = [float('inf')] tower.ky = [float('inf')] tower.kz = [float('inf')] tower.ktx = [float('inf')] tower.kty = [float('inf')] tower.ktz = [float('inf')] # --- extra mass ---- tower.midx = [n-1] # RNA mass at top tower.m = [0.8] tower.mIxx = [1.14930678e+08] tower.mIyy = [2.20354030e+07] tower.mIzz = [1.87597425e+07] tower.mIxy = [0.00000000e+00] tower.mIxz = [5.03710467e+05] tower.mIyz = [0.00000000e+00] tower.mrhox = [-1.13197635] tower.mrhoy = [0.] tower.mrhoz = [0.50875268] tower.addGravityLoadForExtraMass = False # ----------- # --- wind --- tower.wind_zref = 90.0 tower.wind_z0 = 0.0 tower.wind1.shearExp = 0.14 tower.wind2.shearExp = 0.14 # --------------- # # --- loading case 1: max Thrust --- tower.wind_Uref1 = 11.73732 tower.plidx1 = [n-1] # at tower top tower.Fx1 = [0.19620519] tower.Fy1 = [0.] tower.Fz1 = [-2914124.84400512] tower.Mxx1 = [3963732.76208099] tower.Myy1 = [-2275104.79420872] tower.Mzz1 = [-346781.68192839] # # --------------- # # --- loading case 2: max wind speed --- tower.wind_Uref2 = 70.0 tower.plidx1 = [n-1] # at tower top tower.Fx1 = [930198.60063279] tower.Fy1 = [0.] tower.Fz1 = [-2883106.12368949] tower.Mxx1 = [-1683669.22411597] tower.Myy1 = [-2522475.34625363] tower.Mzz1 = [147301.97023764] # # --------------- # # --- run --- tower.run() if (Verbose==True): print '##################### Tower SE ##########################' print 'mass (kg) =', tower.mass print 'f1 (Hz) =', tower.f1 print 'f2 (Hz) =', tower.f2 print 'top_deflection1 (m) =', tower.top_deflection1 print 'top_deflection2 (m) =', tower.top_deflection2 print '#########################################################' ############################################################################ ## 5. Turbine captial costs analysis turbine = Turbine_CostsSE() # NREL 5 MW turbine component masses based on Sunderland model approach # Rotor # inline with the windpact estimates turbine.blade_mass = rotor.mass_one_blade turbine.hub_mass = hubS.hub.mass turbine.pitch_system_mass = hubS.pitchSystem.mass turbine.spinner_mass = hubS.spinner.mass # Drivetrain and Nacelle turbine.low_speed_shaft_mass = nace.lowSpeedShaft.mass turbine.main_bearing_mass=nace.mainBearing.mass turbine.second_bearing_mass = nace.secondBearing.mass turbine.gearbox_mass = nace.gearbox.mass turbine.high_speed_side_mass = nace.highSpeedSide.mass turbine.generator_mass = nace.generator.mass turbine.bedplate_mass = nace.bedplate.mass turbine.yaw_system_mass = nace.yawSystem.mass # Tower turbine.tower_mass = tower.mass*0.5 # Additional non-mass cost model input variables turbine.machine_rating = hubS.machine_rating/1000 turbine.advanced = False turbine.blade_number = rotor.nBlades turbine.drivetrain_design = 'geared' turbine.crane = False turbine.offshore = False # Target year for analysis results turbine.year = 2010 turbine.month = 12 turbine.run() if (Verbose==True): print '##################### TurbinePrice SE ####################' print "Overall rotor cost with 3 advanced blades is ${0:.2f} USD"\ .format(turbine.rotorCC.cost) print "Blade cost is ${0:.2f} USD".format(turbine.rotorCC.bladeCC.cost) print "Hub cost is ${0:.2f} USD".format(turbine.rotorCC.hubCC.cost) print "Pitch system cost is ${0:.2f} USD".format(turbine.rotorCC.pitchSysCC.cost) print "Spinner cost is ${0:.2f} USD".format(turbine.rotorCC.spinnerCC.cost) print print "Overall nacelle cost is ${0:.2f} USD".format(turbine.nacelleCC.cost) print "LSS cost is ${0:.2f} USD".format(turbine.nacelleCC.lssCC.cost) print "Main bearings cost is ${0:.2f} USD".format(turbine.nacelleCC.bearingsCC.cost) print "Gearbox cost is ${0:.2f} USD".format(turbine.nacelleCC.gearboxCC.cost) print "Hight speed side cost is ${0:.2f} USD".format(turbine.nacelleCC.hssCC.cost) print "Generator cost is ${0:.2f} USD".format(turbine.nacelleCC.generatorCC.cost) print "Bedplate cost is ${0:.2f} USD".format(turbine.nacelleCC.bedplateCC.cost) print "Yaw system cost is ${0:.2f} USD".format(turbine.nacelleCC.yawSysCC.cost) print print "Tower cost is ${0:.2f} USD".format(turbine.towerCC.cost) print print "The overall turbine cost is ${0:.2f} USD".format(turbine.turbine_cost) print '#########################################################' ############################################################################ ## 6. Operating Expenses # A simple test of nrel_csm_bos model bos = bos_csm_assembly() # Set input parameters bos = bos_csm_assembly() bos.machine_rating = hubS.machine_rating/1000 bos.rotor_diameter = rotor.diameter bos.turbine_cost = turbine.turbine_cost bos.hub_height = HubHeight bos.turbine_number = 1 bos.sea_depth = 0 bos.year = 2009 bos.month = 12 bos.multiplier = 1.0 bos.run() om = opex_csm_assembly() om.machine_rating = rotor.control.ratedPower/1000 # Need to manipulate input or underlying component will not execute om.net_aep = AEP*10e4 om.sea_depth = 0 om.year = 2009 om.month = 12 om.turbine_number = 100 om.run() if (Verbose==True): print '##################### Operating Costs ####################' print "BOS cost per turbine: ${0:.2f} USD".format(bos.bos_costs / \ bos.turbine_number) print "Average annual operational expenditures" print "OPEX on shore with 100 turbines ${:.2f}: USD".format(\ om.avg_annual_opex) print "Preventative OPEX by turbine: ${:.2f} USD".format(\ om.opex_breakdown.preventative_opex / om.turbine_number) print "Corrective OPEX by turbine: ${:.2f} USD".format(\ om.opex_breakdown.corrective_opex / om.turbine_number) print "Land Lease OPEX by turbine: ${:.2f} USD".format(\ om.opex_breakdown.lease_opex / om.turbine_number) print '#########################################################' CapitalCost = turbine.turbine_cost + bos.bos_costs / bos.turbine_number OperatingCost = om.opex_breakdown.preventative_opex / om.turbine_number + \ om.opex_breakdown.lease_opex / om.turbine_number + \ om.opex_breakdown.corrective_opex / om.turbine_number LCOE = ComputeLCOE(AEP, CapitalCost, OperatingCost, DiscountRate, Years) print '######################***********************###################' print "Levelized Cost of Energy over %d years \ is $%f/kWH" %(Years,LCOE/1000) print '######################***********************###################' return LCOE/1000
def example(wind_class='I',sea_depth=0.0,with_new_nacelle=False,with_landbos=False,flexible_blade=False,with_3pt_drive=False, with_ecn_opex=False, ecn_file=None,with_openwind=False,ow_file=None,ow_wkbook=None): """ Inputs: wind_class : str ('I', 'III', 'Offshore' - selected wind class for project) sea_depth : float (sea depth if an offshore wind plant) """ # === Create LCOE SE assembly ======== lcoe_se = lcoe_se_assembly(with_new_nacelle,with_landbos,flexible_blade,with_3pt_drive,with_ecn_opex,ecn_file) # === Set assembly variables and objects === lcoe_se.sea_depth = sea_depth # 0.0 for land-based turbine lcoe_se.turbine_number = 100 lcoe_se.year = 2009 lcoe_se.month = 12 rotor = lcoe_se.rotor nacelle = lcoe_se.nacelle jacket = lcoe_se.jacket tcc_a = lcoe_se.tcc_a # bos_a = lcoe_se.bos_a # opex_a = lcoe_se.opex_a aep_a = lcoe_se.aep_a fin_a = lcoe_se.fin_a # Turbine =========== from wisdem.reference_turbines.nrel5mw.nrel5mw_jacket import configure_nrel5mw_turbine_with_jacket configure_nrel5mw_turbine_with_jacket(lcoe_se,wind_class,lcoe_se.sea_depth) # TODO: these should be specified at the turbine level and connected to other system inputs lcoe_se.tower_dt = 3.87 # (Array, m): diameters along tower # float for jacket lcoe_se.generator_speed = 1173.7 # (Float, rpm) # generator speed # extra variable constant for now #lcoe_se.nacelle.bedplate.rotor_bending_moment_y = -2.3250E+06 # shouldnt be needed anymore # tcc ==== lcoe_se.advanced_blade = True lcoe_se.offshore = False lcoe_se.assemblyCostMultiplier = 0.30 lcoe_se.profitMultiplier = 0.20 lcoe_se.overheadCostMultiplier = 0.0 lcoe_se.transportMultiplier = 0.0 # for new landBOS ''' # === new landBOS === lcoe_se.voltage = 137 lcoe_se.distInter = 5 lcoe_se.terrain = 'FLAT_TO_ROLLING' lcoe_se.layout = 'SIMPLE' lcoe_se.soil = 'STANDARD' ''' # aep ==== if not with_openwind: lcoe_se.array_losses = 0.059 lcoe_se.other_losses = 0.0 if not with_ecn_opex: lcoe_se.availability = 0.94 # fin === lcoe_se.fixed_charge_rate = 0.095 lcoe_se.construction_finance_rate = 0.0 lcoe_se.tax_rate = 0.4 lcoe_se.discount_rate = 0.07 lcoe_se.construction_time = 1.0 lcoe_se.project_lifetime = 20.0 # Set plant level inputs === shearExp = 0.2 #TODO : should be an input to lcoe rotor.cdf_reference_height_wind_speed = 90.0 if not with_openwind: lcoe_se.array_losses = 0.1 lcoe_se.other_losses = 0.0 if not with_ecn_opex: lcoe_se.availability = 0.98 rotor.turbulence_class = 'B' lcoe_se.multiplier = 2.23 if wind_class == 'Offshore': # rotor.cdf_reference_mean_wind_speed = 8.4 # TODO - aep from its own module # rotor.cdf_reference_height_wind_speed = 50.0 # rotor.weibull_shape = 2.1 shearExp = 0.14 # TODO : should be an input to lcoe lcoe_se.array_losses = 0.15 if not with_ecn_opex: lcoe_se.availability = 0.96 lcoe_se.offshore = True lcoe_se.multiplier = 2.33 lcoe_se.fixed_charge_rate = 0.118 rotor.shearExp = shearExp #tower.wind1.shearExp = shearExp # not needed for jacket #tower.wind2.shearExp = shearExp # ==== from rotorse.precomp import Profile, Orthotropic2DMaterial, CompositeSection # TODO: can just pass file names and do this initialization inside of rotor #from commonse.environment import PowerWind, TowerSoil #from wisdem.reference_turbines.nrel5mw.nrel5mw_jacket import configure_nrel5mw_turbine_with_jacket from commonse.utilities import print_vars #configure_nrel5mw_turbine_with_jacket(turbine) # print_vars(turbine, list_type='inputs', prefix='turbine') rotor = lcoe_se.rotor nacelle = lcoe_se.nacelle jacket = lcoe_se.jacket # ================= # === Turbine Configuration === # --- atmosphere --- lcoe_se.rho = 1.225 # (Float, kg/m**3): density of air lcoe_se.mu = 1.81206e-5 # (Float, kg/m/s): dynamic viscosity of air lcoe_se.shear_exponent = 0.2 # (Float): shear exponent lcoe_se.hub_height = 90.0 # (Float, m): hub height lcoe_se.turbine_class = 'I' # (Enum): IEC turbine class lcoe_se.turbulence_class = 'B' # (Enum): IEC turbulence class class lcoe_se.cdf_reference_height_wind_speed = 90.0 # (Float): reference hub height for IEC wind speed (used in CDF calculation) lcoe_se.g = 9.81 # (Float, m/s**2): acceleration of gravity lcoe_se.downwind = False # (Bool): flag if rotor is downwind lcoe_se.generator_speed = 1173.7 # (Float, rpm) # generator speed lcoe_se.tower_dt = 3.87 # ---------------------- # ============================ # === rotor === # --- blade grid --- rotor.initial_aero_grid = np.array([0.02222276, 0.06666667, 0.11111057, 0.16666667, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.7, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724]) # (Array): initial aerodynamic grid on unit radius rotor.initial_str_grid = np.array([0.0, 0.00492790457512, 0.00652942887106, 0.00813095316699, 0.00983257273154, 0.0114340970275, 0.0130356213234, 0.02222276, 0.024446481932, 0.026048006228, 0.06666667, 0.089508406455, 0.11111057, 0.146462614229, 0.16666667, 0.195309105255, 0.23333333, 0.276686558545, 0.3, 0.333640766319, 0.36666667, 0.400404310407, 0.43333333, 0.5, 0.520818918408, 0.56666667, 0.602196371696, 0.63333333, 0.667358391486, 0.683573824984, 0.7, 0.73242031601, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724, 1.0]) # (Array): initial structural grid on unit radius rotor.idx_cylinder_aero = 3 # (Int): first idx in r_aero_unit of non-cylindrical section, constant twist inboard of here rotor.idx_cylinder_str = 14 # (Int): first idx in r_str_unit of non-cylindrical section rotor.hubFraction = 0.025 # (Float): hub location as fraction of radius # ------------------ # --- blade geometry --- rotor.r_aero = np.array([0.02222276, 0.06666667, 0.11111057, 0.2, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.64, 0.7, 0.83333333, 0.88888943, 0.93333333, 0.97777724]) # (Array): new aerodynamic grid on unit radius rotor.r_max_chord = 0.23577 # (Float): location of max chord on unit radius rotor.chord_sub = [3.2612, 4.5709, 3.3178, 1.4621] # (Array, m): chord at control points. defined at hub, then at linearly spaced locations from r_max_chord to tip rotor.theta_sub = [13.2783, 7.46036, 2.89317, -0.0878099] # (Array, deg): twist at control points. defined at linearly spaced locations from r[idx_cylinder] to tip rotor.precurve_sub = [0.0, 0.0, 0.0] # (Array, m): precurve at control points. defined at same locations at chord, starting at 2nd control point (root must be zero precurve) rotor.delta_precurve_sub = [0.0, 0.0, 0.0] # (Array, m): adjustment to precurve to account for curvature from loading rotor.sparT = [0.05, 0.047754, 0.045376, 0.031085, 0.0061398] # (Array, m): spar cap thickness parameters rotor.teT = [0.1, 0.09569, 0.06569, 0.02569, 0.00569] # (Array, m): trailing-edge thickness parameters rotor.bladeLength = 61.5 # (Float, m): blade length (if not precurved or swept) otherwise length of blade before curvature rotor.delta_bladeLength = 0.0 # (Float, m): adjustment to blade length to account for curvature from loading rotor.precone = 2.5 # (Float, deg): precone angle rotor.tilt = 5.0 # (Float, deg): shaft tilt rotor.yaw = 0.0 # (Float, deg): yaw error rotor.nBlades = 3 # (Int): number of blades # ------------------ # --- airfoil files --- import rotorse basepath = os.path.join(os.path.dirname(rotorse.__file__), '5MW_AFFiles') # basepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), '5MW_AFFiles') # load all airfoils airfoil_types = [0]*8 airfoil_types[0] = os.path.join(basepath, 'Cylinder1.dat') airfoil_types[1] = os.path.join(basepath, 'Cylinder2.dat') airfoil_types[2] = os.path.join(basepath, 'DU40_A17.dat') airfoil_types[3] = os.path.join(basepath, 'DU35_A17.dat') airfoil_types[4] = os.path.join(basepath, 'DU30_A17.dat') airfoil_types[5] = os.path.join(basepath, 'DU25_A17.dat') airfoil_types[6] = os.path.join(basepath, 'DU21_A17.dat') airfoil_types[7] = os.path.join(basepath, 'NACA64_A17.dat') # place at appropriate radial stations af_idx = [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7] n = len(af_idx) af = [0]*n for i in range(n): af[i] = airfoil_types[af_idx[i]] rotor.airfoil_files = af # (List): names of airfoil file # ---------------------- # --- control --- rotor.control.Vin = 3.0 # (Float, m/s): cut-in wind speed rotor.control.Vout = 25.0 # (Float, m/s): cut-out wind speed #rotor.control.ratedPower = 5e6 # (Float, W): rated power lcoe_se.machine_rating = 5e3 # (Float, kW): rated power rotor.control.minOmega = 0.0 # (Float, rpm): minimum allowed rotor rotation speed rotor.control.maxOmega = 12.0 # (Float, rpm): maximum allowed rotor rotation speed rotor.control.tsr = 7.55 # (Float): tip-speed ratio in Region 2 (should be optimized externally) rotor.control.pitch = 0.0 # (Float, deg): pitch angle in region 2 (and region 3 for fixed pitch machines) rotor.pitch_extreme = 0.0 # (Float, deg): worst-case pitch at survival wind condition rotor.azimuth_extreme = 0.0 # (Float, deg): worst-case azimuth at survival wind condition rotor.VfactorPC = 0.7 # (Float): fraction of rated speed at which the deflection is assumed to representative throughout the power curve calculation # ---------------------- # --- aero and structural analysis options --- rotor.nSector = 4 # (Int): number of sectors to divide rotor face into in computing thrust and power rotor.npts_coarse_power_curve = 20 # (Int): number of points to evaluate aero analysis at rotor.npts_spline_power_curve = 200 # (Int): number of points to use in fitting spline to power curve rotor.AEP_loss_factor = 1.0 # (Float): availability and other losses (soiling, array, etc.) rotor.drivetrainType = 'geared' # (Enum) rotor.nF = 5 # (Int): number of natural frequencies to compute rotor.dynamic_amplication_tip_deflection = 1.35 # (Float): a dynamic amplification factor to adjust the static deflection calculation # ---------------------- # --- materials and composite layup --- basepath = os.path.join(os.path.dirname(rotorse.__file__), '5MW_PreCompFiles') # basepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), '5MW_PrecompFiles') materials = Orthotropic2DMaterial.listFromPreCompFile(os.path.join(basepath, 'materials.inp')) ncomp = len(rotor.initial_str_grid) upper = [0]*ncomp lower = [0]*ncomp webs = [0]*ncomp profile = [0]*ncomp rotor.leLoc = np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.498, 0.497, 0.465, 0.447, 0.43, 0.411, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4]) # (Array): array of leading-edge positions from a reference blade axis (usually blade pitch axis). locations are normalized by the local chord length. e.g. leLoc[i] = 0.2 means leading edge is 0.2*chord[i] from reference axis. positive in -x direction for airfoil-aligned coordinate system rotor.sector_idx_strain_spar = [2]*ncomp # (Array): index of sector for spar (PreComp definition of sector) rotor.sector_idx_strain_te = [3]*ncomp # (Array): index of sector for trailing-edge (PreComp definition of sector) web1 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.4114, 0.4102, 0.4094, 0.3876, 0.3755, 0.3639, 0.345, 0.3342, 0.3313, 0.3274, 0.323, 0.3206, 0.3172, 0.3138, 0.3104, 0.307, 0.3003, 0.2982, 0.2935, 0.2899, 0.2867, 0.2833, 0.2817, 0.2799, 0.2767, 0.2731, 0.2664, 0.2607, 0.2562, 0.1886, -1.0]) web2 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.5886, 0.5868, 0.5854, 0.5508, 0.5315, 0.5131, 0.4831, 0.4658, 0.4687, 0.4726, 0.477, 0.4794, 0.4828, 0.4862, 0.4896, 0.493, 0.4997, 0.5018, 0.5065, 0.5101, 0.5133, 0.5167, 0.5183, 0.5201, 0.5233, 0.5269, 0.5336, 0.5393, 0.5438, 0.6114, -1.0]) web3 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]) rotor.chord_str_ref = np.array([3.2612, 3.3100915356, 3.32587052924, 3.34159388653, 3.35823798667, 3.37384375335, 3.38939112914, 3.4774055542, 3.49839685, 3.51343645709, 3.87017220335, 4.04645623801, 4.19408216643, 4.47641008477, 4.55844487985, 4.57383098262, 4.57285771934, 4.51914315648, 4.47677655262, 4.40075650022, 4.31069949379, 4.20483735936, 4.08985563932, 3.82931757126, 3.74220276467, 3.54415796922, 3.38732428502, 3.24931446473, 3.23421422609, 3.22701537997, 3.21972125648, 3.08979310611, 2.95152261813, 2.330753331, 2.05553464181, 1.82577817774, 1.5860853279, 1.4621]) # (Array, m): chord distribution for reference section, thickness of structural layup scaled with reference thickness (fixed t/c for this case) for i in range(ncomp): webLoc = [] if web1[i] != -1: webLoc.append(web1[i]) if web2[i] != -1: webLoc.append(web2[i]) if web3[i] != -1: webLoc.append(web3[i]) upper[i], lower[i], webs[i] = CompositeSection.initFromPreCompLayupFile(os.path.join(basepath, 'layup_' + str(i+1) + '.inp'), webLoc, materials) profile[i] = Profile.initFromPreCompFile(os.path.join(basepath, 'shape_' + str(i+1) + '.inp')) rotor.materials = materials # (List): list of all Orthotropic2DMaterial objects used in defining the geometry rotor.upperCS = upper # (List): list of CompositeSection objections defining the properties for upper surface rotor.lowerCS = lower # (List): list of CompositeSection objections defining the properties for lower surface rotor.websCS = webs # (List): list of CompositeSection objections defining the properties for shear webs rotor.profile = profile # (List): airfoil shape at each radial position # -------------------------------------- # --- fatigue --- rotor.rstar_damage = np.array([0.000, 0.022, 0.067, 0.111, 0.167, 0.233, 0.300, 0.367, 0.433, 0.500, 0.567, 0.633, 0.700, 0.767, 0.833, 0.889, 0.933, 0.978]) # (Array): nondimensional radial locations of damage equivalent moments rotor.Mxb_damage = 1e3*np.array([2.3743E+003, 2.0834E+003, 1.8108E+003, 1.5705E+003, 1.3104E+003, 1.0488E+003, 8.2367E+002, 6.3407E+002, 4.7727E+002, 3.4804E+002, 2.4458E+002, 1.6339E+002, 1.0252E+002, 5.7842E+001, 2.7349E+001, 1.1262E+001, 3.8549E+000, 4.4738E-001]) # (Array, N*m): damage equivalent moments about blade c.s. x-direction rotor.Myb_damage = 1e3*np.array([2.7732E+003, 2.8155E+003, 2.6004E+003, 2.3933E+003, 2.1371E+003, 1.8459E+003, 1.5582E+003, 1.2896E+003, 1.0427E+003, 8.2015E+002, 6.2449E+002, 4.5229E+002, 3.0658E+002, 1.8746E+002, 9.6475E+001, 4.2677E+001, 1.5409E+001, 1.8426E+000]) # (Array, N*m): damage equivalent moments about blade c.s. y-direction rotor.strain_ult_spar = 1.0e-2 # (Float): ultimate strain in spar cap rotor.strain_ult_te = 2500*1e-6 * 2 # (Float): uptimate strain in trailing-edge panels, note that I am putting a factor of two for the damage part only. rotor.eta_damage = 1.35*1.3*1.0 # (Float): safety factor for fatigue rotor.m_damage = 10.0 # (Float): slope of S-N curve for fatigue analysis rotor.N_damage = 365*24*3600*20.0 # (Float): number of cycles used in fatigue analysis TODO: make function of rotation speed # ---------------- # ================= # === nacelle ====== nacelle.L_ms = 1.0 # (Float, m): main shaft length downwind of main bearing in low-speed shaft nacelle.L_mb = 2.5 # (Float, m): main shaft length in low-speed shaft nacelle.h0_front = 1.7 # (Float, m): height of Ibeam in bedplate front nacelle.h0_rear = 1.35 # (Float, m): height of Ibeam in bedplate rear # TODO: sync with rotor drivetrainType variable nacelle.drivetrain_design = 'geared' nacelle.crane = True # (Bool): flag for presence of crane nacelle.bevel = 0 # (Int): Flag for the presence of a bevel stage - 1 if present, 0 if not nacelle.gear_configuration = 'eep' # (Str): tring that represents the configuration of the gearbox (stage number and types) nacelle.Np = [3, 3, 1] # (Array): number of planets in each stage nacelle.ratio_type = 'optimal' # (Str): optimal or empirical stage ratios nacelle.shaft_type = 'normal' # (Str): normal or short shaft length #nacelle.shaft_angle = 5.0 # (Float, deg): Angle of the LSS inclindation with respect to the horizontal nacelle.shaft_ratio = 0.10 # (Float): Ratio of inner diameter to outer diameter. Leave zero for solid LSS #nacelle.shrink_disc_mass = 1000.0 # (Float, kg): Mass of the shrink disc nacelle.mb1Type = 'CARB' # (Str): Main bearing type: CARB, TRB or SRB nacelle.mb2Type = 'SRB' # (Str): Second bearing type: CARB, TRB or SRB nacelle.yaw_motors_number = 8.0 # (Float): number of yaw motors nacelle.uptower_transformer = True nacelle.flange_length = 0.5 #m nacelle.gearbox_cm = 0.1 nacelle.hss_length = 1.5 nacelle.overhang = 5.0 #TODO - should come from turbine configuration level nacelle.check_fatigue = 0 #0 if no fatigue check, 1 if parameterized fatigue check, 2 if known loads inputs # TODO: should come from rotor (these are FAST outputs) nacelle.DrivetrainEfficiency = 0.95 nacelle.rotor_bending_moment_x = 330770.0# Nm nacelle.rotor_bending_moment_y = -16665000.0 # Nm nacelle.rotor_bending_moment_z = 2896300.0 # Nm nacelle.rotor_force_x = 599610.0 # N nacelle.rotor_force_y = 186780.0 # N nacelle.rotor_force_z = -842710.0 # N #nacelle.h0_rear = 1.35 # only used in drive smooth #nacelle.h0_front = 1.7 # ================= # === jacket === #--- Set Jacket Input Parameters ---# Jcktins=JcktGeoInputs() Jcktins.nlegs =4 Jcktins.nbays =5 Jcktins.batter=12. Jcktins.dck_botz =16. Jcktins.dck_width=2*6. Jcktins.weld2D =0.5 Jcktins.VPFlag = True #vertical pile T/F; to enable piles in frame3DD set pileinputs.ndiv>0 Jcktins.clamped= False #whether or not the bottom of the structure is rigidly connected. Use False when equivalent spring constants are being used. Jcktins.AFflag = False #whether or not to use apparent fixity piles Jcktins.PreBuildTPLvl = 5 #if >0, the TP is prebuilt according to rules per PreBuildTP #Soil inputs Soilinputs=SoilGeoInputs() Soilinputs.zbots =-np.array([3.,5.,7.,15.,30.,50.]) Soilinputs.gammas =np.array([10000.,10000.,10000.,10000.,10000.,10000.]) Soilinputs.cus =np.array([60000.,60000.,60000.,60000.,60000.,60000.]) Soilinputs.phis =np.array([26.,26.,26.,26.,26.,26])#np.array([36.,33.,26.,37.,35.,37.5])#np.array([36.,33.,26.,37.,35.,37.5]) Soilinputs.delta =25. Soilinputs.sndflg =True Soilinputs.PenderSwtch =False #True Soilinputs.SoilSF =1. Soilinputs2=copy.copy(Soilinputs) #Parked case. We assume same stiffness although this may not be the case under a different load #Water and wind inputs Waterinputs=WaterInputs() Waterinputs.wdepth =30. Waterinputs.wlevel =30. #Distance from bottom of structure to surface THIS, I believe is no longer needed as piles may be negative in z, to check and remove in case Waterinputs.T=12. #Wave Period Waterinputs.HW=10. #Wave Height Waterinputs.Cd=3. #Drag Coefficient, enhanced to account for marine growth and other members not calculated Waterinputs.Cm=8.#2. #ADded mass Coefficient Waterinputs2=copy.copy(Waterinputs) #PARKED CONDITIONS - still max wave here Waterinputs.T=8. #Wave Period Waterinputs.HW=4. #Wave Height Windinputs=WindInputs() Windinputs.Cdj=4. #Drag Coefficient for jacket members, enhanced to account for TP drag not calculated otherwise Windinputs.Cdt=2 #Drag Coefficient for tower, enhanced to account for TP drag not calculated otherwise Windinputs.HH=100. #CHECK HOW THIS COMPLIES.... Windinputs.U50HH=30. #assumed gust speed ## if turbine_jacket ##Windinputs.HH=90. #CHECK HOW THIS COMPLIES.... ##Windinputs.U50HH=11.7373200354 # using rated loads ##Windinputs.rho = 1.225 ##Windinputs.mu = 1.81206e-05 Windinputs2=copy.copy(Windinputs) Windinputs2.U50HH=70. #assumed gust speed #Pile data Pilematin=MatInputs() Pilematin.matname=np.array(['steel']) Pilematin.E=np.array([ 25.e9]) Dpile=2.5#0.75 # 2.0 tpile=0.01 Lp=20. #45 Pileinputs=PileGeoInputs() Pileinputs.Pilematins=Pilematin Pileinputs.ndiv=0 #3 Pileinputs.Dpile=Dpile Pileinputs.tpile=tpile Pileinputs.Lp=Lp #[m] Embedment length #Legs data legmatin=MatInputs() legmatin.matname=(['heavysteel','heavysteel','heavysteel','heavysteel']) #legmatin.E=np.array([2.0e11]) Dleg=np.array([1.5,1.5,1.5,1.5,1.5,1.5]) tleg=1.5*np.array([0.0254]).repeat(Dleg.size) leginputs=LegGeoInputs() leginputs.legZbot = 1.0 leginputs.ndiv=1 leginputs.legmatins=legmatin leginputs.Dleg0=Dleg[0] leginputs.tleg0=tleg[0] legbot_stmphin =1.5 #Distance from bottom of leg to second joint along z; must be>0 #Xbrc data Xbrcmatin=MatInputs() Xbrcmatin.matname=np.array(['heavysteel']).repeat(Jcktins.nbays) #Xbrcmatin.E=np.array([ 2.2e11, 2.0e11,2.0e11,2.0e11,2.0e11]) Dbrc=np.array([1.,1.,1.0,1.0,1.0]) tbrc=np.array([1.,1.,1.0,1.0,1.0])*0.0254 Xbrcinputs=XBrcGeoInputs() Xbrcinputs.Dbrc0=Dbrc[0] Xbrcinputs.tbrc0=tbrc[0] Xbrcinputs.ndiv=2#2 Xbrcinputs.Xbrcmatins=Xbrcmatin Xbrcinputs.precalc=False #True #This can be set to true if we want Xbraces to be precalculated in D and t, in which case the above set Dbrc and tbrc would be overwritten #Mbrc data Mbrcmatin=MatInputs() Mbrcmatin.matname=np.array(['heavysteel']) #Mbrcmatin.E=np.array([ 2.5e11]) Dbrc_mud=1.5 Mbrcinputs=MudBrcGeoInputs() Mbrcinputs.Dbrc_mud=Dbrc_mud Mbrcinputs.ndiv=2 Mbrcinputs.Mbrcmatins=Mbrcmatin Mbrcinputs.precalc=False #True #This can be set to true if we want Mudbrace to be precalculated in D and t, in which case the above set Dbrc_mud and tbrc_mud would be overwritten #Hbrc data Hbrcmatin=MatInputs() Hbrcmatin.matname=np.array(['heavysteel']) Hbrcmatin.E=np.array([ 2.5e11]) Dbrc_hbrc=1.1 Hbrcinputs=HBrcGeoInputs() Hbrcinputs.Dbrch=Dbrc_hbrc Hbrcinputs.ndiv=0#2 Hbrcinputs.Hbrcmatins=Hbrcmatin Hbrcinputs.precalc=True #This can be set to true if we want Hbrace to be set=Xbrace top D and t, in which case the above set Dbrch and tbrch would be overwritten #TP data TPlumpinputs=TPlumpMass() TPlumpinputs.mass=200.e3 #[kg] TPstmpsmatin=MatInputs() TPbrcmatin=MatInputs() TPstemmatin=MatInputs() TPbrcmatin.matname=np.array(['heavysteel']) #TPbrcmatin.E=np.array([ 2.5e11]) TPstemmatin.matname=np.array(['heavysteel']).repeat(2) #TPstemmatin.E=np.array([ 2.1e11]).repeat(2) TPinputs=TPGeoInputs() TPinputs.TPbrcmatins=TPbrcmatin TPinputs.TPstemmatins=TPstemmatin TPinputs.TPstmpmatins=TPstmpsmatin TPinputs.Dstrut=leginputs.Dleg[-1] TPinputs.tstrut=leginputs.tleg[-1] TPinputs.Dgir=Dbrc_hbrc TPinputs.tgir=0.0254 TPinputs.Dbrc=1.1 TPinputs.Dbrc=TPinputs.Dgir TPinputs.tbrc=TPinputs.tgir TPinputs.hstump=1.0#1.0 TPinputs.Dstump=1.25#1.0 TPinputs.stumpndiv=1#2 TPinputs.brcndiv=1#2 TPinputs.girndiv=1#2 TPinputs.strutndiv=1#2 TPinputs.stemndiv=1#2 TPinputs.nstems=3 TPinputs.Dstem=np.array([6.]).repeat(TPinputs.nstems) TPinputs.tstem=np.array([0.1,0.11,0.11]) TPinputs.hstem=np.array([6./TPinputs.nstems]).repeat(TPinputs.nstems) #Tower data Twrmatin=MatInputs() Twrmatin.matname=np.array(['heavysteel']) #Twrmatin.E=np.array([ 2.77e11]) Twrinputs=TwrGeoInputs() Twrinputs.Twrmatins=Twrmatin #Twrinputs.Htwr=70. #Trumped by HH Twrinputs.Htwr2frac=0.2 #fraction of tower height with constant x-section Twrinputs.ndiv=np.array([6,12]) #ndiv for uniform and tapered section Twrinputs.DeltaZmax= 6. #[m], maximum FE element length allowed in the tower members (i.e. the uniform and the tapered members) Twrinputs.Db=5.6 Twrinputs.DTRb=130. Twrinputs.DTRt=150. Twrinputs.Dt=0.55*Twrinputs.Db ## if turbine_jacket ##Twrinputs.Dt = 3.87 TwrRigidTop=True #False #False=Account for RNA via math rather than a physical rigidmember #RNA data RNAins=RNAprops() RNAins.mass=3*350.e3 RNAins.I[0]=86.579E+6 RNAins.I[1]=53.530E+6 RNAins.I[2]=58.112E+6 RNAins.CMoff[2]=2.34 RNAins.yawangle=45. #angle with respect to global X, CCW looking from above, wind from left RNAins.rna_weightM=True ## if turbine_jacket ##RNAins.mass=285598.806453 ##RNAins.I = np.array([1.14930678e8, 2.20354030e7, 1.87597425e7, 0.0, 5.03710467e5, 0.0]) ##RNAins.CMoff = np.array([-1.13197635, 0.0, 0.50875268]) ##RNAins.yawangle=0.0 #angle with respect to global X, CCW looking from above, wind from left #RNAins.rna_weightM=True RNAins2=copy.copy(RNAins) #PARKED CASE, for now assume the same #RNA loads Fx-z, Mxx-zz RNA_F=np.array([1000.e3,0.,0.,0.,0.,0.]) #operational RNA_F2=np.array([500.e3,0.,0.,0.,0.,0.]) #Parked ## if turbine_jacket ##RNA_F=np.array([1284744.19620519,0.,-2914124.84400512,3963732.76208099,-2275104.79420872,-346781.68192839]) #Frame3DD parameters FrameAuxIns=Frame3DDaux() FrameAuxIns.sh_fg=1 #shear flag-->Timoshenko FrameAuxIns.deltaz=5. FrameAuxIns.geo_fg=0 FrameAuxIns.nModes = 6 # number of desired dynamic modes of vibration FrameAuxIns.Mmethod = 1 # 1: subspace Jacobi 2: Stodola FrameAuxIns.lump = 0 # 0: consistent mass ... 1: lumped mass matrix FrameAuxIns.tol = 1e-9 # mode shape tolerance FrameAuxIns.shift = 0.0 # shift value ... for unrestrained structures FrameAuxIns.gvector=np.array([0.,0.,-9.8065]) #GRAVITY ## if turbine_jacket ##FrameAuxIns.gvector=np.array([0.,0.,-9.81]) #GRAVITY #Decide whether or not to consider DLC 6.1 as well twodlcs=False #-----Launch the assembly-----# #turbine.jacket=JacketSE(Jcktins.clamped,Jcktins.AFflag,twodlcs=twodlcs) #turbine.jacket=set_as_top(JacketSE(Jcktins.clamped,Jcktins.AFflag,twodlcs=twodlcs)) ##(Jcktins.PreBuildTPLvl>0), #Pass all inputs to assembly lcoe_se.jacket.JcktGeoIn=Jcktins lcoe_se.jacket.Soilinputs=Soilinputs lcoe_se.jacket.Soilinputs2=Soilinputs2 #Parked conditions lcoe_se.jacket.Waterinputs=Waterinputs lcoe_se.jacket.Windinputs=Windinputs lcoe_se.jacket.RNA_F=RNA_F lcoe_se.jacket.Waterinputs2=Waterinputs2 #Parked conditions lcoe_se.jacket.Windinputs2=Windinputs2 #Parked conditions lcoe_se.jacket.RNA_F2=RNA_F2 #Parked conditions lcoe_se.jacket.Pileinputs=Pileinputs lcoe_se.jacket.leginputs=leginputs #lcoe_se.jacket.legbot_stmphin =legbot_stmphin lcoe_se.jacket.Xbrcinputs=Xbrcinputs lcoe_se.jacket.Mbrcinputs=Mbrcinputs lcoe_se.jacket.Hbrcinputs=Hbrcinputs lcoe_se.jacket.TPlumpinputs=TPlumpinputs lcoe_se.jacket.TPinputs=TPinputs lcoe_se.jacket.RNAinputs=RNAins lcoe_se.jacket.RNAinputs2=RNAins2 lcoe_se.jacket.Twrinputs=Twrinputs lcoe_se.jacket.TwrRigidTop=TwrRigidTop lcoe_se.jacket.FrameAuxIns=FrameAuxIns # === Run default assembly and print results lcoe_se.run() # ==== # === Print === print "Key Turbine Outputs for NREL 5 MW Reference Turbine" print 'mass rotor blades:{0:.2f} (kg) '.format(lcoe_se.rotor.mass_all_blades) print 'mass hub system: {0:.2f} (kg) '.format(lcoe_se.hub.hub_system_mass) print 'mass nacelle: {0:.2f} (kg) '.format(lcoe_se.nacelle.nacelle_mass) print 'mass tower: {0:.2f} (kg) '.format(lcoe_se.jacket.Tower.Twrouts.mass) print 'maximum tip deflection: {0:.2f} (m) '.format(lcoe_se.maxdeflection.max_tip_deflection) print 'ground clearance: {0:.2f} (m) '.format(lcoe_se.maxdeflection.ground_clearance) print print "Key Plant Outputs for wind plant with NREL 5 MW Turbine" #print "LCOE: ${0:.4f} USD/kWh".format(lcoe_se.lcoe) # not in base output set (add to assembly output if desired) print "COE: ${0:.4f} USD/kWh".format(lcoe_se.coe) print print "AEP per turbine: {0:.1f} kWh/turbine".format(lcoe_se.net_aep / lcoe_se.turbine_number) print "Turbine Cost: ${0:.2f} USD".format(lcoe_se.turbine_cost) print "BOS costs per turbine: ${0:.2f} USD/turbine".format(lcoe_se.bos_costs / lcoe_se.turbine_number) print "OPEX per turbine: ${0:.2f} USD/turbine".format(lcoe_se.avg_annual_opex / lcoe_se.turbine_number)
def configure_nrel5mw_turbine(turbine, wind_class="I", sea_depth=0.0): """ Inputs: rotor = RotorSE() nacelle = DriveSE() tower = TowerSE() wind_class : str ('I', 'III', 'Offshore' - selected wind class for project) sea_depth : float (sea depth if an offshore wind plant) """ # === Turbine === turbine.rho = 1.225 # (Float, kg/m**3): density of air turbine.mu = 1.81206e-5 # (Float, kg/m/s): dynamic viscosity of air turbine.shear_exponent = 0.2 # (Float): shear exponent turbine.hub_height = 90.0 # (Float, m): hub height turbine.turbine_class = "I" # (Enum): IEC turbine class turbine.turbulence_class = "B" # (Enum): IEC turbulence class class turbine.cdf_reference_height_wind_speed = ( 90.0 ) # (Float): reference hub height for IEC wind speed (used in CDF calculation) turbine.g = 9.81 # (Float, m/s**2): acceleration of gravity # ====================== # === rotor === # --- blade grid --- turbine.rotor.initial_aero_grid = np.array( [ 0.02222276, 0.06666667, 0.11111057, 0.16666667, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.7, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724, ] ) # (Array): initial aerodynamic grid on unit radius turbine.rotor.initial_str_grid = np.array( [ 0.0, 0.00492790457512, 0.00652942887106, 0.00813095316699, 0.00983257273154, 0.0114340970275, 0.0130356213234, 0.02222276, 0.024446481932, 0.026048006228, 0.06666667, 0.089508406455, 0.11111057, 0.146462614229, 0.16666667, 0.195309105255, 0.23333333, 0.276686558545, 0.3, 0.333640766319, 0.36666667, 0.400404310407, 0.43333333, 0.5, 0.520818918408, 0.56666667, 0.602196371696, 0.63333333, 0.667358391486, 0.683573824984, 0.7, 0.73242031601, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724, 1.0, ] ) # (Array): initial structural grid on unit radius turbine.rotor.idx_cylinder_aero = ( 3 ) # (Int): first idx in r_aero_unit of non-cylindrical section, constant twist inboard of here turbine.rotor.idx_cylinder_str = 14 # (Int): first idx in r_str_unit of non-cylindrical section turbine.rotor.hubFraction = 0.025 # (Float): hub location as fraction of radius # ------------------ # --- blade geometry --- turbine.rotor.r_aero = np.array( [ 0.02222276, 0.06666667, 0.11111057, 0.2, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.64, 0.7, 0.83333333, 0.88888943, 0.93333333, 0.97777724, ] ) # (Array): new aerodynamic grid on unit radius turbine.rotor.r_max_chord = 0.23577 # (Float): location of max chord on unit radius turbine.rotor.chord_sub = [ 3.2612, 4.5709, 3.3178, 1.4621, ] # (Array, m): chord at control points. defined at hub, then at linearly spaced locations from r_max_chord to tip turbine.rotor.theta_sub = [ 13.2783, 7.46036, 2.89317, -0.0878099, ] # (Array, deg): twist at control points. defined at linearly spaced locations from r[idx_cylinder] to tip turbine.rotor.precurve_sub = [ 0.0, 0.0, 0.0, ] # (Array, m): precurve at control points. defined at same locations at chord, starting at 2nd control point (root must be zero precurve) turbine.rotor.delta_precurve_sub = [ 0.0, 0.0, 0.0, ] # (Array, m): adjustment to precurve to account for curvature from loading turbine.rotor.sparT = [0.05, 0.047754, 0.045376, 0.031085, 0.0061398] # (Array, m): spar cap thickness parameters turbine.rotor.teT = [0.1, 0.09569, 0.06569, 0.02569, 0.00569] # (Array, m): trailing-edge thickness parameters turbine.rotor.bladeLength = ( 61.5 ) # (Float, m): blade length (if not precurved or swept) otherwise length of blade before curvature turbine.rotor.delta_bladeLength = ( 0.0 ) # (Float, m): adjustment to blade length to account for curvature from loading turbine.rotor.precone = 2.5 # (Float, deg): precone angle turbine.rotor.tilt = 5.0 # (Float, deg): shaft tilt turbine.rotor.yaw = 0.0 # (Float, deg): yaw error turbine.rotor.nBlades = 3 # (Int): number of blades # ------------------ # --- airfoil files --- import rotorse # basepath = os.path.join('5MW_files', '5MW_AFFiles') basepath = os.path.join("..", "reference_turbines", "nrel5mw", "airfoils") # load all airfoils airfoil_types = [0] * 8 airfoil_types[0] = os.path.join(basepath, "Cylinder1.dat") airfoil_types[1] = os.path.join(basepath, "Cylinder2.dat") airfoil_types[2] = os.path.join(basepath, "DU40_A17.dat") airfoil_types[3] = os.path.join(basepath, "DU35_A17.dat") airfoil_types[4] = os.path.join(basepath, "DU30_A17.dat") airfoil_types[5] = os.path.join(basepath, "DU25_A17.dat") airfoil_types[6] = os.path.join(basepath, "DU21_A17.dat") airfoil_types[7] = os.path.join(basepath, "NACA64_A17.dat") # place at appropriate radial stations af_idx = [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7] n = len(af_idx) af = [0] * n for i in range(n): af[i] = airfoil_types[af_idx[i]] turbine.rotor.airfoil_files = af # (List): names of airfoil file # ---------------------- # --- control --- turbine.rotor.control.Vin = 3.0 # (Float, m/s): cut-in wind speed turbine.rotor.control.Vout = 25.0 # (Float, m/s): cut-out wind speed turbine.rotor.control.ratedPower = 5e6 # (Float, W): rated power turbine.rotor.control.minOmega = 0.0 # (Float, rpm): minimum allowed rotor rotation speed turbine.rotor.control.maxOmega = 12.0 # (Float, rpm): maximum allowed rotor rotation speed turbine.rotor.control.tsr = 7.55 # (Float): tip-speed ratio in Region 2 (should be optimized externally) turbine.rotor.control.pitch = 0.0 # (Float, deg): pitch angle in region 2 (and region 3 for fixed pitch machines) turbine.rotor.pitch_extreme = 0.0 # (Float, deg): worst-case pitch at survival wind condition turbine.rotor.azimuth_extreme = 0.0 # (Float, deg): worst-case azimuth at survival wind condition turbine.rotor.VfactorPC = ( 0.7 ) # (Float): fraction of rated speed at which the deflection is assumed to representative throughout the power curve calculation # ---------------------- # --- aero and structural analysis options --- turbine.rotor.nSector = 4 # (Int): number of sectors to divide rotor face into in computing thrust and power turbine.rotor.npts_coarse_power_curve = 20 # (Int): number of points to evaluate aero analysis at turbine.rotor.npts_spline_power_curve = 200 # (Int): number of points to use in fitting spline to power curve turbine.rotor.AEP_loss_factor = 1.0 # (Float): availability and other losses (soiling, array, etc.) turbine.rotor.drivetrainType = "geared" # (Enum) turbine.rotor.nF = 5 # (Int): number of natural frequencies to compute turbine.rotor.dynamic_amplication_tip_deflection = ( 1.35 ) # (Float): a dynamic amplification factor to adjust the static deflection calculation # ---------------------- # --- materials and composite layup --- # basepath = os.path.join('5MW_files', '5MW_PrecompFiles') basepath = os.path.join("..", "reference_turbines", "nrel5mw", "blade") materials = Orthotropic2DMaterial.listFromPreCompFile(os.path.join(basepath, "materials.inp")) ncomp = len(turbine.rotor.initial_str_grid) upper = [0] * ncomp lower = [0] * ncomp webs = [0] * ncomp profile = [0] * ncomp turbine.rotor.leLoc = np.array( [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.498, 0.497, 0.465, 0.447, 0.43, 0.411, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, ] ) # (Array): array of leading-edge positions from a reference blade axis (usually blade pitch axis). locations are normalized by the local chord length. e.g. leLoc[i] = 0.2 means leading edge is 0.2*chord[i] from reference axis. positive in -x direction for airfoil-aligned coordinate system turbine.rotor.sector_idx_strain_spar = [ 2 ] * ncomp # (Array): index of sector for spar (PreComp definition of sector) turbine.rotor.sector_idx_strain_te = [ 3 ] * ncomp # (Array): index of sector for trailing-edge (PreComp definition of sector) web1 = np.array( [ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.4114, 0.4102, 0.4094, 0.3876, 0.3755, 0.3639, 0.345, 0.3342, 0.3313, 0.3274, 0.323, 0.3206, 0.3172, 0.3138, 0.3104, 0.307, 0.3003, 0.2982, 0.2935, 0.2899, 0.2867, 0.2833, 0.2817, 0.2799, 0.2767, 0.2731, 0.2664, 0.2607, 0.2562, 0.1886, -1.0, ] ) web2 = np.array( [ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.5886, 0.5868, 0.5854, 0.5508, 0.5315, 0.5131, 0.4831, 0.4658, 0.4687, 0.4726, 0.477, 0.4794, 0.4828, 0.4862, 0.4896, 0.493, 0.4997, 0.5018, 0.5065, 0.5101, 0.5133, 0.5167, 0.5183, 0.5201, 0.5233, 0.5269, 0.5336, 0.5393, 0.5438, 0.6114, -1.0, ] ) web3 = np.array( [ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, ] ) turbine.rotor.chord_str_ref = np.array( [ 3.2612, 3.3100915356, 3.32587052924, 3.34159388653, 3.35823798667, 3.37384375335, 3.38939112914, 3.4774055542, 3.49839685, 3.51343645709, 3.87017220335, 4.04645623801, 4.19408216643, 4.47641008477, 4.55844487985, 4.57383098262, 4.57285771934, 4.51914315648, 4.47677655262, 4.40075650022, 4.31069949379, 4.20483735936, 4.08985563932, 3.82931757126, 3.74220276467, 3.54415796922, 3.38732428502, 3.24931446473, 3.23421422609, 3.22701537997, 3.21972125648, 3.08979310611, 2.95152261813, 2.330753331, 2.05553464181, 1.82577817774, 1.5860853279, 1.4621, ] ) # (Array, m): chord distribution for reference section, thickness of structural layup scaled with reference thickness (fixed t/c for this case) for i in range(ncomp): webLoc = [] if web1[i] != -1: webLoc.append(web1[i]) if web2[i] != -1: webLoc.append(web2[i]) if web3[i] != -1: webLoc.append(web3[i]) upper[i], lower[i], webs[i] = CompositeSection.initFromPreCompLayupFile( os.path.join(basepath, "layup_" + str(i + 1) + ".inp"), webLoc, materials ) profile[i] = Profile.initFromPreCompFile(os.path.join(basepath, "shape_" + str(i + 1) + ".inp")) turbine.rotor.materials = ( materials ) # (List): list of all Orthotropic2DMaterial objects used in defining the geometry turbine.rotor.upperCS = ( upper ) # (List): list of CompositeSection objections defining the properties for upper surface turbine.rotor.lowerCS = ( lower ) # (List): list of CompositeSection objections defining the properties for lower surface turbine.rotor.websCS = webs # (List): list of CompositeSection objections defining the properties for shear webs turbine.rotor.profile = profile # (List): airfoil shape at each radial position # -------------------------------------- strain_ult_spar = 1.0e-2 strain_ult_te = 2500 * 1e-6 # --- fatigue --- turbine.rotor.rstar_damage = np.array( [ 0.000, 0.022, 0.067, 0.111, 0.167, 0.233, 0.300, 0.367, 0.433, 0.500, 0.567, 0.633, 0.700, 0.767, 0.833, 0.889, 0.933, 0.978, ] ) # (Array): nondimensional radial locations of damage equivalent moments turbine.rotor.Mxb_damage = 1e3 * np.array( [ 2.3743e003, 2.0834e003, 1.8108e003, 1.5705e003, 1.3104e003, 1.0488e003, 8.2367e002, 6.3407e002, 4.7727e002, 3.4804e002, 2.4458e002, 1.6339e002, 1.0252e002, 5.7842e001, 2.7349e001, 1.1262e001, 3.8549e000, 4.4738e-001, ] ) # (Array, N*m): damage equivalent moments about blade c.s. x-direction turbine.rotor.Myb_damage = 1e3 * np.array( [ 2.7732e003, 2.8155e003, 2.6004e003, 2.3933e003, 2.1371e003, 1.8459e003, 1.5582e003, 1.2896e003, 1.0427e003, 8.2015e002, 6.2449e002, 4.5229e002, 3.0658e002, 1.8746e002, 9.6475e001, 4.2677e001, 1.5409e001, 1.8426e000, ] ) # (Array, N*m): damage equivalent moments about blade c.s. y-direction turbine.rotor.strain_ult_spar = 1.0e-2 # (Float): ultimate strain in spar cap turbine.rotor.strain_ult_te = ( 2500 * 1e-6 * 2 ) # (Float): uptimate strain in trailing-edge panels, note that I am putting a factor of two for the damage part only. turbine.rotor.eta_damage = 1.35 * 1.3 * 1.0 # (Float): safety factor for fatigue turbine.rotor.m_damage = 10.0 # (Float): slope of S-N curve for fatigue analysis turbine.rotor.N_damage = ( 365 * 24 * 3600 * 20.0 ) # (Float): number of cycles used in fatigue analysis TODO: make function of rotation speed # ---------------- # ================= # === nacelle ====== turbine.nacelle.L_ms = 1.0 # (Float, m): main shaft length downwind of main bearing in low-speed shaft turbine.nacelle.L_mb = 2.5 # (Float, m): main shaft length in low-speed shaft turbine.nacelle.h0_front = 1.7 # (Float, m): height of Ibeam in bedplate front turbine.nacelle.h0_rear = 1.35 # (Float, m): height of Ibeam in bedplate rear turbine.nacelle.drivetrain_design = "geared" turbine.nacelle.crane = True # (Bool): flag for presence of crane turbine.nacelle.bevel = 0 # (Int): Flag for the presence of a bevel stage - 1 if present, 0 if not turbine.nacelle.gear_configuration = ( "eep" ) # (Str): tring that represents the configuration of the gearbox (stage number and types) turbine.nacelle.Np = [3, 3, 1] # (Array): number of planets in each stage turbine.nacelle.ratio_type = "optimal" # (Str): optimal or empirical stage ratios turbine.nacelle.shaft_type = "normal" # (Str): normal or short shaft length # turbine.nacelle.shaft_angle = 5.0 # (Float, deg): Angle of the LSS inclindation with respect to the horizontal turbine.nacelle.shaft_ratio = 0.10 # (Float): Ratio of inner diameter to outer diameter. Leave zero for solid LSS turbine.nacelle.carrier_mass = 8000.0 # estimated for 5 MW turbine.nacelle.mb1Type = "CARB" # (Str): Main bearing type: CARB, TRB or SRB turbine.nacelle.mb2Type = "SRB" # (Str): Second bearing type: CARB, TRB or SRB turbine.nacelle.yaw_motors_number = 8.0 # (Float): number of yaw motors turbine.nacelle.uptower_transformer = True turbine.nacelle.flange_length = 0.5 # m turbine.nacelle.gearbox_cm = 0.1 turbine.nacelle.hss_length = 1.5 turbine.nacelle.overhang = 5.0 # TODO - should come from turbine configuration level turbine.nacelle.check_fatigue = ( 0 ) # 0 if no fatigue check, 1 if parameterized fatigue check, 2 if known loads inputs # TODO: should come from rotor (these are FAST outputs) turbine.nacelle.DrivetrainEfficiency = 0.95 # turbine.nacelle.rotor_bending_moment_x = 330770.0# Nm # turbine.nacelle.rotor_bending_moment_y = -16665000.0 # Nm # turbine.nacelle.rotor_bending_moment_z = 2896300.0 # Nm # turbine.nacelle.rotor_force_x = 599610.0 # N # turbine.nacelle.rotor_force_y = 186780.0 # N # turbine.nacelle.rotor_force_z = -842710.0 # N''' # turbine.nacelle.h0_rear = 1.35 # only used in drive smooth # turbine.nacelle.h0_front = 1.7 # ================= # === tower === # ---- tower ------ turbine.tower.replace("wind1", PowerWind()) turbine.tower.replace("wind2", PowerWind()) # onshore (no waves) if turbine.sea_depth <> 0.0: turbine.tower.replace("wave1", LinearWaves()) turbine.tower.replace("wave2", LinearWaves()) turbine.tower.wave1.Uc = 0.0 turbine.tower.wave1.hs = 8.0 * 1.86 turbine.tower.wave1.T = 10.0 turbine.tower.wave1.z_surface = 0.0 turbine.tower.wave1.z_floor = -sea_depth turbine.tower.wave1.g = 9.81 turbine.tower.wave1.betaWave = 0.0 turbine.tower.wave2.Uc = 0.0 turbine.tower.wave2.hs = 8.0 * 1.86 turbine.tower.wave2.T = 10.0 turbine.tower.wave2.z_surface = 0.0 turbine.tower.wave2.z_floor = -sea_depth turbine.tower.wave2.g = 9.81 turbine.tower.wave2.betaWave = 0.0 if turbine.sea_depth == 0.0: # --- geometry ---- # np.insert(turbine.tower.z_param,0,87.9) # np.insert(turbine.tower.z_param,0,43.8) # np.insert(turbine.tower.z_param,0,0.0) turbine.tower.z_param = [0.0, 43.8, 87.9] turbine.tower_d = [6.0, 4.935, 3.87] turbine.tower.t_param = [0.027 * 1.3, 0.023 * 1.3, 0.019 * 1.3] n = 15 turbine.tower.z_full = np.linspace(0.0, 87.6, n) turbine.tower.L_reinforced = 30.0 * np.ones(n) # [m] buckling length turbine.tower.theta_stress = 0.0 * np.ones(n) turbine.tower.yaw = 0.0 # --- material props --- turbine.tower.E = 210e9 * np.ones(n) turbine.tower.G = 80.8e9 * np.ones(n) turbine.tower.rho = 8500.0 * np.ones(n) turbine.tower.sigma_y = 450.0e6 * np.ones(n) else: # --- geometry ---- # np.insert(turbine.tower.z_param,0,87.9) # np.insert(turbine.tower.z_param,0,43.8) # np.insert(turbine.tower.z_param,0,0.0) # np.insert(turbine.tower.z_param,0,-20.0) turbine.tower.z_param = [-20.0, 0.0, 43.8, 87.9] turbine.tower_d = [6.0, 6.0, 4.935, 3.87] turbine.tower.t_param = [0.06, 0.027 * 1.3, 0.023 * 1.3, 0.019 * 1.3] n = 20 turbine.tower.z_full = np.linspace(-20, 87.6, n) turbine.tower.L_reinforced = 30.0 * np.ones(n) # [m] buckling length turbine.tower.theta_stress = 0.0 * np.ones(n) turbine.tower.yaw = 0.0 # --- material props --- turbine.tower.E = 210e9 * np.ones(n) turbine.tower.G = 80.8e9 * np.ones(n) turbine.tower.rho = 8500.0 * np.ones(n) turbine.tower.sigma_y = 450.0e6 * np.ones(n) # --- spring reaction data. Use float('inf') for rigid constraints. --- turbine.tower.kidx = [0] # applied at base turbine.tower.kx = [float("inf")] turbine.tower.ky = [float("inf")] turbine.tower.kz = [float("inf")] turbine.tower.ktx = [float("inf")] turbine.tower.kty = [float("inf")] turbine.tower.ktz = [float("inf")] # --- extra mass ---- turbine.tower.midx = [n - 1] # RNA mass at top turbine.tower.m = [285598.8] turbine.tower.mIxx = [1.14930678e08] turbine.tower.mIyy = [2.20354030e07] turbine.tower.mIzz = [1.87597425e07] turbine.tower.mIxy = [0.00000000e00] turbine.tower.mIxz = [5.03710467e05] turbine.tower.mIyz = [0.00000000e00] turbine.tower.mrhox = [-1.13197635] turbine.tower.mrhoy = [0.0] turbine.tower.mrhoz = [0.50875268] turbine.tower.addGravityLoadForExtraMass = True # ----------- # --- wind --- # turbine.tower.wind_zref = 90.0 turbine.tower.wind_z0 = 0.0 turbine.tower.wind1.shearExp = 0.2 turbine.tower.wind2.shearExp = 0.2 # --------------- # if addGravityLoadForExtraMass=True be sure not to double count by adding those force here also # # --- loading case 1: max Thrust --- # turbine.tower.wind_Uref1 = 11.73732 turbine.tower.plidx1 = [n - 1] # at tower top turbine.tower.Fx1 = [1284744.19620519] turbine.tower.Fy1 = [0.0] turbine.tower.Fz1 = [-2914124.84400512] turbine.tower.Mxx1 = [3963732.76208099] turbine.tower.Myy1 = [-2275104.79420872] turbine.tower.Mzz1 = [-346781.68192839] # # --------------- # # --- loading case 2: max wind speed --- # turbine.tower.wind_Uref2 = 70.0 turbine.tower.plidx2 = [n - 1] # at tower top turbine.tower.Fx2 = [930198.60063279] turbine.tower.Fy2 = [0.0] turbine.tower.Fz2 = [-2883106.12368949] turbine.tower.Mxx2 = [-1683669.22411597] turbine.tower.Myy2 = [-2522475.34625363] turbine.tower.Mzz2 = [147301.97023764] # # --------------- # --- safety factors --- turbine.tower.gamma_f = 1.35 turbine.tower.gamma_m = 1.3 turbine.tower.gamma_n = 1.0 turbine.tower.gamma_b = 1.1 # --------------- # --- fatigue --- turbine.tower.z_DEL = np.array( [ 0.000, 1.327, 3.982, 6.636, 9.291, 11.945, 14.600, 17.255, 19.909, 22.564, 25.218, 27.873, 30.527, 33.182, 35.836, 38.491, 41.145, 43.800, 46.455, 49.109, 51.764, 54.418, 57.073, 59.727, 62.382, 65.036, 67.691, 70.345, 73.000, 75.655, 78.309, 80.964, 83.618, 86.273, 87.600, ] ) turbine.tower.M_DEL = 1e3 * np.array( [ 8.2940e003, 8.1518e003, 7.8831e003, 7.6099e003, 7.3359e003, 7.0577e003, 6.7821e003, 6.5119e003, 6.2391e003, 5.9707e003, 5.7070e003, 5.4500e003, 5.2015e003, 4.9588e003, 4.7202e003, 4.4884e003, 4.2577e003, 4.0246e003, 3.7942e003, 3.5664e003, 3.3406e003, 3.1184e003, 2.8977e003, 2.6811e003, 2.4719e003, 2.2663e003, 2.0673e003, 1.8769e003, 1.7017e003, 1.5479e003, 1.4207e003, 1.3304e003, 1.2780e003, 1.2673e003, 1.2761e003, ] ) turbine.tower.gamma_fatigue = 1.35 * 1.3 * 1.0 turbine.tower.life = 20.0 turbine.tower.m_SN = 4 # --------------- # --- constraints --- turbine.tower.min_d_to_t = 120.0 turbine.tower.min_taper = 0.4 # --------------- # ==== Other options if wind_class == "I": turbine.rotor.turbine_class = "I" elif wind_class == "III": turbine.rotor.turbine_class = "III" # for fatigue based analysis of class III wind turbine turbine.tower.M_DEL = ( 1.028713178 * 1e3 * np.array( [ 7.8792e003, 7.7507e003, 7.4918e003, 7.2389e003, 6.9815e003, 6.7262e003, 6.4730e003, 6.2174e003, 5.9615e003, 5.7073e003, 5.4591e003, 5.2141e003, 4.9741e003, 4.7399e003, 4.5117e003, 4.2840e003, 4.0606e003, 3.8360e003, 3.6118e003, 3.3911e003, 3.1723e003, 2.9568e003, 2.7391e003, 2.5294e003, 2.3229e003, 2.1246e003, 1.9321e003, 1.7475e003, 1.5790e003, 1.4286e003, 1.3101e003, 1.2257e003, 1.1787e003, 1.1727e003, 1.1821e003, ] ) ) turbine.rotor.Mxb_damage = 1e3 * np.array( [ 2.3617e003, 2.0751e003, 1.8051e003, 1.5631e003, 1.2994e003, 1.0388e003, 8.1384e002, 6.2492e002, 4.6916e002, 3.4078e002, 2.3916e002, 1.5916e002, 9.9752e001, 5.6139e001, 2.6492e001, 1.0886e001, 3.7210e000, 4.3206e-001, ] ) turbine.rotor.Myb_damage = 1e3 * np.array( [ 2.5492e003, 2.6261e003, 2.4265e003, 2.2308e003, 1.9882e003, 1.7184e003, 1.4438e003, 1.1925e003, 9.6251e002, 7.5564e002, 5.7332e002, 4.1435e002, 2.8036e002, 1.7106e002, 8.7732e001, 3.8678e001, 1.3942e001, 1.6600e000, ] ) elif wind_class == "Offshore": turbine.rotor.turbine_class = "I" # TODO: these should be specified at the turbine level and connected to other system inputs if turbine.sea_depth == 0.0: turbine.tower_d = [6.0, 4.935, 3.87] # (Array, m): diameters along tower else: turbine.tower_d = [6.0, 6.0, 4.935, 3.87] turbine.generator_speed = 1173.7 # (Float, rpm) # generator speed
def EvaluateLCOE(BladeLength, HubHeight, MaximumRotSpeed, Verbose=False): ############################################################################ # Define baseline paremeters used for scaling ReferenceBladeLength = 35 ReferenceTowerHeight = 95 WindReferenceHeight = 50 WindReferenceMeanVelocity = 3 WeibullShapeFactor = 2.0 ShearFactor = 0.25 RatedPower = 1.5e6 # Years used for analysis Years = 25 DiscountRate = 0.08 ############################################################################ ############################################################################ ### 1. Aerodynamic and structural performance using RotorSE rotor = RotorSE() # ------------------- # === blade grid === # (Array): initial aerodynamic grid on unit radius rotor.initial_aero_grid = np.array([0.02222276, 0.06666667, 0.11111057, \ 0.16666667, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, \ 0.63333333, 0.7, 0.76666667, 0.83333333, 0.88888943, 0.93333333, \ 0.97777724]) # (Array): initial structural grid on unit radius rotor.initial_str_grid = np.array([ 0.0, 0.00492790457512, 0.00652942887106, 0.00813095316699, 0.00983257273154, 0.0114340970275, 0.0130356213234, 0.02222276, 0.024446481932, 0.026048006228, 0.06666667, 0.089508406455, 0.11111057, 0.146462614229, 0.16666667, 0.195309105255, 0.23333333, 0.276686558545, 0.3, 0.333640766319, 0.36666667, 0.400404310407, 0.43333333, 0.5, 0.520818918408, 0.56666667, 0.602196371696, 0.63333333, 0.667358391486, 0.683573824984, 0.7, 0.73242031601, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724, 1.0 ]) # (Int): first idx in r_aero_unit of non-cylindrical section, # constant twist inboard of here rotor.idx_cylinder_aero = 3 # (Int): first idx in r_str_unit of non-cylindrical section rotor.idx_cylinder_str = 14 # (Float): hub location as fraction of radius rotor.hubFraction = 0.025 # ------------------ # === blade geometry === # (Array): new aerodynamic grid on unit radius rotor.r_aero = np.array([ 0.02222276, 0.06666667, 0.11111057, 0.2, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.64, 0.7, 0.83333333, 0.88888943, 0.93333333, 0.97777724 ]) # (Float): location of max chord on unit radius rotor.r_max_chord = 0.23577 # (Array, m): chord at control points. defined at hub, then at linearly spaced # locations from r_max_chord to tip ReferenceChord = [3.2612, 4.5709, 3.3178, 1.4621] rotor.chord_sub = [x * np.true_divide(BladeLength,ReferenceBladeLength) \ for x in ReferenceChord] # (Array, deg): twist at control points. defined at linearly spaced locations # from r[idx_cylinder] to tip rotor.theta_sub = [13.2783, 7.46036, 2.89317, -0.0878099] # (Array, m): precurve at control points. defined at same locations at chord, # starting at 2nd control point (root must be zero precurve) rotor.precurve_sub = [0.0, 0.0, 0.0] # (Array, m): adjustment to precurve to account for curvature from loading rotor.delta_precurve_sub = [0.0, 0.0, 0.0] # (Array, m): spar cap thickness parameters rotor.sparT = [0.05, 0.047754, 0.045376, 0.031085, 0.0061398] # (Array, m): trailing-edge thickness parameters rotor.teT = [0.1, 0.09569, 0.06569, 0.02569, 0.00569] # (Float, m): blade length (if not precurved or swept) # otherwise length of blade before curvature rotor.bladeLength = BladeLength # (Float, m): adjustment to blade length to account for curvature from # loading rotor.delta_bladeLength = 0.0 rotor.precone = 2.5 # (Float, deg): precone angle rotor.tilt = 5.0 # (Float, deg): shaft tilt rotor.yaw = 0.0 # (Float, deg): yaw error rotor.nBlades = 3 # (Int): number of blades # ------------------ # === airfoil files === basepath = os.path.join(os.path.dirname(\ os.path.realpath(__file__)), '5MW_AFFiles') # load all airfoils airfoil_types = [0] * 8 airfoil_types[0] = os.path.join(basepath, 'Cylinder1.dat') airfoil_types[1] = os.path.join(basepath, 'Cylinder2.dat') airfoil_types[2] = os.path.join(basepath, 'DU40_A17.dat') airfoil_types[3] = os.path.join(basepath, 'DU35_A17.dat') airfoil_types[4] = os.path.join(basepath, 'DU30_A17.dat') airfoil_types[5] = os.path.join(basepath, 'DU25_A17.dat') airfoil_types[6] = os.path.join(basepath, 'DU21_A17.dat') airfoil_types[7] = os.path.join(basepath, 'NACA64_A17.dat') # place at appropriate radial stations af_idx = [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7] n = len(af_idx) af = [0] * n for i in range(n): af[i] = airfoil_types[af_idx[i]] rotor.airfoil_files = af # (List): names of airfoil file # ---------------------- # === atmosphere === rotor.rho = 1.225 # (Float, kg/m**3): density of air rotor.mu = 1.81206e-5 # (Float, kg/m/s): dynamic viscosity of air rotor.shearExp = 0.25 # (Float): shear exponent rotor.hubHt = HubHeight # (Float, m): hub height rotor.turbine_class = 'I' # (Enum): IEC turbine class rotor.turbulence_class = 'B' # (Enum): IEC turbulence class class rotor.cdf_reference_height_wind_speed = 30.0 rotor.g = 9.81 # (Float, m/s**2): acceleration of gravity # ---------------------- # === control === rotor.control.Vin = 3.0 # (Float, m/s): cut-in wind speed rotor.control.Vout = 26.0 # (Float, m/s): cut-out wind speed rotor.control.ratedPower = RatedPower # (Float, W): rated power # (Float, rpm): minimum allowed rotor rotation speed # (Float, rpm): maximum allowed rotor rotation speed rotor.control.minOmega = 0.0 rotor.control.maxOmega = MaximumRotSpeed # (Float): tip-speed ratio in Region 2 (should be optimized externally) rotor.control.tsr = 7 # (Float, deg): pitch angle in region 2 (and region 3 for fixed pitch machines) rotor.control.pitch = 0.0 # (Float, deg): worst-case pitch at survival wind condition rotor.pitch_extreme = 0.0 # (Float, deg): worst-case azimuth at survival wind condition rotor.azimuth_extreme = 0.0 # (Float): fraction of rated speed at which the deflection is assumed to # representative throughout the power curve calculation rotor.VfactorPC = 0.7 # ---------------------- # === aero and structural analysis options === # (Int): number of sectors to divide rotor face into in computing thrust and power rotor.nSector = 4 # (Int): number of points to evaluate aero analysis at rotor.npts_coarse_power_curve = 20 # (Int): number of points to use in fitting spline to power curve rotor.npts_spline_power_curve = 200 # (Float): availability and other losses (soiling, array, etc.) rotor.AEP_loss_factor = 1.0 rotor.drivetrainType = 'geared' # (Enum) # (Int): number of natural frequencies to compute rotor.nF = 5 # (Float): a dynamic amplification factor to adjust the static deflection # calculation rotor.dynamic_amplication_tip_deflection = 1.35 # ---------------------- # === materials and composite layup === basepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), \ '5MW_PrecompFiles') materials = Orthotropic2DMaterial.listFromPreCompFile(os.path.join(basepath,\ 'materials.inp')) ncomp = len(rotor.initial_str_grid) upper = [0] * ncomp lower = [0] * ncomp webs = [0] * ncomp profile = [0] * ncomp # (Array): array of leading-edge positions from a reference blade axis # (usually blade pitch axis). locations are normalized by the local chord # length. e.g. leLoc[i] = 0.2 means leading edge is 0.2*chord[i] from reference # axis. positive in -x direction for airfoil-aligned coordinate system rotor.leLoc = np.array([ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.498, 0.497, 0.465, 0.447, 0.43, 0.411, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4 ]) # (Array): index of sector for spar (PreComp definition of sector) rotor.sector_idx_strain_spar = [2] * ncomp # (Array): index of sector for trailing-edge (PreComp definition of sector) rotor.sector_idx_strain_te = [3] * ncomp web1 = np.array([ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.4114, 0.4102, 0.4094, 0.3876, 0.3755, 0.3639, 0.345, 0.3342, 0.3313, 0.3274, 0.323, 0.3206, 0.3172, 0.3138, 0.3104, 0.307, 0.3003, 0.2982, 0.2935, 0.2899, 0.2867, 0.2833, 0.2817, 0.2799, 0.2767, 0.2731, 0.2664, 0.2607, 0.2562, 0.1886, -1.0 ]) web2 = np.array([ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.5886, 0.5868, 0.5854, 0.5508, 0.5315, 0.5131, 0.4831, 0.4658, 0.4687, 0.4726, 0.477, 0.4794, 0.4828, 0.4862, 0.4896, 0.493, 0.4997, 0.5018, 0.5065, 0.5101, 0.5133, 0.5167, 0.5183, 0.5201, 0.5233, 0.5269, 0.5336, 0.5393, 0.5438, 0.6114, -1.0 ]) web3 = np.array([ -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0 ]) # (Array, m): chord distribution for reference section, thickness of structural # layup scaled with reference thickness (fixed t/c for this case) rotor.chord_str_ref = np.array([3.2612, 3.3100915356, 3.32587052924, 3.34159388653, 3.35823798667, 3.37384375335, 3.38939112914, 3.4774055542, 3.49839685, 3.51343645709, 3.87017220335, 4.04645623801, 4.19408216643, 4.47641008477, 4.55844487985, 4.57383098262, 4.57285771934, 4.51914315648, 4.47677655262, 4.40075650022, 4.31069949379, 4.20483735936, 4.08985563932, 3.82931757126, 3.74220276467, 3.54415796922, 3.38732428502, 3.24931446473, 3.23421422609, 3.22701537997, 3.21972125648, 3.08979310611, 2.95152261813, 2.330753331, 2.05553464181, 1.82577817774, 1.5860853279, 1.4621])* \ np.true_divide(BladeLength,ReferenceBladeLength) for i in range(ncomp): webLoc = [] if web1[i] != -1: webLoc.append(web1[i]) if web2[i] != -1: webLoc.append(web2[i]) if web3[i] != -1: webLoc.append(web3[i]) upper[i], lower[i], webs[i] = CompositeSection.initFromPreCompLayupFile\ (os.path.join(basepath, 'layup_' + str(i+1) + '.inp'), webLoc, materials) profile[i] = Profile.initFromPreCompFile(os.path.join(basepath, 'shape_' \ + str(i+1) + '.inp')) # (List): list of all Orthotropic2DMaterial objects used in # defining the geometry rotor.materials = materials # (List): list of CompositeSection objections defining the properties for # upper surface rotor.upperCS = upper # (List): list of CompositeSection objections defining the properties for # lower surface rotor.lowerCS = lower # (List): list of CompositeSection objections defining the properties for # shear webs rotor.websCS = webs # (List): airfoil shape at each radial position rotor.profile = profile # -------------------------------------- # === fatigue === # (Array): nondimensional radial locations of damage equivalent moments rotor.rstar_damage = np.array([ 0.000, 0.022, 0.067, 0.111, 0.167, 0.233, 0.300, 0.367, 0.433, 0.500, 0.567, 0.633, 0.700, 0.767, 0.833, 0.889, 0.933, 0.978 ]) # (Array, N*m): damage equivalent moments about blade c.s. x-direction rotor.Mxb_damage = 1e3 * np.array([ 2.3743E+003, 2.0834E+003, 1.8108E+003, 1.5705E+003, 1.3104E+003, 1.0488E+003, 8.2367E+002, 6.3407E+002, 4.7727E+002, 3.4804E+002, 2.4458E+002, 1.6339E+002, 1.0252E+002, 5.7842E+001, 2.7349E+001, 1.1262E+001, 3.8549E+000, 4.4738E-001 ]) # (Array, N*m): damage equivalent moments about blade c.s. y-direction rotor.Myb_damage = 1e3 * np.array([ 2.7732E+003, 2.8155E+003, 2.6004E+003, 2.3933E+003, 2.1371E+003, 1.8459E+003, 1.5582E+003, 1.2896E+003, 1.0427E+003, 8.2015E+002, 6.2449E+002, 4.5229E+002, 3.0658E+002, 1.8746E+002, 9.6475E+001, 4.2677E+001, 1.5409E+001, 1.8426E+000 ]) rotor.strain_ult_spar = 1.0e-2 # (Float): ultimate strain in spar cap # (Float): uptimate strain in trailing-edge panels, note that I am putting a # factor of two for the damage part only. rotor.strain_ult_te = 2500 * 1e-6 * 2 rotor.eta_damage = 1.35 * 1.3 * 1.0 # (Float): safety factor for fatigue rotor.m_damage = 10.0 # (Float): slope of S-N curve for fatigue analysis # (Float): number of cycles used in fatigue analysis rotor.N_damage = 365 * 24 * 3600 * 20.0 # ---------------- # from myutilities import plt # === run and outputs === rotor.run() # Evaluate AEP Using Lewis' Functions # Weibull Wind Parameters WindReferenceHeight = 50 WindReferenceMeanVelocity = 7.5 WeibullShapeFactor = 2.0 ShearFactor = 0.25 PowerCurve = rotor.P / 1e6 PowerCurveVelocity = rotor.V HubHeight = rotor.hubHt AEP,WeibullScale = CalculateAEPWeibull(PowerCurve,PowerCurveVelocity, HubHeight, \ BladeLength,WeibullShapeFactor, WindReferenceHeight, \ WindReferenceMeanVelocity, ShearFactor) NamePlateCapacity = EstimateCapacity(PowerCurve,PowerCurveVelocity, \ rotor.ratedConditions.V) # AEP At Constant 7.5m/s Wind used for benchmarking... #AEP = CalculateAEPConstantWind(PowerCurve, PowerCurveVelocity, 7.5) if (Verbose == True): print '################### ROTORSE ######################' print 'AEP = %d MWH' % (AEP) print 'NamePlateCapacity = %fMW' % (NamePlateCapacity) print 'diameter =', rotor.diameter print 'ratedConditions.V =', rotor.ratedConditions.V print 'ratedConditions.Omega =', rotor.ratedConditions.Omega print 'ratedConditions.pitch =', rotor.ratedConditions.pitch print 'mass_one_blade =', rotor.mass_one_blade print 'mass_all_blades =', rotor.mass_all_blades print 'I_all_blades =', rotor.I_all_blades print 'freq =', rotor.freq print 'tip_deflection =', rotor.tip_deflection print 'root_bending_moment =', rotor.root_bending_moment print '#########################################################' ############################################################################# ### 2. Hub Sizing # Specify hub parameters based off rotor # Load default hub model hubS = HubSE() hubS.rotor_diameter = rotor.Rtip * 2 # m hubS.blade_number = rotor.nBlades hubS.blade_root_diameter = rotor.chord_sub[0] * 1.25 hubS.L_rb = rotor.hubFraction * rotor.diameter hubS.MB1_location = np.array([-0.5, 0.0, 0.0]) hubS.machine_rating = rotor.control.ratedPower hubS.blade_mass = rotor.mass_one_blade hubS.rotor_bending_moment = rotor.root_bending_moment hubS.run() RotorTotalWeight = rotor.mass_all_blades + hubS.spinner.mass + \ hubS.hub.mass + hubS.pitchSystem.mass if (Verbose == True): print '##################### Hub SE ############################' print "Estimate of Hub Component Sizes:" print "Hub Components" print ' Hub: {0:8.1f} kg'.format(hubS.hub.mass) print ' Pitch system: {0:8.1f} kg'.format(hubS.pitchSystem.mass) print ' Nose cone: {0:8.1f} kg'.format(hubS.spinner.mass) print 'Rotor Total Weight = %d kg' % RotorTotalWeight print '#########################################################' ############################################################################ ### 3. Drive train + Nacelle Mass estimation nace = Drive4pt() nace.rotor_diameter = rotor.Rtip * 2 # m nace.rotor_speed = rotor.ratedConditions.Omega # #rpm m/s nace.machine_rating = hubS.machine_rating / 1000 nace.DrivetrainEfficiency = 0.95 # 6.35e6 #4365248.74 # Nm nace.rotor_torque = rotor.ratedConditions.Q nace.rotor_thrust = rotor.ratedConditions.T # N nace.rotor_mass = 0.0 #accounted for in F_z # kg nace.rotor_bending_moment_x = rotor.Mxyz_0[0] nace.rotor_bending_moment_y = rotor.Mxyz_0[1] nace.rotor_bending_moment_z = rotor.Mxyz_0[2] nace.rotor_force_x = rotor.Fxyz_0[0] # N nace.rotor_force_y = rotor.Fxyz_0[1] nace.rotor_force_z = rotor.Fxyz_0[2] # N # geared 3-stage Gearbox with induction generator machine nace.drivetrain_design = 'geared' nace.gear_ratio = 96.76 # 97:1 as listed in the 5 MW reference document nace.gear_configuration = 'eep' # epicyclic-epicyclic-parallel nace.crane = True # onboard crane present nace.shaft_angle = 5.0 #deg nace.shaft_ratio = 0.10 nace.Np = [3, 3, 1] nace.ratio_type = 'optimal' nace.shaft_type = 'normal' nace.uptower_transformer = False nace.shrink_disc_mass = 333.3 * nace.machine_rating / 1000.0 # estimated nace.mb1Type = 'CARB' nace.mb2Type = 'SRB' nace.flange_length = 0.5 #m nace.overhang = 5.0 nace.gearbox_cm = 0.1 nace.hss_length = 1.5 #0 if no fatigue check, 1 if parameterized fatigue check, #2 if known loads inputs nace.check_fatigue = 0 nace.blade_number = rotor.nBlades nace.cut_in = rotor.control.Vin #cut-in m/s nace.cut_out = rotor.control.Vout #cut-out m/s nace.Vrated = rotor.ratedConditions.V #rated windspeed m/s nace.weibull_k = WeibullShapeFactor # windepeed distribution shape parameter # windspeed distribution scale parameter nace.weibull_A = WeibullScale nace.T_life = 20. #design life in years nace.IEC_Class_Letter = 'B' # length from hub center to main bearing, leave zero if unknown nace.L_rb = hubS.L_rb # NREL 5 MW Tower Variables nace.tower_top_diameter = 3.78 # m nace.run() if (Verbose == True): print '##################### Drive SE ############################' print "Estimate of Nacelle Component Sizes" print 'Low speed shaft: {0:8.1f} kg'.format(nace.lowSpeedShaft.mass) print 'Main bearings: {0:8.1f} kg'.format(\ nace.mainBearing.mass + nace.secondBearing.mass) print 'Gearbox: {0:8.1f} kg'.format(nace.gearbox.mass) print 'High speed shaft & brakes: {0:8.1f} kg'.format\ (nace.highSpeedSide.mass) print 'Generator: {0:8.1f} kg'.format(nace.generator.mass) print 'Variable speed electronics: {0:8.1f} kg'.format(\ nace.above_yaw_massAdder.vs_electronics_mass) print 'Overall mainframe:{0:8.1f} kg'.format(\ nace.above_yaw_massAdder.mainframe_mass) print ' Bedplate: {0:8.1f} kg'.format(nace.bedplate.mass) print 'Electrical connections: {0:8.1f} kg'.format(\ nace.above_yaw_massAdder.electrical_mass) print 'HVAC system: {0:8.1f} kg'.format(\ nace.above_yaw_massAdder.hvac_mass ) print 'Nacelle cover: {0:8.1f} kg'.format(\ nace.above_yaw_massAdder.cover_mass) print 'Yaw system: {0:8.1f} kg'.format(nace.yawSystem.mass) print 'Overall nacelle: {0:8.1f} kg'.format(nace.nacelle_mass, \ nace.nacelle_cm[0], nace.nacelle_cm[1], nace.nacelle_cm[2], \ nace.nacelle_I[0], nace.nacelle_I[1], nace.nacelle_I[2]) print '#########################################################' ############################################################################ ### 4. Tower Mass # --- tower setup ------ from commonse.environment import PowerWind tower = set_as_top(TowerSE()) # ---- tower ------ tower.replace('wind1', PowerWind()) tower.replace('wind2', PowerWind()) # onshore (no waves) # --- geometry ---- tower.z_param = [0.0, HubHeight * 0.5, HubHeight] TowerRatio = np.true_divide(HubHeight, ReferenceTowerHeight) tower.d_param = [6.0 * TowerRatio, 4.935 * TowerRatio, 3.87 * TowerRatio] tower.t_param = [0.027*1.3*TowerRatio, 0.023*1.3*TowerRatio, \ 0.019*1.3*TowerRatio] n = 10 tower.z_full = np.linspace(0.0, HubHeight, n) tower.L_reinforced = 15.0 * np.ones(n) # [m] buckling length tower.theta_stress = 0.0 * np.ones(n) tower.yaw = 0.0 # --- material props --- tower.E = 210e9 * np.ones(n) tower.G = 80.8e9 * np.ones(n) tower.rho = 8500.0 * np.ones(n) tower.sigma_y = 450.0e6 * np.ones(n) # --- spring reaction data. Use float('inf') for rigid constraints. --- tower.kidx = [0] # applied at base tower.kx = [float('inf')] tower.ky = [float('inf')] tower.kz = [float('inf')] tower.ktx = [float('inf')] tower.kty = [float('inf')] tower.ktz = [float('inf')] # --- extra mass ---- tower.midx = [n - 1] # RNA mass at top tower.m = [0.8] tower.mIxx = [1.14930678e+08] tower.mIyy = [2.20354030e+07] tower.mIzz = [1.87597425e+07] tower.mIxy = [0.00000000e+00] tower.mIxz = [5.03710467e+05] tower.mIyz = [0.00000000e+00] tower.mrhox = [-1.13197635] tower.mrhoy = [0.] tower.mrhoz = [0.50875268] tower.addGravityLoadForExtraMass = False # ----------- # --- wind --- tower.wind_zref = 90.0 tower.wind_z0 = 0.0 tower.wind1.shearExp = 0.14 tower.wind2.shearExp = 0.14 # --------------- # # --- loading case 1: max Thrust --- tower.wind_Uref1 = 11.73732 tower.plidx1 = [n - 1] # at tower top tower.Fx1 = [0.19620519] tower.Fy1 = [0.] tower.Fz1 = [-2914124.84400512] tower.Mxx1 = [3963732.76208099] tower.Myy1 = [-2275104.79420872] tower.Mzz1 = [-346781.68192839] # # --------------- # # --- loading case 2: max wind speed --- tower.wind_Uref2 = 70.0 tower.plidx1 = [n - 1] # at tower top tower.Fx1 = [930198.60063279] tower.Fy1 = [0.] tower.Fz1 = [-2883106.12368949] tower.Mxx1 = [-1683669.22411597] tower.Myy1 = [-2522475.34625363] tower.Mzz1 = [147301.97023764] # # --------------- # # --- run --- tower.run() if (Verbose == True): print '##################### Tower SE ##########################' print 'mass (kg) =', tower.mass print 'f1 (Hz) =', tower.f1 print 'f2 (Hz) =', tower.f2 print 'top_deflection1 (m) =', tower.top_deflection1 print 'top_deflection2 (m) =', tower.top_deflection2 print '#########################################################' ############################################################################ ## 5. Turbine captial costs analysis turbine = Turbine_CostsSE() # NREL 5 MW turbine component masses based on Sunderland model approach # Rotor # inline with the windpact estimates turbine.blade_mass = rotor.mass_one_blade turbine.hub_mass = hubS.hub.mass turbine.pitch_system_mass = hubS.pitchSystem.mass turbine.spinner_mass = hubS.spinner.mass # Drivetrain and Nacelle turbine.low_speed_shaft_mass = nace.lowSpeedShaft.mass turbine.main_bearing_mass = nace.mainBearing.mass turbine.second_bearing_mass = nace.secondBearing.mass turbine.gearbox_mass = nace.gearbox.mass turbine.high_speed_side_mass = nace.highSpeedSide.mass turbine.generator_mass = nace.generator.mass turbine.bedplate_mass = nace.bedplate.mass turbine.yaw_system_mass = nace.yawSystem.mass # Tower turbine.tower_mass = tower.mass * 0.5 # Additional non-mass cost model input variables turbine.machine_rating = hubS.machine_rating / 1000 turbine.advanced = False turbine.blade_number = rotor.nBlades turbine.drivetrain_design = 'geared' turbine.crane = False turbine.offshore = False # Target year for analysis results turbine.year = 2010 turbine.month = 12 turbine.run() if (Verbose == True): print '##################### TurbinePrice SE ####################' print "Overall rotor cost with 3 advanced blades is ${0:.2f} USD"\ .format(turbine.rotorCC.cost) print "Blade cost is ${0:.2f} USD".format(turbine.rotorCC.bladeCC.cost) print "Hub cost is ${0:.2f} USD".format(turbine.rotorCC.hubCC.cost) print "Pitch system cost is ${0:.2f} USD".format( turbine.rotorCC.pitchSysCC.cost) print "Spinner cost is ${0:.2f} USD".format( turbine.rotorCC.spinnerCC.cost) print print "Overall nacelle cost is ${0:.2f} USD".format( turbine.nacelleCC.cost) print "LSS cost is ${0:.2f} USD".format(turbine.nacelleCC.lssCC.cost) print "Main bearings cost is ${0:.2f} USD".format( turbine.nacelleCC.bearingsCC.cost) print "Gearbox cost is ${0:.2f} USD".format( turbine.nacelleCC.gearboxCC.cost) print "Hight speed side cost is ${0:.2f} USD".format( turbine.nacelleCC.hssCC.cost) print "Generator cost is ${0:.2f} USD".format( turbine.nacelleCC.generatorCC.cost) print "Bedplate cost is ${0:.2f} USD".format( turbine.nacelleCC.bedplateCC.cost) print "Yaw system cost is ${0:.2f} USD".format( turbine.nacelleCC.yawSysCC.cost) print print "Tower cost is ${0:.2f} USD".format(turbine.towerCC.cost) print print "The overall turbine cost is ${0:.2f} USD".format( turbine.turbine_cost) print '#########################################################' ############################################################################ ## 6. Operating Expenses # A simple test of nrel_csm_bos model bos = bos_csm_assembly() # Set input parameters bos = bos_csm_assembly() bos.machine_rating = hubS.machine_rating / 1000 bos.rotor_diameter = rotor.diameter bos.turbine_cost = turbine.turbine_cost bos.hub_height = HubHeight bos.turbine_number = 1 bos.sea_depth = 0 bos.year = 2009 bos.month = 12 bos.multiplier = 1.0 bos.run() om = opex_csm_assembly() om.machine_rating = rotor.control.ratedPower / 1000 # Need to manipulate input or underlying component will not execute om.net_aep = AEP * 10e4 om.sea_depth = 0 om.year = 2009 om.month = 12 om.turbine_number = 100 om.run() if (Verbose == True): print '##################### Operating Costs ####################' print "BOS cost per turbine: ${0:.2f} USD".format(bos.bos_costs / \ bos.turbine_number) print "Average annual operational expenditures" print "OPEX on shore with 100 turbines ${:.2f}: USD".format(\ om.avg_annual_opex) print "Preventative OPEX by turbine: ${:.2f} USD".format(\ om.opex_breakdown.preventative_opex / om.turbine_number) print "Corrective OPEX by turbine: ${:.2f} USD".format(\ om.opex_breakdown.corrective_opex / om.turbine_number) print "Land Lease OPEX by turbine: ${:.2f} USD".format(\ om.opex_breakdown.lease_opex / om.turbine_number) print '#########################################################' CapitalCost = turbine.turbine_cost + bos.bos_costs / bos.turbine_number OperatingCost = om.opex_breakdown.preventative_opex / om.turbine_number + \ om.opex_breakdown.lease_opex / om.turbine_number + \ om.opex_breakdown.corrective_opex / om.turbine_number LCOE = ComputeLCOE(AEP, CapitalCost, OperatingCost, DiscountRate, Years) print '######################***********************###################' print "Levelized Cost of Energy over %d years \ is $%f/kWH" % (Years, LCOE / 1000) print '######################***********************###################' return LCOE / 1000
def configure_nrel5mw_turbine_with_jacket(turbine,wind_class='I',sea_depth = 0.0): """ Inputs: rotor = RotorSE() nacelle = DriveSE() jacket = JacketSE() wind_class : str ('I', 'III', 'Offshore' - selected wind class for project) sea_depth : float (sea depth if an offshore wind plant) """ # ================= # === Turbine === turbine.rho = 1.225 # (Float, kg/m**3): density of air turbine.mu = 1.81206e-5 # (Float, kg/m/s): dynamic viscosity of air turbine.shear_exponent = 0.2 # (Float): shear exponent turbine.hub_height = 90.0 # (Float, m): hub height turbine.turbine_class = 'I' # (Enum): IEC turbine class turbine.turbulence_class = 'B' # (Enum): IEC turbulence class class turbine.cdf_reference_height_wind_speed = 90.0 # (Float): reference hub height for IEC wind speed (used in CDF calculation) turbine.g = 9.81 # (Float, m/s**2): acceleration of gravity # ====================== # === rotor === # --- blade grid --- turbine.rotor.initial_aero_grid = np.array([0.02222276, 0.06666667, 0.11111057, 0.16666667, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.7, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724]) # (Array): initial aerodynamic grid on unit radius turbine.rotor.initial_str_grid = np.array([0.0, 0.00492790457512, 0.00652942887106, 0.00813095316699, 0.00983257273154, 0.0114340970275, 0.0130356213234, 0.02222276, 0.024446481932, 0.026048006228, 0.06666667, 0.089508406455, 0.11111057, 0.146462614229, 0.16666667, 0.195309105255, 0.23333333, 0.276686558545, 0.3, 0.333640766319, 0.36666667, 0.400404310407, 0.43333333, 0.5, 0.520818918408, 0.56666667, 0.602196371696, 0.63333333, 0.667358391486, 0.683573824984, 0.7, 0.73242031601, 0.76666667, 0.83333333, 0.88888943, 0.93333333, 0.97777724, 1.0]) # (Array): initial structural grid on unit radius turbine.rotor.idx_cylinder_aero = 3 # (Int): first idx in r_aero_unit of non-cylindrical section, constant twist inboard of here turbine.rotor.idx_cylinder_str = 14 # (Int): first idx in r_str_unit of non-cylindrical section turbine.rotor.hubFraction = 0.025 # (Float): hub location as fraction of radius # ------------------ # --- blade geometry --- turbine.rotor.r_aero = np.array([0.02222276, 0.06666667, 0.11111057, 0.2, 0.23333333, 0.3, 0.36666667, 0.43333333, 0.5, 0.56666667, 0.63333333, 0.64, 0.7, 0.83333333, 0.88888943, 0.93333333, 0.97777724]) # (Array): new aerodynamic grid on unit radius turbine.rotor.r_max_chord = 0.23577 # (Float): location of max chord on unit radius turbine.rotor.chord_sub = [3.2612, 4.5709, 3.3178, 1.4621] # (Array, m): chord at control points. defined at hub, then at linearly spaced locations from r_max_chord to tip turbine.rotor.theta_sub = [13.2783, 7.46036, 2.89317, -0.0878099] # (Array, deg): twist at control points. defined at linearly spaced locations from r[idx_cylinder] to tip turbine.rotor.precurve_sub = [0.0, 0.0, 0.0] # (Array, m): precurve at control points. defined at same locations at chord, starting at 2nd control point (root must be zero precurve) turbine.rotor.delta_precurve_sub = [0.0, 0.0, 0.0] # (Array, m): adjustment to precurve to account for curvature from loading turbine.rotor.sparT = [0.05, 0.047754, 0.045376, 0.031085, 0.0061398] # (Array, m): spar cap thickness parameters turbine.rotor.teT = [0.1, 0.09569, 0.06569, 0.02569, 0.00569] # (Array, m): trailing-edge thickness parameters turbine.rotor.bladeLength = 61.5 # (Float, m): blade length (if not precurved or swept) otherwise length of blade before curvature turbine.rotor.delta_bladeLength = 0.0 # (Float, m): adjustment to blade length to account for curvature from loading turbine.rotor.precone = 2.5 # (Float, deg): precone angle turbine.rotor.tilt = 5.0 # (Float, deg): shaft tilt turbine.rotor.yaw = 0.0 # (Float, deg): yaw error turbine.rotor.nBlades = 3 # (Int): number of blades # ------------------ # --- airfoil files --- import rotorse #basepath = os.path.join('5MW_files', '5MW_AFFiles') basepath = os.path.join('..','reference_turbines','nrel5mw','airfoils') # load all airfoils airfoil_types = [0]*8 airfoil_types[0] = os.path.join(basepath, 'Cylinder1.dat') airfoil_types[1] = os.path.join(basepath, 'Cylinder2.dat') airfoil_types[2] = os.path.join(basepath, 'DU40_A17.dat') airfoil_types[3] = os.path.join(basepath, 'DU35_A17.dat') airfoil_types[4] = os.path.join(basepath, 'DU30_A17.dat') airfoil_types[5] = os.path.join(basepath, 'DU25_A17.dat') airfoil_types[6] = os.path.join(basepath, 'DU21_A17.dat') airfoil_types[7] = os.path.join(basepath, 'NACA64_A17.dat') # place at appropriate radial stations af_idx = [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7] n = len(af_idx) af = [0]*n for i in range(n): af[i] = airfoil_types[af_idx[i]] turbine.rotor.airfoil_files = af # (List): names of airfoil file # ---------------------- # --- control --- turbine.rotor.control.Vin = 3.0 # (Float, m/s): cut-in wind speed turbine.rotor.control.Vout = 25.0 # (Float, m/s): cut-out wind speed turbine.rotor.control.ratedPower = 5e6 # (Float, W): rated power turbine.rotor.control.minOmega = 0.0 # (Float, rpm): minimum allowed rotor rotation speed turbine.rotor.control.maxOmega = 12.0 # (Float, rpm): maximum allowed rotor rotation speed turbine.rotor.control.tsr = 7.55 # (Float): tip-speed ratio in Region 2 (should be optimized externally) turbine.rotor.control.pitch = 0.0 # (Float, deg): pitch angle in region 2 (and region 3 for fixed pitch machines) turbine.rotor.pitch_extreme = 0.0 # (Float, deg): worst-case pitch at survival wind condition turbine.rotor.azimuth_extreme = 0.0 # (Float, deg): worst-case azimuth at survival wind condition turbine.rotor.VfactorPC = 0.7 # (Float): fraction of rated speed at which the deflection is assumed to representative throughout the power curve calculation # ---------------------- # --- aero and structural analysis options --- turbine.rotor.nSector = 4 # (Int): number of sectors to divide rotor face into in computing thrust and power turbine.rotor.npts_coarse_power_curve = 20 # (Int): number of points to evaluate aero analysis at turbine.rotor.npts_spline_power_curve = 200 # (Int): number of points to use in fitting spline to power curve turbine.rotor.AEP_loss_factor = 1.0 # (Float): availability and other losses (soiling, array, etc.) turbine.rotor.drivetrainType = 'geared' # (Enum) turbine.rotor.nF = 5 # (Int): number of natural frequencies to compute turbine.rotor.dynamic_amplication_tip_deflection = 1.35 # (Float): a dynamic amplification factor to adjust the static deflection calculation # ---------------------- # --- materials and composite layup --- #basepath = os.path.join('5MW_files', '5MW_PrecompFiles') basepath = os.path.join('..', 'reference_turbines','nrel5mw','blade') materials = Orthotropic2DMaterial.listFromPreCompFile(os.path.join(basepath, 'materials.inp')) ncomp = len(turbine.rotor.initial_str_grid) upper = [0]*ncomp lower = [0]*ncomp webs = [0]*ncomp profile = [0]*ncomp turbine.rotor.leLoc = np.array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.498, 0.497, 0.465, 0.447, 0.43, 0.411, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4]) # (Array): array of leading-edge positions from a reference blade axis (usually blade pitch axis). locations are normalized by the local chord length. e.g. leLoc[i] = 0.2 means leading edge is 0.2*chord[i] from reference axis. positive in -x direction for airfoil-aligned coordinate system turbine.rotor.sector_idx_strain_spar = [2]*ncomp # (Array): index of sector for spar (PreComp definition of sector) turbine.rotor.sector_idx_strain_te = [3]*ncomp # (Array): index of sector for trailing-edge (PreComp definition of sector) web1 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.4114, 0.4102, 0.4094, 0.3876, 0.3755, 0.3639, 0.345, 0.3342, 0.3313, 0.3274, 0.323, 0.3206, 0.3172, 0.3138, 0.3104, 0.307, 0.3003, 0.2982, 0.2935, 0.2899, 0.2867, 0.2833, 0.2817, 0.2799, 0.2767, 0.2731, 0.2664, 0.2607, 0.2562, 0.1886, -1.0]) web2 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 0.5886, 0.5868, 0.5854, 0.5508, 0.5315, 0.5131, 0.4831, 0.4658, 0.4687, 0.4726, 0.477, 0.4794, 0.4828, 0.4862, 0.4896, 0.493, 0.4997, 0.5018, 0.5065, 0.5101, 0.5133, 0.5167, 0.5183, 0.5201, 0.5233, 0.5269, 0.5336, 0.5393, 0.5438, 0.6114, -1.0]) web3 = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0]) turbine.rotor.chord_str_ref = np.array([3.2612, 3.3100915356, 3.32587052924, 3.34159388653, 3.35823798667, 3.37384375335, 3.38939112914, 3.4774055542, 3.49839685, 3.51343645709, 3.87017220335, 4.04645623801, 4.19408216643, 4.47641008477, 4.55844487985, 4.57383098262, 4.57285771934, 4.51914315648, 4.47677655262, 4.40075650022, 4.31069949379, 4.20483735936, 4.08985563932, 3.82931757126, 3.74220276467, 3.54415796922, 3.38732428502, 3.24931446473, 3.23421422609, 3.22701537997, 3.21972125648, 3.08979310611, 2.95152261813, 2.330753331, 2.05553464181, 1.82577817774, 1.5860853279, 1.4621]) # (Array, m): chord distribution for reference section, thickness of structural layup scaled with reference thickness (fixed t/c for this case) for i in range(ncomp): webLoc = [] if web1[i] != -1: webLoc.append(web1[i]) if web2[i] != -1: webLoc.append(web2[i]) if web3[i] != -1: webLoc.append(web3[i]) upper[i], lower[i], webs[i] = CompositeSection.initFromPreCompLayupFile(os.path.join(basepath, 'layup_' + str(i+1) + '.inp'), webLoc, materials) profile[i] = Profile.initFromPreCompFile(os.path.join(basepath, 'shape_' + str(i+1) + '.inp')) turbine.rotor.materials = materials # (List): list of all Orthotropic2DMaterial objects used in defining the geometry turbine.rotor.upperCS = upper # (List): list of CompositeSection objections defining the properties for upper surface turbine.rotor.lowerCS = lower # (List): list of CompositeSection objections defining the properties for lower surface turbine.rotor.websCS = webs # (List): list of CompositeSection objections defining the properties for shear webs turbine.rotor.profile = profile # (List): airfoil shape at each radial position # -------------------------------------- strain_ult_spar = 1.0e-2 strain_ult_te = 2500*1e-6 # --- fatigue --- turbine.rotor.rstar_damage = np.array([0.000, 0.022, 0.067, 0.111, 0.167, 0.233, 0.300, 0.367, 0.433, 0.500, 0.567, 0.633, 0.700, 0.767, 0.833, 0.889, 0.933, 0.978]) # (Array): nondimensional radial locations of damage equivalent moments turbine.rotor.Mxb_damage = 1e3*np.array([2.3743E+003, 2.0834E+003, 1.8108E+003, 1.5705E+003, 1.3104E+003, 1.0488E+003, 8.2367E+002, 6.3407E+002, 4.7727E+002, 3.4804E+002, 2.4458E+002, 1.6339E+002, 1.0252E+002, 5.7842E+001, 2.7349E+001, 1.1262E+001, 3.8549E+000, 4.4738E-001]) # (Array, N*m): damage equivalent moments about blade c.s. x-direction turbine.rotor.Myb_damage = 1e3*np.array([2.7732E+003, 2.8155E+003, 2.6004E+003, 2.3933E+003, 2.1371E+003, 1.8459E+003, 1.5582E+003, 1.2896E+003, 1.0427E+003, 8.2015E+002, 6.2449E+002, 4.5229E+002, 3.0658E+002, 1.8746E+002, 9.6475E+001, 4.2677E+001, 1.5409E+001, 1.8426E+000]) # (Array, N*m): damage equivalent moments about blade c.s. y-direction turbine.rotor.strain_ult_spar = 1.0e-2 # (Float): ultimate strain in spar cap turbine.rotor.strain_ult_te = 2500*1e-6 * 2 # (Float): uptimate strain in trailing-edge panels, note that I am putting a factor of two for the damage part only. turbine.rotor.eta_damage = 1.35*1.3*1.0 # (Float): safety factor for fatigue turbine.rotor.m_damage = 10.0 # (Float): slope of S-N curve for fatigue analysis turbine.rotor.N_damage = 365*24*3600*20.0 # (Float): number of cycles used in fatigue analysis TODO: make function of rotation speed # ---------------- # ================= # === nacelle ====== turbine.nacelle.L_ms = 1.0 # (Float, m): main shaft length downwind of main bearing in low-speed shaft turbine.nacelle.L_mb = 2.5 # (Float, m): main shaft length in low-speed shaft turbine.nacelle.h0_front = 1.7 # (Float, m): height of Ibeam in bedplate front turbine.nacelle.h0_rear = 1.35 # (Float, m): height of Ibeam in bedplate rear turbine.nacelle.drivetrain_design = 'geared' turbine.nacelle.crane = True # (Bool): flag for presence of crane turbine.nacelle.bevel = 0 # (Int): Flag for the presence of a bevel stage - 1 if present, 0 if not turbine.nacelle.gear_configuration = 'eep' # (Str): tring that represents the configuration of the gearbox (stage number and types) turbine.nacelle.Np = [3, 3, 1] # (Array): number of planets in each stage turbine.nacelle.ratio_type = 'optimal' # (Str): optimal or empirical stage ratios turbine.nacelle.shaft_type = 'normal' # (Str): normal or short shaft length #turbine.nacelle.shaft_angle = 5.0 # (Float, deg): Angle of the LSS inclindation with respect to the horizontal turbine.nacelle.shaft_ratio = 0.10 # (Float): Ratio of inner diameter to outer diameter. Leave zero for solid LSS turbine.nacelle.carrier_mass = 8000.0 # estimated for 5 MW turbine.nacelle.mb1Type = 'CARB' # (Str): Main bearing type: CARB, TRB or SRB turbine.nacelle.mb2Type = 'SRB' # (Str): Second bearing type: CARB, TRB or SRB turbine.nacelle.yaw_motors_number = 8.0 # (Float): number of yaw motors turbine.nacelle.uptower_transformer = True turbine.nacelle.flange_length = 0.5 #m turbine.nacelle.gearbox_cm = 0.1 turbine.nacelle.hss_length = 1.5 turbine.nacelle.overhang = 5.0 #TODO - should come from turbine configuration level turbine.nacelle.check_fatigue = 0 #0 if no fatigue check, 1 if parameterized fatigue check, 2 if known loads inputs # TODO: should come from rotor (these are FAST outputs) turbine.nacelle.DrivetrainEfficiency = 0.95 turbine.nacelle.rotor_bending_moment_x = 330770.0# Nm turbine.nacelle.rotor_bending_moment_y = -16665000.0 # Nm turbine.nacelle.rotor_bending_moment_z = 2896300.0 # Nm turbine.nacelle.rotor_force_x = 599610.0 # N turbine.nacelle.rotor_force_y = 186780.0 # N turbine.nacelle.rotor_force_z = -842710.0 # N #turbine.nacelle.h0_rear = 1.35 # only used in drive smooth #turbine.nacelle.h0_front = 1.7 # ================= if wind_class == 'I': turbine.rotor.turbine_class = 'I' elif wind_class == 'III': turbine.rotor.turbine_class = 'III' # for fatigue based analysis of class III wind turbine turbine.tower.M_DEL = 1.028713178 * 1e3*np.array([7.8792E+003, 7.7507E+003, 7.4918E+003, 7.2389E+003, 6.9815E+003, 6.7262E+003, 6.4730E+003, 6.2174E+003, 5.9615E+003, 5.7073E+003, 5.4591E+003, 5.2141E+003, 4.9741E+003, 4.7399E+003, 4.5117E+003, 4.2840E+003, 4.0606E+003, 3.8360E+003, 3.6118E+003, 3.3911E+003, 3.1723E+003, 2.9568E+003, 2.7391E+003, 2.5294E+003, 2.3229E+003, 2.1246E+003, 1.9321E+003, 1.7475E+003, 1.5790E+003, 1.4286E+003, 1.3101E+003, 1.2257E+003, 1.1787E+003, 1.1727E+003, 1.1821E+003]) turbine.rotor.Mxb_damage = 1e3*np.array([2.3617E+003, 2.0751E+003, 1.8051E+003, 1.5631E+003, 1.2994E+003, 1.0388E+003, 8.1384E+002, 6.2492E+002, 4.6916E+002, 3.4078E+002, 2.3916E+002, 1.5916E+002, 9.9752E+001, 5.6139E+001, 2.6492E+001, 1.0886E+001, 3.7210E+000, 4.3206E-001]) turbine.rotor.Myb_damage = 1e3*np.array([2.5492E+003, 2.6261E+003, 2.4265E+003, 2.2308E+003, 1.9882E+003, 1.7184E+003, 1.4438E+003, 1.1925E+003, 9.6251E+002, 7.5564E+002, 5.7332E+002, 4.1435E+002, 2.8036E+002, 1.7106E+002, 8.7732E+001, 3.8678E+001, 1.3942E+001, 1.6600E+000]) elif wind_class == 'Offshore': turbine.rotor.turbine_class = 'I' # ================= # === jacket === #--- Set Jacket Input Parameters ---# Jcktins=JcktGeoInputs() Jcktins.nlegs =4 Jcktins.nbays =5 Jcktins.batter=12. Jcktins.dck_botz =16. Jcktins.weld2D =0.5 Jcktins.VPFlag = True #vertical pile T/F; to enable piles in frame3DD set pileinputs.ndiv>0 Jcktins.clamped= False #whether or not the bottom of the structure is rigidly connected. Use False when equivalent spring constants are being used. Jcktins.AFflag = False #whether or not to use apparent fixity piles Jcktins.PreBuildTPLvl = 2 #if >0, the TP is prebuilt according to rules per PreBuildTP #Soil inputs Soilinputs=SoilGeoInputs() Soilinputs.zbots =-np.array([3.,5.,7.,15.,30.,50.]) Soilinputs.gammas =np.array([10000.,10000.,10000.,10000.,10000.,10000.]) Soilinputs.cus =np.array([60000.,60000.,60000.,60000.,60000.,60000.]) Soilinputs.phis =np.array([26.,26.,26.,26.,26.,26])#np.array([36.,33.,26.,37.,35.,37.5])#np.array([36.,33.,26.,37.,35.,37.5]) Soilinputs.delta =25. Soilinputs.sndflg =True Soilinputs.PenderSwtch =False #True Soilinputs.SoilSF =1. #Water and wind inputs Waterinputs=WaterInputs() Waterinputs.wdepth =30. Waterinputs.wlevel =30. #Distance from bottom of structure to surface THIS, I believe is no longer needed as piles may be negative in z, to check and remove in case Waterinputs.T=12. #Wave Period Waterinputs.HW=10. #Wave Height '''Windinputs=WindInputs() Windinputs.HH=100. #CHECK HOW THIS COMPLIES.... Windinputs.U50HH=30. #assumed gust speed''' #RNA loads Fx-z, Mxx-zz #RNA_F=np.array([1000.e3,0.,0.,0.,0.,0.]) #Pile data Pilematin=MatInputs() Pilematin.matname=np.array(['steel']) Pilematin.E=np.array([ 25.e9]) Dpile=2.5#0.75 # 2.0 tpile=0.01 Lp=20. #45 Pileinputs=PileGeoInputs() Pileinputs.Pilematins=Pilematin Pileinputs.ndiv=0 #3 Pileinputs.Dpile=Dpile Pileinputs.tpile=tpile Pileinputs.Lp=Lp #[m] Embedment length #Legs data legmatin=MatInputs() legmatin.matname=(['steel','steel','steel','steel']) legmatin.E=np.array([2.0e11]) Dleg=np.array([2.0,1.8,1.8,1.8,1.8,1.8]) tleg=1.55*np.array([0.0254]).repeat(Dleg.size) leginputs=LegGeoInputs() leginputs.legZbot = 1.0 leginputs.ndiv=1 leginputs.legmatins=legmatin leginputs.Dleg=Dleg leginputs.tleg=tleg legbot_stmphin =1.5 #Distance from bottom of leg to second joint along z; must be>0 #Xbrc data Xbrcmatin=MatInputs() Xbrcmatin.matname=np.array(['steel']).repeat(Jcktins.nbays) Xbrcmatin.E=np.array([ 2.2e11, 2.0e11,2.0e11,2.0e11,2.0e11]) Dbrc=np.array([1.,1.,1.0,1.0,1.0]) tbrc=np.array([1.,1.,1.0,1.0,1.0])*0.0254 Xbrcinputs=XBrcGeoInputs() Xbrcinputs.Dbrc=Dbrc Xbrcinputs.tbrc=tbrc Xbrcinputs.ndiv=2#2 Xbrcinputs.Xbrcmatins=Xbrcmatin Xbrcinputs.precalc=True #This can be set to true if we want Xbraces to be precalculated in D and t, in which case the above set Dbrc and tbrc would be overwritten #Mbrc data Mbrcmatin=MatInputs() Mbrcmatin.matname=np.array(['steel']) Mbrcmatin.E=np.array([ 2.5e11]) Dbrc_mud=1.5 Mbrcinputs=MudBrcGeoInputs() Mbrcinputs.Dbrc_mud=Dbrc_mud Mbrcinputs.ndiv=2 Mbrcinputs.Mbrcmatins=Mbrcmatin Mbrcinputs.precalc=True #This can be set to true if we want Mudbrace to be precalculated in D and t, in which case the above set Dbrc_mud and tbrc_mud would be overwritten #Hbrc data Hbrcmatin=MatInputs() Hbrcmatin.matname=np.array(['steel']) Hbrcmatin.E=np.array([ 2.5e11]) Dbrc_hbrc=1.1 Hbrcinputs=HBrcGeoInputs() Hbrcinputs.Dbrch=Dbrc_hbrc Hbrcinputs.ndiv=0#2 Hbrcinputs.Hbrcmatins=Hbrcmatin Hbrcinputs.precalc=True #This can be set to true if we want Hbrace to be set=Xbrace top D and t, in which case the above set Dbrch and tbrch would be overwritten #TP data TPlumpinputs=TPlumpMass() TPlumpinputs.mass=300.e3 #[kg] TPstmpsmatin=MatInputs() TPbrcmatin=MatInputs() TPstemmatin=MatInputs() TPbrcmatin.matname=np.array(['steel']) TPbrcmatin.E=np.array([ 2.5e11]) TPstemmatin.matname=np.array(['steel']).repeat(2) TPstemmatin.E=np.array([ 2.1e11]).repeat(2) TPinputs=TPGeoInputs() TPinputs.TPbrcmatins=TPbrcmatin TPinputs.TPstemmatins=TPstemmatin TPinputs.TPstmpmatins=TPstmpsmatin TPinputs.Dstrut=1.6 TPinputs.Dgir=Dbrc_hbrc TPinputs.Dbrc=1.1 TPinputs.hstump=0.0#1.0 TPinputs.stumpndiv=1#2 TPinputs.brcndiv=1#2 TPinputs.girndiv=1#2 TPinputs.strutndiv=1#2 TPinputs.stemndiv=1#2 TPinputs.nstems=3 TPinputs.Dstem=np.array([6.]).repeat(TPinputs.nstems) TPinputs.tstem=np.array([0.1,0.11,0.11]) TPinputs.hstem=np.array([4.,3.,1.]) #Tower data Twrmatin=MatInputs() Twrmatin.matname=np.array(['steel']) Twrmatin.E=np.array([ 2.77e11]) Db=5.6 tb=0.05 Dt=Db*0.55 '''Twrinputs=TwrGeoInputs() Twrinputs.Twrmatins=Twrmatin #Twrinputs.Htwr=70. #Trumped by HH Twrinputs.Htwr2frac=0.2 #fraction of tower height with constant x-section Twrinputs.ndiv=np.array([6,6]) #ndiv for uniform and tapered section Twrinputs.Db=Db Twrinputs.DTRb=Db/tb #Twrinputs.Dt=Dt''' TwrRigidTop=True #False #False=Account for RNA via math rather than a physical rigidmember #RNA data '''RNAins=RNAprops() RNAins.mass=3*350.e3 RNAins.I[0]=86.579E+6 RNAins.I[1]=53.530E+6 RNAins.I[2]=58.112E+6 RNAins.CMoff[2]=2.34 RNAins.yawangle=45. #angle with respect to global X, CCW looking from above, wind from left RNAins.rna_weightM=True''' #Frame3DD parameters FrameAuxIns=Frame3DDaux() FrameAuxIns.sh_fg=1 #shear flag-->Timoshenko FrameAuxIns.deltaz=5. FrameAuxIns.geo_fg=0 FrameAuxIns.nModes = 6 # number of desired dynamic modes of vibration FrameAuxIns.Mmethod = 1 # 1: subspace Jacobi 2: Stodola FrameAuxIns.lump = 0 # 0: consistent mass ... 1: lumped mass matrix FrameAuxIns.tol = 1e-9 # mode shape tolerance FrameAuxIns.shift = 0.0 # shift value ... for unrestrained structures FrameAuxIns.gvector=np.array([0.,0.,-9.8065]) #GRAVITY #Pass all inputs to jacket assembly turbine.jacket.JcktGeoIn=Jcktins turbine.jacket.Soilinputs=Soilinputs turbine.jacket.Waterinputs=Waterinputs #turbine.jacket.Windinputs=Windinputs #turbine.jacket.RNA_F=RNA_F turbine.jacket.Pileinputs=Pileinputs turbine.jacket.leginputs=leginputs turbine.jacket.legbot_stmphin =legbot_stmphin turbine.jacket.Xbrcinputs=Xbrcinputs turbine.jacket.Mbrcinputs=Mbrcinputs turbine.jacket.Hbrcinputs=Hbrcinputs turbine.jacket.TPlumpinputs=TPlumpinputs turbine.jacket.TPinputs=TPinputs #turbine.jacket.RNAinputs=RNAins #turbine.jacket.Twrinputs=Twrinputs turbine.jacket.Twrinputs.Twrmatins=Twrmatin turbine.jacket.Twrinputs.Htwr2frac=0.2 #fraction of tower height with constant x-section turbine.jacket.Twrinputs.ndiv=np.array([6,6]) #ndiv for uniform and tapered section turbine.jacket.Twrinputs.Db=Db turbine.jacket.Twrinputs.Dt=Dt turbine.jacket.Twrinputs.DTRb=Db/tb turbine.jacket.Twrinputs.DTRt=Db/Dt # TODO double check turbine.jacket.TwrRigidTop=TwrRigidTop turbine.jacket.FrameAuxIns=FrameAuxIns