def compute(self, inputs, outputs): z = inputs["z"] windLoads = (DirectionVector( inputs["windLoads_Px"], inputs["windLoads_Py"], inputs["windLoads_Pz"]).inertialToWind( inputs["windLoads_beta"]).windToYaw(inputs["yaw"])) waveLoads = (DirectionVector( inputs["waveLoads_Px"], inputs["waveLoads_Py"], inputs["waveLoads_Pz"]).inertialToWind( inputs["waveLoads_beta"]).windToYaw(inputs["yaw"])) Px = np.interp(z, inputs["windLoads_z"], windLoads.x) + np.interp( z, inputs["waveLoads_z"], waveLoads.x) Py = np.interp(z, inputs["windLoads_z"], windLoads.y) + np.interp( z, inputs["waveLoads_z"], waveLoads.y) Pz = np.interp(z, inputs["windLoads_z"], windLoads.z) + np.interp( z, inputs["waveLoads_z"], waveLoads.z) qdyn = np.interp( z, inputs["windLoads_z"], inputs["windLoads_qdyn"]) + np.interp( z, inputs["waveLoads_z"], inputs["waveLoads_qdyn"]) # The following are redundant, at one point we will consolidate them to something that works for both cylinder (not using vartrees) and jacket (still using vartrees) outputs["Px"] = Px outputs["Py"] = Py outputs["Pz"] = Pz outputs["qdyn"] = qdyn
def compute(self, inputs, outputs): z = inputs['z'] hubHt = z[-1] # top of cylinder windLoads = DirectionVector( inputs['windLoads_Px'], inputs['windLoads_Py'], inputs['windLoads_Pz']).inertialToWind( inputs['windLoads_beta']).windToYaw(inputs['yaw']) waveLoads = DirectionVector( inputs['waveLoads_Px'], inputs['waveLoads_Py'], inputs['waveLoads_Pz']).inertialToWind( inputs['waveLoads_beta']).windToYaw(inputs['yaw']) Px = np.interp(z, inputs['windLoads_z'], windLoads.x) + np.interp( z, inputs['waveLoads_z'], waveLoads.x) Py = np.interp(z, inputs['windLoads_z'], windLoads.y) + np.interp( z, inputs['waveLoads_z'], waveLoads.y) Pz = np.interp(z, inputs['windLoads_z'], windLoads.z) + np.interp( z, inputs['waveLoads_z'], waveLoads.z) qdyn = np.interp( z, inputs['windLoads_z'], inputs['windLoads_qdyn']) + np.interp( z, inputs['waveLoads_z'], inputs['waveLoads_qdyn']) #The following are redundant, at one point we will consolidate them to something that works for both cylinder (not using vartrees) and jacket (still using vartrees) outputs['Px'] = Px outputs['Py'] = Py outputs['Pz'] = Pz outputs['qdyn'] = qdyn
def compute_partials(self, inputs, J, discrete_inputs): dF = DirectionVector.fromArray(inputs['F']).hubToYaw(inputs['tilt']) dFx, dFy, dFz = dF.dx, dF.dy, dF.dz dtopF_dFx = np.array([dFx['dx'], dFy['dx'], dFz['dx']]) dtopF_dFy = np.array([dFx['dy'], dFy['dy'], dFz['dy']]) dtopF_dFz = np.array([dFx['dz'], dFy['dz'], dFz['dz']]) dtopF_dF = hstack([dtopF_dFx, dtopF_dFy, dtopF_dFz]) dtopF_w_dm = np.array([0.0, 0.0, -gravity]) #dtopF = hstack([dtopF_dF, np.zeros((3, 6)), dtopF_w_dm, np.zeros((3, 3))]) dM = DirectionVector.fromArray(inputs['M']).hubToYaw(inputs['tilt']) dMx, dMy, dMz = dM.dx, dM.dy, dM.dz dMxcross, dMycross, dMzcross = self.save_rhub.cross_deriv( self.saveF, 'dr', 'dF') dtopM_dMx = np.array([dMx['dx'], dMy['dx'], dMz['dx']]) dtopM_dMy = np.array([dMx['dy'], dMy['dy'], dMz['dy']]) dtopM_dMz = np.array([dMx['dz'], dMy['dz'], dMz['dz']]) dtopM_dM = hstack([dtopM_dMx, dtopM_dMy, dtopM_dMz]) dM_dF = np.array([dMxcross['dF'], dMycross['dF'], dMzcross['dF']]) dtopM_dFx = np.dot(dM_dF, dtopF_dFx) dtopM_dFy = np.dot(dM_dF, dtopF_dFy) dtopM_dFz = np.dot(dM_dF, dtopF_dFz) dtopM_dF = hstack([dtopM_dFx, dtopM_dFy, dtopM_dFz]) dtopM_dr = np.array([dMxcross['dr'], dMycross['dr'], dMzcross['dr']]) #dMx_w_cross, dMy_w_cross, dMz_w_cross = self.save_rcm.cross_deriv(self.saveF_w, 'dr', 'dF') #if discrete_inputs['rna_weightM']: # dtopM_drnacm = np.array([dMx_w_cross['dr'], dMy_w_cross['dr'], dMz_w_cross['dr']]) # dtopM_dF_w = np.array([dMx_w_cross['dF'], dMy_w_cross['dF'], dMz_w_cross['dF']]) #else: # dtopM_drnacm = np.zeros((3, 3)) # dtopM_dF_w = np.zeros((3, 3)) dtopM_drnacm = np.zeros((3, 3)) dtopM_dF_w = np.zeros((3, 3)) dtopM_dm = np.dot(dtopM_dF_w, dtopF_w_dm) if discrete_inputs['downwind']: dtopM_dr[:, 0] *= -1 dtopM_drnacm[:, 0] *= -1 #dtopM = hstack([dtopM_dF, dtopM_dM, dtopM_dr, dtopM_dm, dtopM_drnacm]) J['top_F', 'F'] = dtopF_dF J['top_F', 'M'] = np.zeros((3, 3)) J['top_F', 'hub_cm'] = np.zeros((3, 3)) J['top_F', 'rna_mass'] = dtopF_w_dm J['top_F', 'rna_cm'] = np.zeros((3, 3)) J['top_M', 'F'] = dtopM_dF J['top_M', 'M'] = dtopM_dM J['top_M', 'hub_cm'] = dtopM_dr J['top_M', 'rna_mass'] = dtopM_dm J['top_M', 'rna_cm'] = dtopM_drnacm
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): F = inputs['F'] M = inputs['M'] tilt = float(inputs['tilt']) F = DirectionVector.fromArray(F).hubToYaw(tilt) M = DirectionVector.fromArray(M).hubToYaw(tilt) # change x-direction if downwind hub_cm = np.copy(inputs['hub_cm']) rna_cm = np.copy(inputs['rna_cm']) if discrete_inputs['downwind']: hub_cm[0] *= -1 rna_cm[0] *= -1 hub_cm = DirectionVector.fromArray(hub_cm) rna_cm = DirectionVector.fromArray(rna_cm) self.save_rhub = hub_cm self.save_rcm = rna_cm # aerodynamic moments M = M + hub_cm.cross(F) self.saveF = F ''' Removing this permanently gbarter 1/2020 because of too much confusion in TowerSE and Frame3DD From now on TowerSE will always add to loading of added mass items, including RNA # add weight loads F_w = DirectionVector(0.0, 0.0, -float(inputs['rna_mass'])*gravity) M_w = rna_cm.cross(F_w) self.saveF_w = F_w Fout = F + F_w if discrete_inputs['rna_weightM']: Mout = M + M_w else: Mout = M #REMOVE WEIGHT EFFECT TO ACCOUNT FOR P-Delta Effect print("!!!! No weight effect on rotor moments -TowerSE !!!!") ''' Fout = F Mout = M # put back in array outputs['top_F'] = np.array([Fout.x, Fout.y, Fout.z]) outputs['top_M'] = np.array([Mout.x, Mout.y, Mout.z])
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): F = inputs['F'] M = inputs['M'] tilt = float(inputs['tilt']) F = DirectionVector.fromArray(F).hubToYaw(tilt) M = DirectionVector.fromArray(M).hubToYaw(tilt) # change x-direction if downwind hub_cm = np.copy(inputs['hub_cm']) rna_cm = np.copy(inputs['rna_cm']) if discrete_inputs['downwind']: hub_cm[0] *= -1 rna_cm[0] *= -1 hub_cm = DirectionVector.fromArray(hub_cm) rna_cm = DirectionVector.fromArray(rna_cm) self.save_rhub = hub_cm self.save_rcm = rna_cm # aerodynamic moments M = M + hub_cm.cross(F) self.saveF = F # add weight loads F_w = DirectionVector(0.0, 0.0, -float(inputs['rna_mass'])*gravity) M_w = rna_cm.cross(F_w) self.saveF_w = F_w Fout = F + F_w if discrete_inputs['rna_weightM']: Mout = M + M_w else: Mout = M #REMOVE WEIGHT EFFECT TO ACCOUNT FOR P-Delta Effect print("!!!! No weight effect on rotor moments -TowerSE !!!!") # put back in array outputs['top_F'] = np.array([Fout.x, Fout.y, Fout.z]) outputs['top_M'] = np.array([Mout.x, Mout.y, Mout.z])
def test(self): x = np.array([1.0, 2.0]) y = np.array([1.3, 4.3]) z = np.array([2.3, 2.3]) a = DirectionVector(x, y, z) x = np.array([3.2, 1.5]) y = np.array([2.1, 3.2]) z = np.array([5.6, 7.7]) b = DirectionVector(x, y, z) c = a.cross(b) dx, dy, dz = a.cross_deriv(b) dx, dy, dz = a.cross_deriv_array(b) d = -a e = a + b f = a - b f += a f -= a k = a * b p = a / b p *= b p /= a e = a + 1.2 f = a - 1.2 f += 2. f -= 3. k = a * 4. p = a / 2. p *= 3. p /= 2. T1 = 5.0 T2 = 4.0 T3 = -3.0 tilt = 20.0 F = DirectionVector(T1, T2, T3).hubToYaw(tilt) Fp1 = DirectionVector(T1 + 1e-6, T2, T3).hubToYaw(tilt) Fp2 = DirectionVector(T1, T2 + 1e-6, T3).hubToYaw(tilt) Fp3 = DirectionVector(T1, T2, T3 + 1e-6).hubToYaw(tilt) Fp4 = DirectionVector(T1, T2, T3).hubToYaw(tilt + 1e-6) dFx = F.dx dFy = F.dy dFz = F.dz npt.assert_almost_equal(dFx['dx'], (Fp1.x - F.x) / 1e-6) npt.assert_almost_equal(dFx['dy'], (Fp2.x - F.x) / 1e-6) npt.assert_almost_equal(dFx['dz'], (Fp3.x - F.x) / 1e-6) npt.assert_almost_equal(dFx['dtilt'], (Fp4.x - F.x) / 1e-6) npt.assert_almost_equal(dFy['dx'], (Fp1.y - F.y) / 1e-6) npt.assert_almost_equal(dFy['dy'], (Fp2.y - F.y) / 1e-6) npt.assert_almost_equal(dFy['dz'], (Fp3.y - F.y) / 1e-6) npt.assert_almost_equal(dFy['dtilt'], (Fp4.y - F.y) / 1e-6) npt.assert_almost_equal(dFz['dx'], (Fp1.z - F.z) / 1e-6) npt.assert_almost_equal(dFz['dy'], (Fp2.z - F.z) / 1e-6) npt.assert_almost_equal(dFz['dz'], (Fp3.z - F.z) / 1e-6) npt.assert_almost_equal(dFz['dtilt'], (Fp4.z - F.z) / 1e-6) yaw = 3.0 F = DirectionVector(T1, T2, T3).hubToYaw(tilt).yawToWind(yaw) Fp1 = DirectionVector(T1 + 1e-6, T2, T3).hubToYaw(tilt).yawToWind(yaw) Fp2 = DirectionVector(T1, T2 + 1e-6, T3).hubToYaw(tilt).yawToWind(yaw) Fp3 = DirectionVector(T1, T2, T3 + 1e-6).hubToYaw(tilt).yawToWind(yaw) Fp4 = DirectionVector(T1, T2, T3).hubToYaw(tilt + 1e-6).yawToWind(yaw) Fp5 = DirectionVector(T1, T2, T3).hubToYaw(tilt).yawToWind(yaw + 1e-6) dFx = F.dx dFy = F.dy dFz = F.dz npt.assert_almost_equal(dFx['dx'], (Fp1.x - F.x) / 1e-6) npt.assert_almost_equal(dFx['dy'], (Fp2.x - F.x) / 1e-6) npt.assert_almost_equal(dFx['dz'], (Fp3.x - F.x) / 1e-6) npt.assert_almost_equal(dFx['dtilt'], (Fp4.x - F.x) / 1e-6) npt.assert_almost_equal(dFx['dyaw'], (Fp5.x - F.x) / 1e-6) npt.assert_almost_equal(dFy['dx'], (Fp1.y - F.y) / 1e-6) npt.assert_almost_equal(dFy['dy'], (Fp2.y - F.y) / 1e-6) npt.assert_almost_equal(dFy['dz'], (Fp3.y - F.y) / 1e-6) npt.assert_almost_equal(dFy['dtilt'], (Fp4.y - F.y) / 1e-6) npt.assert_almost_equal(dFy['dyaw'], (Fp5.y - F.y) / 1e-6) npt.assert_almost_equal(dFz['dx'], (Fp1.z - F.z) / 1e-6) npt.assert_almost_equal(dFz['dy'], (Fp2.z - F.z) / 1e-6) npt.assert_almost_equal(dFz['dz'], (Fp3.z - F.z) / 1e-6) npt.assert_almost_equal(dFz['dtilt'], (Fp4.z - F.z) / 1e-6) npt.assert_almost_equal(dFz['dyaw'], (Fp5.z - F.z) / 1e-6) inertial_F = F.windToInertial(20.) new_F = inertial_F.inertialToWind(20.) npt.assert_almost_equal(F.toArray(), new_F.toArray()) wind_F = F.yawToWind(15.) new_F = wind_F.windToYaw(15.) npt.assert_almost_equal(F.toArray(), new_F.toArray()) yaw_F = F.hubToYaw(12.) new_F = yaw_F.yawToHub(12.) npt.assert_almost_equal(F.toArray(), new_F.toArray()) az_F = F.hubToAzimuth(12.) new_F = az_F.azimuthToHub(12.) npt.assert_almost_equal(F.toArray(), new_F.toArray()) blade_F = F.azimuthToBlade(12.) new_F = blade_F.bladeToAzimuth(12.) npt.assert_almost_equal(F.toArray(), new_F.toArray()) blade_F = F.airfoilToBlade(12.) new_F = blade_F.bladeToAirfoil(12.) npt.assert_almost_equal(F.toArray(), new_F.toArray()) profile_F = F.airfoilToProfile() new_F = profile_F.profileToAirfoil() npt.assert_almost_equal(F.toArray(), new_F.toArray())
def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): ############################## def region_stacking(i, idx, start_nd_arc, end_nd_arc, layer_name, layer_thickness, fiber_orientation, layer_mat, material_dict, materials, region_loc): # Recieve start and end of composite sections chordwise, find which composites layers are in each # chordwise regions, generate the precomp composite class instance # error handling to makes sure there were no numeric errors causing values very close too, but not exactly, 0 or 1 start_nd_arc = [ 0. if start_nd_arci != 0. and np.isclose(start_nd_arci, 0.) else start_nd_arci for start_nd_arci in start_nd_arc ] end_nd_arc = [ 0. if end_nd_arci != 0. and np.isclose(end_nd_arci, 0.) else end_nd_arci for end_nd_arci in end_nd_arc ] start_nd_arc = [ 1. if start_nd_arci != 1. and np.isclose(start_nd_arci, 1.) else start_nd_arci for start_nd_arci in start_nd_arc ] end_nd_arc = [ 1. if end_nd_arci != 1. and np.isclose(end_nd_arci, 1.) else end_nd_arci for end_nd_arci in end_nd_arc ] # region end points dp = sorted(list(set(start_nd_arc + end_nd_arc))) #initialize n_plies = [] thk = [] theta = [] mat_idx = [] # loop through division points, find what layers make up the stack between those bounds for i_reg, (dp0, dp1) in enumerate(zip(dp[0:-1], dp[1:])): n_pliesi = [] thki = [] thetai = [] mati = [] for i_sec, start_nd_arci, end_nd_arci in zip( idx, start_nd_arc, end_nd_arc): name = layer_name[i_sec] if start_nd_arci <= dp0 and end_nd_arci >= dp1: if name in region_loc.keys(): if region_loc[name][i] == None: region_loc[name][i] = [i_reg] else: region_loc[name][i].append(i_reg) n_pliesi.append(1.) thki.append(layer_thickness[i_sec]) if fiber_orientation[i_sec] == None: thetai.append(0.) else: thetai.append(fiber_orientation[i_sec]) mati.append(material_dict[layer_mat[i_sec]]) n_plies.append(np.array(n_pliesi)) thk.append(np.array(thki)) theta.append(np.array(thetai)) mat_idx.append(np.array(mati)) # print('----------------------') # print('dp', dp) # print('n_plies', n_plies) # print('thk', thk) # print('theta', theta) # print('mat_idx', mat_idx) # print('materials', materials) sec = CompositeSection(dp, n_plies, thk, theta, mat_idx, materials) return sec, region_loc ############################## def web_stacking(i, web_idx, web_start_nd_arc, web_end_nd_arc, layer_thickness, fiber_orientation, layer_mat, material_dict, materials, flatback, upperCSi): dp = [] n_plies = [] thk = [] theta = [] mat_idx = [] if len(web_idx) > 0: dp = np.mean( (np.abs(web_start_nd_arc), np.abs(web_start_nd_arc)), axis=0).tolist() dp_all = [[-1. * start_nd_arci, -1. * end_nd_arci] for start_nd_arci, end_nd_arci in zip( web_start_nd_arc, web_end_nd_arc)] web_dp, web_ids = np.unique(dp_all, axis=0, return_inverse=True) for webi in np.unique(web_ids): # store variable values (thickness, orientation, material) for layers that make up each web, based on the mapping array web_ids n_pliesi = [ 1. for i_reg, web_idi in zip(web_idx, web_ids) if web_idi == webi ] thki = [ layer_thickness[i_reg] for i_reg, web_idi in zip(web_idx, web_ids) if web_idi == webi ] thetai = [ fiber_orientation[i_reg] for i_reg, web_idi in zip(web_idx, web_ids) if web_idi == webi ] thetai = [ 0. if theta_ij == None else theta_ij for theta_ij in thetai ] mati = [ material_dict[layer_mat[i_reg]] for i_reg, web_idi in zip(web_idx, web_ids) if web_idi == webi ] n_plies.append(np.array(n_pliesi)) thk.append(np.array(thki)) theta.append(np.array(thetai)) mat_idx.append(np.array(mati)) if flatback: dp.append(1.) n_plies.append(upperCSi.n_plies[-1]) thk.append(upperCSi.t[-1]) theta.append(upperCSi.theta[-1]) mat_idx.append(upperCSi.mat_idx[-1]) dp_out = sorted(list(set(dp))) sec = CompositeSection(dp_out, n_plies, thk, theta, mat_idx, materials) return sec ############################## layer_name = self.options['modeling_options']['blade']['layer_name'] layer_mat = self.options['modeling_options']['blade']['layer_mat'] upperCS = [None] * self.n_span lowerCS = [None] * self.n_span websCS = [None] * self.n_span profile = [None] * self.n_span # Check that the layer to be optimized actually exist te_ss_var_ok = False te_ps_var_ok = False spar_cap_ss_var_ok = False spar_cap_ps_var_ok = False for i_layer in range(self.n_layers): if layer_name[i_layer] == self.te_ss_var: te_ss_var_ok = True if layer_name[i_layer] == self.te_ps_var: te_ps_var_ok = True if layer_name[i_layer] == self.spar_cap_ss_var: spar_cap_ss_var_ok = True if layer_name[i_layer] == self.spar_cap_ps_var: spar_cap_ps_var_ok = True if te_ss_var_ok == False: print( 'The layer at the trailing edge suction side is set to be optimized, but does not exist in the input yaml. Please check.' ) if te_ps_var_ok == False: print( 'The layer at the trailing edge pressure side is set to be optimized, but does not exist in the input yaml. Please check.' ) if spar_cap_ss_var_ok == False: print( 'The layer at the spar cap suction side is set to be optimized, but does not exist in the input yaml. Please check.' ) if spar_cap_ps_var_ok == False: print( 'The layer at the spar cap pressure side is set to be optimized, but does not exist in the input yaml. Please check.' ) region_loc_vars = [ self.te_ss_var, self.te_ps_var, self.spar_cap_ss_var, self.spar_cap_ps_var ] region_loc_ss = { } # track precomp regions for user selected composite layers region_loc_ps = {} for var in region_loc_vars: region_loc_ss[var] = [None] * self.n_span region_loc_ps[var] = [None] * self.n_span ## Materials material_dict = {} materials = [] for i_mat in range(self.n_mat): materials.append( Orthotropic2DMaterial(inputs['E'][i_mat, 0], inputs['E'][i_mat, 1], inputs['G'][i_mat, 0], inputs['nu'][i_mat, 0], inputs['rho'][i_mat], discrete_inputs['mat_name'][i_mat])) material_dict[discrete_inputs['mat_name'][i_mat]] = i_mat ## Spanwise for i in range(self.n_span): # time0 = time.time() ## Profiles # rotate profile_i = inputs['coord_xy_interp'][i, :, :] profile_i_rot = np.column_stack( rotate(inputs['pitch_axis'][i], 0., profile_i[:, 0], profile_i[:, 1], np.radians(inputs['theta'][i]))) # import matplotlib.pyplot as plt # plt.plot(profile_i[:,0], profile_i[:,1]) # plt.plot(profile_i_rot[:,0], profile_i_rot[:,1]) # plt.axis('equal') # plt.title(i) # plt.show() # normalize profile_i_rot[:, 0] -= min(profile_i_rot[:, 0]) profile_i_rot = profile_i_rot / max(profile_i_rot[:, 0]) profile_i_rot_precomp = copy.copy(profile_i_rot) idx_s = 0 idx_le_precomp = np.argmax(profile_i_rot_precomp[:, 0]) if idx_le_precomp != 0: if profile_i_rot_precomp[0, 0] == profile_i_rot_precomp[-1, 0]: idx_s = 1 profile_i_rot_precomp = np.row_stack( (profile_i_rot_precomp[idx_le_precomp:], profile_i_rot_precomp[idx_s:idx_le_precomp, :])) profile_i_rot_precomp[:, 1] -= profile_i_rot_precomp[ np.argmin(profile_i_rot_precomp[:, 0]), 1] # # renormalize profile_i_rot_precomp[:, 0] -= min(profile_i_rot_precomp[:, 0]) profile_i_rot_precomp = profile_i_rot_precomp / max( profile_i_rot_precomp[:, 0]) if profile_i_rot_precomp[-1, 0] != 1.: profile_i_rot_precomp = np.row_stack( (profile_i_rot_precomp, profile_i_rot_precomp[0, :])) # 'web' at trailing edge needed for flatback airfoils if profile_i_rot_precomp[0, 1] != profile_i_rot_precomp[ -1, 1] and profile_i_rot_precomp[ 0, 0] == profile_i_rot_precomp[-1, 0]: flatback = True else: flatback = False profile[i] = Profile.initWithTEtoTEdata( profile_i_rot_precomp[:, 0], profile_i_rot_precomp[:, 1]) # import matplotlib.pyplot as plt # plt.plot(profile_i_rot_precomp[:,0], profile_i_rot_precomp[:,1]) # plt.axis('equal') # plt.title(i) # plt.show() idx_le = np.argmin(profile_i_rot[:, 0]) profile_i_arc = arc_length(profile_i_rot) arc_L = profile_i_arc[-1] profile_i_arc /= arc_L loc_LE = profile_i_arc[idx_le] len_PS = 1. - loc_LE ## Composites ss_idx = [] ss_start_nd_arc = [] ss_end_nd_arc = [] ps_idx = [] ps_start_nd_arc = [] ps_end_nd_arc = [] web_start_nd_arc = [] web_end_nd_arc = [] web_idx = [] # Determine spanwise composite layer elements that are non-zero at this spanwise location, # determine their chord-wise start and end location on the pressure and suctions side spline_arc2xnd = PchipInterpolator(profile_i_arc, profile_i_rot[:, 0]) # time1 = time.time() for idx_sec in range(self.n_layers): if discrete_inputs['definition_layer'][idx_sec] != 10: if inputs['layer_thickness'][idx_sec, i] != 0.: if inputs['layer_start_nd'][ idx_sec, i] < loc_LE or inputs['layer_end_nd'][ idx_sec, i] < loc_LE: ss_idx.append(idx_sec) if inputs['layer_start_nd'][idx_sec, i] < loc_LE: # ss_start_nd_arc.append(sec['start_nd_arc']['values'][i]) ss_end_nd_arc_temp = float( spline_arc2xnd( inputs['layer_start_nd'][idx_sec, i])) if ss_end_nd_arc_temp > 1 or ss_end_nd_arc_temp < 0: exit( 'Error in the definition of material ' + layer_name[idx_sec] + '. It cannot fit in the section number ' + str(i) + ' at span location ' + str(inputs['r'][i] / inputs['r'][-1] * 100.) + ' %.') if ss_end_nd_arc_temp == profile_i_rot[ 0, 0] and profile_i_rot[0, 0] != 1.: ss_end_nd_arc_temp = 1. ss_end_nd_arc.append(ss_end_nd_arc_temp) else: ss_end_nd_arc.append(1.) # ss_end_nd_arc.append(min(sec['end_nd_arc']['values'][i], loc_LE)/loc_LE) if inputs['layer_end_nd'][idx_sec, i] < loc_LE: ss_start_nd_arc.append( float( spline_arc2xnd( inputs['layer_end_nd'][idx_sec, i]))) else: ss_start_nd_arc.append(0.) if inputs['layer_start_nd'][ idx_sec, i] > loc_LE or inputs['layer_end_nd'][ idx_sec, i] > loc_LE: ps_idx.append(idx_sec) # ps_start_nd_arc.append((max(sec['start_nd_arc']['values'][i], loc_LE)-loc_LE)/len_PS) # ps_end_nd_arc.append((min(sec['end_nd_arc']['values'][i], 1.)-loc_LE)/len_PS) if inputs['layer_start_nd'][ idx_sec, i] > loc_LE and inputs['layer_end_nd'][ idx_sec, i] < loc_LE: # ps_start_nd_arc.append(float(remap2grid(profile_i_arc, profile_i_rot[:,0], sec['start_nd_arc']['values'][i]))) ps_end_nd_arc.append(1.) else: ps_end_nd_arc_temp = float( spline_arc2xnd( inputs['layer_end_nd'][idx_sec, i])) if np.isclose(ps_end_nd_arc_temp, profile_i_rot[-1, 0] ) and profile_i_rot[-1, 0] != 1.: ps_end_nd_arc_temp = 1. ps_end_nd_arc.append(ps_end_nd_arc_temp) if inputs['layer_start_nd'][idx_sec, i] < loc_LE: ps_start_nd_arc.append(0.) else: ps_start_nd_arc.append( float( spline_arc2xnd( inputs['layer_start_nd'][idx_sec, i]))) else: target_idx = inputs['layer_web'][idx_sec] - 1 if inputs['layer_thickness'][idx_sec, i] != 0.: web_idx.append(idx_sec) start_nd_arc = float( spline_arc2xnd( inputs['web_start_nd'][int(target_idx), i])) end_nd_arc = float( spline_arc2xnd( inputs['web_end_nd'][int(target_idx), i])) web_start_nd_arc.append(start_nd_arc) web_end_nd_arc.append(end_nd_arc) # time1 = time.time() - time1 # print(time1) # generate the Precomp composite stacks for chordwise regions if np.min([ss_start_nd_arc, ss_end_nd_arc]) < 0 or np.max( [ss_start_nd_arc, ss_end_nd_arc]) > 1: print('Error in the layer definition at station number ' + str(i)) exit() upperCS[i], region_loc_ss = region_stacking( i, ss_idx, ss_start_nd_arc, ss_end_nd_arc, layer_name, inputs['layer_thickness'][:, i], inputs['fiber_orientation'][:, i], layer_mat, material_dict, materials, region_loc_ss) lowerCS[i], region_loc_ps = region_stacking( i, ps_idx, ps_start_nd_arc, ps_end_nd_arc, layer_name, inputs['layer_thickness'][:, i], inputs['fiber_orientation'][:, i], layer_mat, material_dict, materials, region_loc_ps) if len(web_idx) > 0 or flatback: websCS[i] = web_stacking(i, web_idx, web_start_nd_arc, web_end_nd_arc, inputs['layer_thickness'][:, i], inputs['fiber_orientation'][:, i], layer_mat, material_dict, materials, flatback, upperCS[i]) else: websCS[i] = CompositeSection([], [], [], [], [], []) sector_idx_strain_spar_cap_ss = [ None if regs == None else regs[int(len(regs) / 2)] for regs in region_loc_ss[self.spar_cap_ss_var] ] sector_idx_strain_spar_cap_ps = [ None if regs == None else regs[int(len(regs) / 2)] for regs in region_loc_ps[self.spar_cap_ps_var] ] sector_idx_strain_te_ss = [ None if regs == None else regs[int(len(regs) / 2)] for regs in region_loc_ss[self.te_ss_var] ] sector_idx_strain_te_ps = [ None if regs == None else regs[int(len(regs) / 2)] for regs in region_loc_ps[self.te_ps_var] ] # Get Beam Properties beam = PreComp(inputs['r'], inputs['chord'], np.zeros_like( inputs['r']), inputs['pitch_axis'], inputs['precurve'], inputs['presweep'], profile, materials, upperCS, lowerCS, websCS, sector_idx_strain_spar_cap_ps, sector_idx_strain_spar_cap_ss, sector_idx_strain_te_ps, sector_idx_strain_te_ss) EIxx, EIyy, GJ, EA, EIxy, x_ec, y_ec, rhoA, area, rhoJ, Tw_iner, flap_iner, edge_iner, x_tc, y_tc, x_sc, y_sc, x_cg, y_cg = beam.sectionProperties( ) # outputs['eps_crit_spar'] = beam.panelBucklingStrain(sector_idx_strain_spar_cap_ss) # outputs['eps_crit_te'] = beam.panelBucklingStrain(sector_idx_strain_te_ss) xu_strain_spar, xl_strain_spar, yu_strain_spar, yl_strain_spar = beam.criticalStrainLocations( sector_idx_strain_spar_cap_ss, sector_idx_strain_spar_cap_ps) xu_strain_te, xl_strain_te, yu_strain_te, yl_strain_te = beam.criticalStrainLocations( sector_idx_strain_te_ss, sector_idx_strain_te_ps) # Store what materials make up the composites for SC/TE for i in range(self.n_span): for j in range(self.n_mat): if sector_idx_strain_spar_cap_ss[i]: if j in upperCS[i].mat_idx[ sector_idx_strain_spar_cap_ss[i]]: outputs['sc_ss_mats'][i, j] = 1. if sector_idx_strain_spar_cap_ps[i]: if j in lowerCS[i].mat_idx[ sector_idx_strain_spar_cap_ps[i]]: outputs['sc_ps_mats'][i, j] = 1. if sector_idx_strain_te_ss[i]: if j in upperCS[i].mat_idx[sector_idx_strain_te_ss[i]]: outputs['te_ss_mats'][i, j] = 1. if sector_idx_strain_te_ps[i]: if j in lowerCS[i].mat_idx[sector_idx_strain_te_ps[i]]: outputs['te_ps_mats'][i, j] = 1. outputs['z'] = inputs['r'] outputs['EIxx'] = EIxx outputs['EIyy'] = EIyy outputs['GJ'] = GJ outputs['EA'] = EA outputs['EIxy'] = EIxy outputs['x_ec'] = x_ec outputs['y_ec'] = y_ec outputs['rhoA'] = rhoA outputs['A'] = area outputs['rhoJ'] = rhoJ outputs['Tw_iner'] = Tw_iner outputs['flap_iner'] = flap_iner outputs['edge_iner'] = edge_iner outputs["x_tc"] = x_tc outputs["y_tc"] = y_tc outputs["x_sc"] = x_sc outputs["y_sc"] = y_sc outputs["x_cg"] = x_cg outputs["y_cg"] = y_cg outputs['xu_strain_spar'] = xu_strain_spar outputs['xl_strain_spar'] = xl_strain_spar outputs['yu_strain_spar'] = yu_strain_spar outputs['yl_strain_spar'] = yl_strain_spar outputs['xu_strain_te'] = xu_strain_te outputs['xl_strain_te'] = xl_strain_te outputs['yu_strain_te'] = yu_strain_te outputs['yl_strain_te'] = yl_strain_te # Compute mass and inertia of blade and rotor blade_mass = np.trapz(rhoA, inputs['r']) blade_moment_of_inertia = np.trapz(rhoA * inputs['r']**2., inputs['r']) tilt = inputs['uptilt'] n_blades = discrete_inputs['n_blades'] mass_all_blades = n_blades * blade_mass Ibeam = n_blades * blade_moment_of_inertia Ixx = Ibeam Iyy = Ibeam / 2.0 # azimuthal average for 2 blades, exact for 3+ Izz = Ibeam / 2.0 Ixy = 0.0 Ixz = 0.0 Iyz = 0.0 # azimuthal average for 2 blades, exact for 3+ # rotate to yaw c.s. I = DirectionVector(Ixx, Iyy, Izz).hubToYaw( tilt) # because off-diagonal components are all zero I_all_blades = np.r_[I.x, I.y, I.z, Ixy, Ixz, Iyz] outputs['blade_mass'] = blade_mass outputs['blade_moment_of_inertia'] = blade_moment_of_inertia outputs['mass_all_blades'] = mass_all_blades outputs['I_all_blades'] = I_all_blades # Placeholder - rotor cost bcm = blade_cost_model(verbosity=self.verbosity) bcm.name = '' bcm.materials = {} bcm.mat_options = {} bcm.mat_options['core_mat_id'] = np.zeros(self.n_mat) bcm.mat_options['coating_mat_id'] = -1 bcm.mat_options['le_reinf_mat_id'] = -1 bcm.mat_options['te_reinf_mat_id'] = -1 for i_mat in range(self.n_mat): name = discrete_inputs['mat_name'][i_mat] # if name != 'resin': bcm.materials[name] = {} bcm.materials[name]['id'] = i_mat + 1 bcm.materials[name]['name'] = discrete_inputs['mat_name'][i_mat] bcm.materials[name]['density'] = inputs['rho'][i_mat] bcm.materials[name]['unit_cost'] = inputs['unit_cost'][i_mat] bcm.materials[name]['waste'] = inputs['waste'][i_mat] * 100. if discrete_inputs['component_id'][i_mat] > 1: # It's a composite bcm.materials[name]['fiber_density'] = inputs['rho_fiber'][ i_mat] bcm.materials[name]['area_density_dry'] = inputs[ 'rho_area_dry'][i_mat] bcm.materials[name]['fvf'] = inputs['fvf'][i_mat] * 100. bcm.materials[name]['fwf'] = inputs['fwf'][i_mat] * 100. bcm.materials[name]['ply_t'] = inputs['ply_t'][i_mat] if discrete_inputs['component_id'][ i_mat] > 3: # The material does not need to be cut@station bcm.materials[name]['cut@station'] = 'N' else: bcm.materials[name]['cut@station'] = 'Y' bcm.materials[name]['roll_mass'] = inputs['roll_mass'][ i_mat] else: bcm.materials[name]['fvf'] = 100. bcm.materials[name]['fwf'] = 100. bcm.materials[name]['cut@station'] = 'N' if discrete_inputs['component_id'][i_mat] <= 0: bcm.materials[name]['ply_t'] = inputs['ply_t'][i_mat] if discrete_inputs['component_id'][i_mat] == 0: bcm.mat_options['coating_mat_id'] = bcm.materials[name][ 'id'] # Assigning the material to the coating elif discrete_inputs['component_id'][i_mat] == 1: bcm.mat_options['core_mat_id'][ bcm.materials[name]['id'] - 1] = 1 # Assigning the material to the core elif discrete_inputs['component_id'][i_mat] == 2: bcm.mat_options['skin_mat_id'] = bcm.materials[name][ 'id'] # Assigning the material to the shell skin elif discrete_inputs['component_id'][i_mat] == 3: bcm.mat_options['skinwebs_mat_id'] = bcm.materials[name][ 'id'] # Assigning the material to the webs skin elif discrete_inputs['component_id'][i_mat] == 4: bcm.mat_options['sc_mat_id'] = bcm.materials[name][ 'id'] # Assigning the material to the spar caps elif discrete_inputs['component_id'][i_mat] == 5: bcm.mat_options['le_reinf_mat_id'] = bcm.materials[name][ 'id'] # Assigning the material to the le reinf bcm.mat_options['te_reinf_mat_id'] = bcm.materials[name][ 'id'] # Assigning the material to the te reinf bcm.upperCS = upperCS bcm.lowerCS = lowerCS bcm.websCS = websCS bcm.profile = profile bcm.chord = inputs['chord'] bcm.r = inputs['r'] - inputs['r'][0] bcm.bladeLength = inputs['r'][-1] - inputs['r'][0] bcm.le_location = inputs['pitch_axis'] blade_cost, blade_mass = bcm.execute_blade_cost_model() outputs['total_blade_cost'] = blade_cost outputs['total_blade_mass'] = blade_mass
get_cp_cm = CCBlade(r, chord, twist, af, hub_r, Rtip, B, rho, mu, cone, tilt, 0., shearExp, hub_height, nSector, presweep, precurve[-1], presweep, presweep[-1], tiploss, hubloss, wakerotation, usecd) # get_cp_cm.induction = True Omega = Uhub * tsr / Rtip * 30.0 / np.pi # Rotor speed myout, derivs = get_cp_cm.evaluate([Uhub], [Omega], [pitch], coefficients=True) P, T, Q, M, CP, CT, CQ, CM = [ myout[key] for key in ['P', 'T', 'Q', 'M', 'CP', 'CT', 'CQ', 'CM'] ] get_cp_cm.induction_inflow = True loads, deriv = get_cp_cm.distributedAeroLoads([Uhub], Omega, pitch, 0.) ########## Post Process Output ########## # Compute forces in the airfoil coordinate system, pag 21 of https://www.nrel.gov/docs/fy13osti/58819.pdf P_b = DirectionVector(loads['Np'], -loads['Tp'], 0) P_af = P_b.bladeToAirfoil(twist) # Compute lift and drag forces F = P_b.bladeToAirfoil(twist + loads['alpha'] + pitch) # Print output .dat file with the quantities distributed along span np.savetxt( os.path.join(output_folder, 'distributed_quantities.dat'), np.array([ s, r, loads['alpha'], loads['a'], loads['ap'], loads['Cl'], loads['Cd'], F.x, F.y, loads['Np'], -loads['Tp'], P_af.x, P_af.y ]).T, header= 'Blade nondimensional span position (-) \t Rotor position (m) \t Angle of attack (deg) \t Axial induction (-) \t Tangential induction (-) \t Lift coefficient (-) \t Drag coefficient (-) \t Lift force (N) \t Drag force (N) \t Force along x BCS - N (N) \t Force along y BCS - T (N) \t Force along x ACS (N) \t Force along y ACS (N)', delimiter='\t') # Print output .dat file with the rotor equivalent quantities np.savetxt(