def rotor_from_excel_type03(in_op_params, in_geom_params, in_excel_description, in_options): """ generate_from_excel_type03_db Function needed to generate a wind turbine from an excel database type03 Args: op_param (dict): Dictionary with operating parameters geom_param (dict): Dictionray with geometical parameters excel_description (dict): Dictionary describing the sheets of the excel file option (dict): Dictionary with the different options for the wind turbine generation Returns: rotor (sharpy.utils.generate_cases.AeroelasticInfromation): Aeroelastic information of the rotor """ # Default values op_params = {} op_params['rotation_velocity'] = None # Rotation velocity of the rotor op_params['pitch_deg'] = None # pitch angle in degrees op_params[ 'wsp'] = 0. # wind speed (It may be needed for discretisation purposes) op_params[ 'dt'] = 0. # time step (It may be needed for discretisation purposes) geom_params = {} geom_params[ 'chord_panels'] = None # Number of panels on the blade surface in the chord direction geom_params[ 'tol_remove_points'] = 1e-3 # maximum distance to remove adjacent points geom_params[ 'n_points_camber'] = 100 # number of points to define the camber of the airfoil geom_params[ 'h5_cross_sec_prop'] = None # h5 containing mass and stiffness matrices along the blade geom_params['m_distribution'] = 'uniform' # options = {} options[ 'camber_effect_on_twist'] = False # When true plain airfoils are used and the blade is twisted and preloaded based on thin airfoil theory options[ 'user_defined_m_distribution_type'] = None # type of distribution of the chordwise panels when 'm_distribution' == 'user_defined' options['include_polars'] = False # excel_description = {} excel_description['excel_file_name'] = 'database_excel_type02.xlsx' excel_description['excel_sheet_parameters'] = 'parameters' excel_description['excel_sheet_structural_blade'] = 'structural_blade' excel_description[ 'excel_sheet_discretization_blade'] = 'discretization_blade' excel_description['excel_sheet_aero_blade'] = 'aero_blade' excel_description['excel_sheet_airfoil_info'] = 'airfoil_info' excel_description['excel_sheet_airfoil_chord'] = 'airfoil_coord' # Overwrite the default values with the values of the input arguments for key in in_op_params: op_params[key] = in_op_params[key] for key in in_geom_params: geom_params[key] = in_geom_params[key] for key in in_options: options[key] = in_options[key] for key in in_excel_description: excel_description[key] = in_excel_description[key] # Put the dictionaries information into variables (to avoid changing the function) rotation_velocity = op_params['rotation_velocity'] pitch_deg = op_params['pitch_deg'] wsp = op_params['wsp'] dt = op_params['dt'] chord_panels = geom_params['chord_panels'] tol_remove_points = geom_params['tol_remove_points'] n_points_camber = geom_params['n_points_camber'] h5_cross_sec_prop = geom_params['h5_cross_sec_prop'] m_distribution = geom_params['m_distribution'] camber_effect_on_twist = options['camber_effect_on_twist'] user_defined_m_distribution_type = options[ 'user_defined_m_distribution_type'] include_polars = options['include_polars'] excel_file_name = excel_description['excel_file_name'] excel_sheet_parameters = excel_description['excel_sheet_parameters'] excel_sheet_structural_blade = excel_description[ 'excel_sheet_structural_blade'] excel_sheet_discretization_blade = excel_description[ 'excel_sheet_discretization_blade'] excel_sheet_aero_blade = excel_description['excel_sheet_aero_blade'] excel_sheet_airfoil_info = excel_description['excel_sheet_airfoil_info'] excel_sheet_airfoil_coord = excel_description['excel_sheet_airfoil_chord'] ###################################################################### ## BLADE ###################################################################### blade = gc.AeroelasticInformation() ###################################################################### ### STRUCTURE ###################################################################### # Read blade structural information from excel file rR_structural = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'rR') OutPElAxis = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'OutPElAxis') InPElAxis = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'InPElAxis') ElAxisAftLEc = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'ElAxisAftLEc') StrcTwst = gc.read_column_sheet_type01( excel_file_name, excel_sheet_structural_blade, 'StrcTwst') * deg2rad BMassDen = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'BMassDen') FlpStff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'FlpStff') EdgStff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'EdgStff') FlapEdgeStiff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'FlapEdgeStiff') GJStff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'GJStff') EAStff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'EAStff') FlpIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'FlpIner') EdgIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'EdgIner') FlapEdgeIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'FlapEdgeIner') PrebendRef = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'PrebendRef') PreswpRef = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'PreswpRef') OutPcg = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'OutPcg') InPcg = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'InPcg') # Blade parameters TipRad = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'TipRad') # HubRad = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'HubRad') # Discretization points rR = gc.read_column_sheet_type01(excel_file_name, excel_sheet_discretization_blade, 'rR') # Interpolate excel variables into the correct locations # Geometry if rR[0] < rR_structural[0]: rR_structural = np.concatenate((np.array([0.]), rR_structural), ) OutPElAxis = np.concatenate((np.array([OutPElAxis[0]]), OutPElAxis), ) InPElAxis = np.concatenate((np.array([InPElAxis[0]]), InPElAxis), ) ElAxisAftLEc = np.concatenate( (np.array([ElAxisAftLEc[0]]), ElAxisAftLEc), ) StrcTwst = np.concatenate((np.array([StrcTwst[0]]), StrcTwst), ) BMassDen = np.concatenate((np.array([BMassDen[0]]), BMassDen), ) FlpStff = np.concatenate((np.array([FlpStff[0]]), FlpStff), ) EdgStff = np.concatenate((np.array([EdgStff[0]]), EdgStff), ) FlapEdgeStiff = np.concatenate( (np.array([FlapEdgeStiff[0]]), FlapEdgeStiff), ) GJStff = np.concatenate((np.array([GJStff[0]]), GJStff), ) EAStff = np.concatenate((np.array([EAStff[0]]), EAStff), ) FlpIner = np.concatenate((np.array([FlpIner[0]]), FlpIner), ) EdgIner = np.concatenate((np.array([EdgIner[0]]), EdgIner), ) FlapEdgeIner = np.concatenate( (np.array([FlapEdgeIner[0]]), FlapEdgeIner), ) PrebendRef = np.concatenate((np.array([PrebendRef[0]]), PrebendRef), ) PreswpRef = np.concatenate((np.array([PreswpRef[0]]), PreswpRef), ) OutPcg = np.concatenate((np.array([OutPcg[0]]), OutPcg), ) InPcg = np.concatenate((np.array([InPcg[0]]), InPcg), ) # Base parameters use_excel_struct_as_elem = False if use_excel_struct_as_elem: blade.StructuralInformation.num_node_elem = 3 blade.StructuralInformation.num_elem = len(rR) - 2 blade.StructuralInformation.compute_basic_num_node() node_r, elem_r = create_node_radial_pos_from_elem_centres( rR * TipRad, blade.StructuralInformation.num_node, blade.StructuralInformation.num_elem, blade.StructuralInformation.num_node_elem) else: # Use excel struct as nodes # Check the number of nodes blade.StructuralInformation.num_node_elem = 3 blade.StructuralInformation.num_node = len(rR) if ((len(rR) - 1) % (blade.StructuralInformation.num_node_elem - 1)) == 0: blade.StructuralInformation.num_elem = int( (len(rR) - 1) / (blade.StructuralInformation.num_node_elem - 1)) node_r = rR * TipRad elem_rR = rR[1::2] + 0. elem_r = rR[1::2] * TipRad + 0. else: raise RuntimeError( ("ERROR: Cannot build %d-noded elements from %d nodes" % (blade.StructuralInformation.num_node_elem, blade.StructuralInformation.num_node))) node_y = np.interp(rR, rR_structural, InPElAxis) + np.interp( rR, rR_structural, PreswpRef) node_z = -np.interp(rR, rR_structural, OutPElAxis) - np.interp( rR, rR_structural, PrebendRef) node_twist = -1.0 * np.interp(rR, rR_structural, StrcTwst) coordinates = create_blade_coordinates( blade.StructuralInformation.num_node, node_r, node_y, node_z) if h5_cross_sec_prop is None: # Stiffness elem_EA = np.interp(elem_rR, rR_structural, EAStff) elem_EIy = np.interp(elem_rR, rR_structural, FlpStff) elem_EIz = np.interp(elem_rR, rR_structural, EdgStff) elem_EIyz = np.interp(elem_rR, rR_structural, FlapEdgeStiff) elem_GJ = np.interp(elem_rR, rR_structural, GJStff) # Stiffness: estimate unknown properties cout.cout_wrap.print_file = False cout.cout_wrap( 'WARNING: The poisson cofficient is assumed equal to 0.3', 3) cout.cout_wrap('WARNING: Cross-section area is used as shear area', 3) poisson_coef = 0.3 elem_GAy = elem_EA / 2.0 / (1.0 + poisson_coef) elem_GAz = elem_EA / 2.0 / (1.0 + poisson_coef) # Inertia elem_pos_cg_B = np.zeros((blade.StructuralInformation.num_elem, 3), ) elem_pos_cg_B[:, 1] = np.interp(elem_rR, rR_structural, InPcg) elem_pos_cg_B[:, 2] = -np.interp(elem_rR, rR_structural, OutPcg) elem_mass_per_unit_length = np.interp(elem_rR, rR_structural, BMassDen) elem_mass_iner_y = np.interp(elem_rR, rR_structural, FlpIner) elem_mass_iner_z = np.interp(elem_rR, rR_structural, EdgIner) elem_mass_iner_yz = np.interp(elem_rR, rR_structural, FlapEdgeIner) # Inertia: estimate unknown properties cout.cout_wrap( 'WARNING: Using perpendicular axis theorem to compute the inertia around xB', 3) elem_mass_iner_x = elem_mass_iner_y + elem_mass_iner_z # Generate blade structural properties blade.StructuralInformation.create_mass_db_from_vector( elem_mass_per_unit_length, elem_mass_iner_x, elem_mass_iner_y, elem_mass_iner_z, elem_pos_cg_B, elem_mass_iner_yz) blade.StructuralInformation.create_stiff_db_from_vector( elem_EA, elem_GAy, elem_GAz, elem_GJ, elem_EIy, elem_EIz, elem_EIyz) else: # read Mass/Stiffness from database cross_prop = h5.readh5(h5_cross_sec_prop).str_prop # create mass_db/stiffness_db (interpolate at mid-node of each element) blade.StructuralInformation.mass_db = scint.interp1d( cross_prop.radius, cross_prop.M, kind='cubic', copy=False, assume_sorted=True, axis=0, bounds_error=False, fill_value='extrapolate')(node_r[1::2]) blade.StructuralInformation.stiffness_db = scint.interp1d( cross_prop.radius, cross_prop.K, kind='cubic', copy=False, assume_sorted=True, axis=0, bounds_error=False, fill_value='extrapolate')(node_r[1::2]) blade.StructuralInformation.generate_1to1_from_vectors( num_node_elem=blade.StructuralInformation.num_node_elem, num_node=blade.StructuralInformation.num_node, num_elem=blade.StructuralInformation.num_elem, coordinates=coordinates, stiffness_db=blade.StructuralInformation.stiffness_db, mass_db=blade.StructuralInformation.mass_db, frame_of_reference_delta='y_AFoR', vec_node_structural_twist=node_twist, num_lumped_mass=0) # Boundary conditions blade.StructuralInformation.boundary_conditions = np.zeros( (blade.StructuralInformation.num_node), dtype=int) blade.StructuralInformation.boundary_conditions[0] = 1 blade.StructuralInformation.boundary_conditions[-1] = -1 ###################################################################### ### AERODYNAMICS ###################################################################### # Read blade aerodynamic information from excel file rR_aero = gc.read_column_sheet_type01(excel_file_name, excel_sheet_aero_blade, 'rR') chord_aero = gc.read_column_sheet_type01(excel_file_name, excel_sheet_aero_blade, 'BlChord') thickness_aero = gc.read_column_sheet_type01(excel_file_name, excel_sheet_aero_blade, 'BlThickness') pure_airfoils_names = gc.read_column_sheet_type01( excel_file_name, excel_sheet_airfoil_info, 'Name') pure_airfoils_thickness = gc.read_column_sheet_type01( excel_file_name, excel_sheet_airfoil_info, 'Thickness') node_ElAxisAftLEc = np.interp(node_r, rR_structural * TipRad, ElAxisAftLEc) # Read coordinates of the pure airfoils n_pure_airfoils = len(pure_airfoils_names) pure_airfoils_camber = np.zeros((n_pure_airfoils, n_points_camber, 2), ) xls = pd.ExcelFile(excel_file_name) excel_db = pd.read_excel(xls, sheet_name=excel_sheet_airfoil_coord) for iairfoil in range(n_pure_airfoils): # Look for the NaN icoord = 2 while (not (math.isnan( excel_db["%s_x" % pure_airfoils_names[iairfoil]][icoord]))): icoord += 1 if (icoord == len(excel_db["%s_x" % pure_airfoils_names[iairfoil]])): break # Compute the camber of the airfoils at the defined chord points pure_airfoils_camber[iairfoil, :, 0], pure_airfoils_camber[ iairfoil, :, 1] = gc.get_airfoil_camber( excel_db["%s_x" % pure_airfoils_names[iairfoil]][2:icoord], excel_db["%s_y" % pure_airfoils_names[iairfoil]][2:icoord], n_points_camber) # Basic variables surface_distribution = np.zeros((blade.StructuralInformation.num_elem), dtype=int) # Interpolate in the correct positions node_chord = np.interp(node_r, rR_aero * TipRad, chord_aero) # Define the nodes with aerodynamic properties # Look for the first element that is goint to be aerodynamic first_aero_elem = 0 while (elem_r[first_aero_elem] <= rR_aero[0] * TipRad): first_aero_elem += 1 first_aero_node = first_aero_elem * ( blade.StructuralInformation.num_node_elem - 1) aero_node = np.zeros((blade.StructuralInformation.num_node, ), dtype=bool) aero_node[first_aero_node:] = np.ones( (blade.StructuralInformation.num_node - first_aero_node, ), dtype=bool) # Define the airfoil at each stage # airfoils = blade.AerodynamicInformation.interpolate_airfoils_camber(pure_airfoils_camber,excel_aero_r, node_r, n_points_camber) node_thickness = np.interp(node_r, rR_aero * TipRad, thickness_aero) airfoils = blade.AerodynamicInformation.interpolate_airfoils_camber_thickness( pure_airfoils_camber, pure_airfoils_thickness, node_thickness, n_points_camber) airfoil_distribution = np.linspace(0, blade.StructuralInformation.num_node - 1, blade.StructuralInformation.num_node, dtype=int) # User defined m distribution if (m_distribution == 'user_defined') and (user_defined_m_distribution_type == 'last_geometric'): blade_nodes = blade.StructuralInformation.num_node udmd_by_nodes = np.zeros((blade_nodes, chord_panels[0] + 1)) for inode in range(blade_nodes): r = np.linalg.norm( blade.StructuralInformation.coordinates[inode, :]) vrel = np.sqrt(rotation_velocity**2 * r**2 + wsp**2) last_length = vrel * dt / node_chord[inode] last_length = np.minimum(last_length, 0.5) if last_length <= 0.5: ratio = gc.get_factor_geometric_progression( last_length, 1., chord_panels) udmd_by_nodes[inode, -1] = 1. udmd_by_nodes[inode, 0] = 0. for im in range(chord_panels[0] - 1, 0, -1): udmd_by_nodes[inode, im] = udmd_by_nodes[inode, im + 1] - last_length last_length *= ratio # Check if (np.diff(udmd_by_nodes[inode, :]) < 0.).any(): sys.error( "ERROR in the panel discretization of the blade in node %d" % (inode)) else: raise RuntimeError( ("ERROR: cannot match the last panel size for node: %d" % inode)) udmd_by_nodes[inode, :] = np.linspace(0, 1, chord_panels + 1) else: udmd_by_nodes = None node_twist = np.zeros_like(node_chord) if camber_effect_on_twist: cout.cout_wrap( "WARNING: The steady applied Mx should be manually multiplied by the density", 3) for inode in range(blade.StructuralInformation.num_node): node_twist[inode] = gc.get_aoacl0_from_camber( airfoils[inode, :, 0], airfoils[inode, :, 1]) mu0 = gc.get_mu0_from_camber(airfoils[inode, :, 0], airfoils[inode, :, 1]) r = np.linalg.norm( blade.StructuralInformation.coordinates[inode, :]) vrel = np.sqrt(rotation_velocity**2 * r**2 + wsp**2) if inode == 0: dr = 0.5 * np.linalg.norm( blade.StructuralInformation.coordinates[1, :] - blade.StructuralInformation.coordinates[0, :]) elif inode == len(blade.StructuralInformation.coordinates[:, 0]) - 1: dr = 0.5 * np.linalg.norm( blade.StructuralInformation.coordinates[-1, :] - blade.StructuralInformation.coordinates[-2, :]) else: dr = 0.5 * np.linalg.norm( blade.StructuralInformation.coordinates[inode + 1, :] - blade.StructuralInformation.coordinates[inode - 1, :]) moment_factor = 0.5 * vrel**2 * node_chord[inode]**2 * dr # print("node", inode, "mu0", mu0, "CMc/4", 2.*mu0 + np.pi/2*node_twist[inode]) blade.StructuralInformation.app_forces[ inode, 3] = (2. * mu0 + np.pi / 2 * node_twist[inode]) * moment_factor airfoils[inode, :, 1] *= 0. # Write SHARPy format blade.AerodynamicInformation.create_aerodynamics_from_vec( blade.StructuralInformation, aero_node, node_chord, node_twist, np.pi * np.ones_like(node_chord), chord_panels, surface_distribution, m_distribution, node_ElAxisAftLEc, airfoil_distribution, airfoils, udmd_by_nodes) # Read the polars of the pure airfoils if include_polars: pure_polars = [None] * n_pure_airfoils for iairfoil in range(n_pure_airfoils): excel_sheet_polar = pure_airfoils_names[iairfoil] aoa = gc.read_column_sheet_type01(excel_file_name, excel_sheet_polar, 'AoA') cl = gc.read_column_sheet_type01(excel_file_name, excel_sheet_polar, 'CL') cd = gc.read_column_sheet_type01(excel_file_name, excel_sheet_polar, 'CD') cm = gc.read_column_sheet_type01(excel_file_name, excel_sheet_polar, 'CM') polar = ap.polar() polar.initialise(np.column_stack((aoa, cl, cd, cm))) pure_polars[iairfoil] = polar # Generate the polars for each airfoil blade.AerodynamicInformation.polars = [ None ] * blade.StructuralInformation.num_node for inode in range(blade.StructuralInformation.num_node): # Find the airfoils between which the node is; ipure = 0 while pure_airfoils_thickness[ipure] > node_thickness[inode]: ipure += 1 if (ipure == n_pure_airfoils): ipure -= 1 break coef = (node_thickness[inode] - pure_airfoils_thickness[ipure - 1] ) / (pure_airfoils_thickness[ipure] - pure_airfoils_thickness[ipure - 1]) polar = ap.interpolate(pure_polars[ipure - 1], pure_polars[ipure], coef) blade.AerodynamicInformation.polars[inode] = polar.table ###################################################################### ## ROTOR ###################################################################### # Read from excel file numberOfBlades = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'NumBl') tilt = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'ShftTilt') * deg2rad cone = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'Cone') * deg2rad # pitch = gc.read_column_sheet_type01(excel_file_name, excel_sheet_rotor, 'Pitch')*deg2rad # Apply pitch blade.StructuralInformation.rotate_around_origin(np.array([1., 0., 0.]), -pitch_deg * deg2rad) # Apply coning blade.StructuralInformation.rotate_around_origin(np.array([0., 1., 0.]), -cone) # Build the whole rotor rotor = blade.copy() for iblade in range(numberOfBlades - 1): blade2 = blade.copy() blade2.StructuralInformation.rotate_around_origin( np.array([0., 0., 1.]), (iblade + 1) * (360.0 / numberOfBlades) * deg2rad) rotor.assembly(blade2) blade2 = None rotor.remove_duplicated_points(tol_remove_points) # Apply tilt rotor.StructuralInformation.rotate_around_origin(np.array([0., 1., 0.]), tilt) return rotor
def generate_from_excel_type02( chord_panels, rotation_velocity, pitch_deg, excel_file_name='database_excel_type02.xlsx', excel_sheet_parameters='parameters', excel_sheet_structural_blade='structural_blade', excel_sheet_discretization_blade='discretization_blade', excel_sheet_aero_blade='aero_blade', excel_sheet_airfoil_info='airfoil_info', excel_sheet_airfoil_coord='airfoil_coord', excel_sheet_structural_tower='structural_tower', m_distribution='uniform', h5_cross_sec_prop=None, n_points_camber=100, tol_remove_points=1e-3, user_defined_m_distribution_type=None, wsp=0., dt=0.): """ generate_from_excel_type02 Function needed to generate a wind turbine from an excel database according to OpenFAST inputs Args: chord_panels (int): Number of panels on the blade surface in the chord direction rotation_velocity (float): Rotation velocity of the rotor pitch_deg (float): pitch angle in degrees excel_file_name (str): excel_sheet_structural_blade (str): excel_sheet_aero_blade (str): excel_sheet_airfoil_coord (str): excel_sheet_parameters (str): excel_sheet_structural_tower (str): m_distribution (str): n_points_camber (int): number of points to define the camber of the airfoil, tol_remove_points (float): maximum distance to remove adjacent points user_defined_m_distribution_type (string): type of distribution of the chordwise panels when 'm_distribution' == 'user_defined' camber_effects_on_twist (bool): When true plain airfoils are used and the blade is twisted and preloaded based on thin airfoil theory wsp (float): wind speed (It may be needed for discretisation purposes) dt (float): time step (It may be needed for discretisation purposes) Returns: wt (sharpy.utils.generate_cases.AeroelasticInfromation): Aeroelastic infrmation of the wind turbine LC (list): list of all the Lagrange constraints needed in the cases (sharpy.utils.generate_cases.LagrangeConstraint) MB (list): list of the multibody information of each body (sharpy.utils.generate_cases.BodyInfrmation) """ rotor = rotor_from_excel_type02( chord_panels, rotation_velocity, pitch_deg, excel_file_name=excel_file_name, excel_sheet_parameters=excel_sheet_parameters, excel_sheet_structural_blade=excel_sheet_structural_blade, excel_sheet_discretization_blade=excel_sheet_discretization_blade, excel_sheet_aero_blade=excel_sheet_aero_blade, excel_sheet_airfoil_info=excel_sheet_airfoil_info, excel_sheet_airfoil_coord=excel_sheet_airfoil_coord, m_distribution=m_distribution, h5_cross_sec_prop=h5_cross_sec_prop, n_points_camber=n_points_camber, tol_remove_points=tol_remove_points, user_defined_m_distribution_type=user_defined_m_distribution_type, wsp=0., dt=0.) ###################################################################### ## TOWER ###################################################################### # Read from excel file HtFract = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'HtFract') TMassDen = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TMassDen') TwFAStif = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TwFAStif') TwSSStif = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TwSSStif') # TODO> variables to be defined TwGJStif = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TwGJStif') TwEAStif = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TwEAStif') TwFAIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TwFAIner') TwSSIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TwSSIner') TwFAcgOf = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TwFAcgOf') TwSScgOf = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_tower, 'TwSScgOf') # Define the TOWER TowerHt = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'TowerHt') Elevation = TowerHt * HtFract tower = gc.AeroelasticInformation() tower.StructuralInformation.num_elem = len(Elevation) - 2 tower.StructuralInformation.num_node_elem = 3 tower.StructuralInformation.compute_basic_num_node() # Interpolate excel variables into the correct locations node_r, elem_r = create_node_radial_pos_from_elem_centres( Elevation, tower.StructuralInformation.num_node, tower.StructuralInformation.num_elem, tower.StructuralInformation.num_node_elem) # Stiffness elem_EA = np.interp(elem_r, Elevation, TwEAStif) elem_EIz = np.interp(elem_r, Elevation, TwSSStif) elem_EIy = np.interp(elem_r, Elevation, TwFAStif) elem_GJ = np.interp(elem_r, Elevation, TwGJStif) # Stiffness: estimate unknown properties cout.cout_wrap.print_file = False cout.cout_wrap('WARNING: The poisson cofficient is assumed equal to 0.3', 3) cout.cout_wrap('WARNING: Cross-section area is used as shear area', 3) poisson_coef = 0.3 elem_GAy = elem_EA / 2.0 / (1.0 + poisson_coef) elem_GAz = elem_EA / 2.0 / (1.0 + poisson_coef) # Inertia elem_mass_per_unit_length = np.interp(elem_r, Elevation, TMassDen) elem_mass_iner_y = np.interp(elem_r, Elevation, TwFAIner) elem_mass_iner_z = np.interp(elem_r, Elevation, TwSSIner) # TODO: check yz axis and Flap-edge elem_pos_cg_B = np.zeros((tower.StructuralInformation.num_elem, 3), ) elem_pos_cg_B[:, 1] = np.interp(elem_r, Elevation, TwSScgOf) elem_pos_cg_B[:, 2] = np.interp(elem_r, Elevation, TwFAcgOf) # Stiffness: estimate unknown properties cout.cout_wrap( 'WARNING: Using perpendicular axis theorem to compute the inertia around xB', 3) elem_mass_iner_x = elem_mass_iner_y + elem_mass_iner_z # Create the tower tower.StructuralInformation.create_mass_db_from_vector( elem_mass_per_unit_length, elem_mass_iner_x, elem_mass_iner_y, elem_mass_iner_z, elem_pos_cg_B) tower.StructuralInformation.create_stiff_db_from_vector( elem_EA, elem_GAy, elem_GAz, elem_GJ, elem_EIy, elem_EIz) coordinates = np.zeros((tower.StructuralInformation.num_node, 3), ) coordinates[:, 0] = node_r tower.StructuralInformation.generate_1to1_from_vectors( num_node_elem=tower.StructuralInformation.num_node_elem, num_node=tower.StructuralInformation.num_node, num_elem=tower.StructuralInformation.num_elem, coordinates=coordinates, stiffness_db=tower.StructuralInformation.stiffness_db, mass_db=tower.StructuralInformation.mass_db, frame_of_reference_delta='y_AFoR', vec_node_structural_twist=np.zeros( (tower.StructuralInformation.num_node, ), ), num_lumped_mass=1) tower.StructuralInformation.boundary_conditions = np.zeros( (tower.StructuralInformation.num_node), dtype=int) tower.StructuralInformation.boundary_conditions[0] = 1 # Read overhang and nacelle properties from excel file overhang_len = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'overhang') # HubMass = gc.read_column_sheet_type01(excel_file_name, excel_sheet_nacelle, 'HubMass') NacelleMass = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'NacMass') # NacelleYawIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_nacelle, 'NacelleYawIner') # Include nacelle mass tower.StructuralInformation.lumped_mass_nodes = np.array( [tower.StructuralInformation.num_node - 1], dtype=int) tower.StructuralInformation.lumped_mass = np.array([NacelleMass], dtype=float) tower.AerodynamicInformation.set_to_zero( tower.StructuralInformation.num_node_elem, tower.StructuralInformation.num_node, tower.StructuralInformation.num_elem) # Assembly overhang with the tower # numberOfBlades = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'NumBl') tilt = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'ShftTilt') * deg2rad # cone = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'Cone')*deg2rad overhang = gc.AeroelasticInformation() overhang.StructuralInformation.num_node = 3 overhang.StructuralInformation.num_node_elem = 3 overhang.StructuralInformation.compute_basic_num_elem() node_pos = np.zeros((overhang.StructuralInformation.num_node, 3), ) node_pos[:, 0] += tower.StructuralInformation.coordinates[-1, 0] node_pos[:, 0] += np.linspace(0., overhang_len * np.sin(tilt * deg2rad), overhang.StructuralInformation.num_node) node_pos[:, 2] = np.linspace(0., -overhang_len * np.cos(tilt * deg2rad), overhang.StructuralInformation.num_node) # TODO: change the following by real values # Same properties as the last element of the tower cout.cout_wrap( "WARNING: Using the structural properties of the last tower section for the overhang", 3) oh_mass_per_unit_length = tower.StructuralInformation.mass_db[-1, 0, 0] oh_mass_iner = tower.StructuralInformation.mass_db[-1, 3, 3] oh_EA = tower.StructuralInformation.stiffness_db[-1, 0, 0] oh_GA = tower.StructuralInformation.stiffness_db[-1, 1, 1] oh_GJ = tower.StructuralInformation.stiffness_db[-1, 3, 3] oh_EI = tower.StructuralInformation.stiffness_db[-1, 4, 4] overhang.StructuralInformation.generate_uniform_sym_beam( node_pos, oh_mass_per_unit_length, oh_mass_iner, oh_EA, oh_GA, oh_GJ, oh_EI, num_node_elem=3, y_BFoR='y_AFoR', num_lumped_mass=0) overhang.StructuralInformation.boundary_conditions = np.zeros( (overhang.StructuralInformation.num_node), dtype=int) overhang.StructuralInformation.boundary_conditions[-1] = -1 overhang.AerodynamicInformation.set_to_zero( overhang.StructuralInformation.num_node_elem, overhang.StructuralInformation.num_node, overhang.StructuralInformation.num_elem) tower.assembly(overhang) tower.remove_duplicated_points(tol_remove_points) ###################################################################### ## WIND TURBINE ###################################################################### # Assembly the whole case wt = tower.copy() hub_position = tower.StructuralInformation.coordinates[-1, :] rotor.StructuralInformation.coordinates += hub_position wt.assembly(rotor) # Redefine the body numbers wt.StructuralInformation.body_number *= 0 wt.StructuralInformation.body_number[tower.StructuralInformation. num_elem:wt.StructuralInformation. num_elem] += 1 ###################################################################### ## MULTIBODY ###################################################################### # Define the boundary condition between the rotor and the tower tip LC1 = gc.LagrangeConstraint() LC1.behaviour = 'hinge_node_FoR_constant_vel' LC1.node_in_body = tower.StructuralInformation.num_node - 1 LC1.body = 0 LC1.body_FoR = 1 LC1.rot_axisB = np.array([1., 0., 0.0]) LC1.rot_vel = -rotation_velocity LC = [] LC.append(LC1) # Define the multibody infromation for the tower and the rotor MB1 = gc.BodyInformation() MB1.body_number = 0 MB1.FoR_position = np.zeros((6, ), ) MB1.FoR_velocity = np.zeros((6, ), ) MB1.FoR_acceleration = np.zeros((6, ), ) MB1.FoR_movement = 'prescribed' MB1.quat = np.array([1.0, 0.0, 0.0, 0.0]) MB2 = gc.BodyInformation() MB2.body_number = 1 MB2.FoR_position = np.array([ rotor.StructuralInformation.coordinates[0, 0], rotor.StructuralInformation.coordinates[0, 1], rotor.StructuralInformation.coordinates[0, 2], 0.0, 0.0, 0.0 ]) MB2.FoR_velocity = np.array([0., 0., 0., 0., 0., rotation_velocity]) MB2.FoR_acceleration = np.zeros((6, ), ) MB2.FoR_movement = 'free' MB2.quat = algebra.euler2quat(np.array([0.0, tilt, 0.0])) MB = [] MB.append(MB1) MB.append(MB2) ###################################################################### ## RETURN ###################################################################### return wt, LC, MB
GA = 1e9 EI = 0.15 tip_force = 0.0 * np.array([0.0, 0.0, 1.0, 0.0, 0.0, 0.0]) tip_mass = 10. theta_ini = 0. * deg2rad euler = np.array([0, theta_ini, 0]) # Useless aero information m = 1 mstar = 1 m_distribution = 'uniform' airfoil = np.zeros((1, 20, 2), ) airfoil[0, :, 0] = np.linspace(0., 1., 20) # Create the structure beam1 = gc.AeroelasticInformation() node_pos = np.zeros((nnodes, 3), ) # node_pos[:, 0] = np.linspace(0.0, length, nnodes) r = np.linspace(0.0, length, nnodes) node_pos[:, 0] = -r * np.sin(theta_ini) node_pos[:, 2] = -r * np.cos(theta_ini) beam1.StructuralInformation.generate_uniform_sym_beam(node_pos, mass_per_unit_length, mass_iner, EA, GA, GJ, EI, num_node_elem=3, y_BFoR='y_AFoR', num_lumped_mass=1)
def setUp(self): nodes_per_elem = 3 # beam1: uniform and symmetric with aerodynamic properties equal to zero nnodes1 = 11 length1 = 10. mass_per_unit_length = 0.1 mass_iner = 1e-4 EA = 1e9 GA = 1e9 GJ = 1e3 EI = 1e4 # Create beam1 beam1 = gc.AeroelasticInformation() # Structural information beam1.StructuralInformation.num_node = nnodes1 beam1.StructuralInformation.num_node_elem = nodes_per_elem beam1.StructuralInformation.compute_basic_num_elem() beam1.StructuralInformation.set_to_zero(beam1.StructuralInformation.num_node_elem, beam1.StructuralInformation.num_node, beam1.StructuralInformation.num_elem) node_pos = np.zeros((nnodes1, 3), ) node_pos[:, 0] = np.linspace(0.0, length1, nnodes1) beam1.StructuralInformation.generate_uniform_sym_beam(node_pos, mass_per_unit_length, mass_iner, EA, GA, GJ, EI, num_node_elem = 3, y_BFoR = 'y_AFoR', num_lumped_mass=2) beam1.StructuralInformation.boundary_conditions[0] = 1 beam1.StructuralInformation.boundary_conditions[-1] = -1 beam1.StructuralInformation.lumped_mass_nodes = np.array([0, nnodes1-1], dtype=int) beam1.StructuralInformation.lumped_mass = np.array([2., 1.]) beam1.StructuralInformation.lumped_mass_inertia = np.zeros((2, 3, 3),) beam1.StructuralInformation.lumped_mass_position = np.zeros((2, 3),) # Aerodynamic information airfoil = np.zeros((1, 20, 2),) airfoil[0, :, 0] = np.linspace(0., 1., 20) beam1.AerodynamicInformation.create_one_uniform_aerodynamics( beam1.StructuralInformation, chord=1., twist=0., sweep=0., num_chord_panels=4, m_distribution='uniform', elastic_axis=0.5, num_points_camber=20, airfoil=airfoil) # SOLVER CONFIGURATION SimInfo = gc.SimulationInformation() SimInfo.set_default_values() SimInfo.define_uinf(np.array([0.0,1.0,0.0]), 10.) SimInfo.solvers['SHARPy']['flow'] = ['BeamLoader', 'AerogridLoader', 'StaticCoupled', 'DynamicCoupled'] self.name = 'fix_node_velocity_wrtG' SimInfo.solvers['SHARPy']['case'] = self.name SimInfo.solvers['SHARPy']['write_screen'] = 'off' SimInfo.solvers['SHARPy']['route'] = folder + '/' SimInfo.set_variable_all_dicts('dt', 0.1) SimInfo.set_variable_all_dicts('rho', 0.0) SimInfo.set_variable_all_dicts('velocity_field_input', SimInfo.solvers['SteadyVelocityField']) SimInfo.set_variable_all_dicts('folder', folder + '/output/') SimInfo.solvers['BeamLoader']['unsteady'] = 'on' SimInfo.solvers['AerogridLoader']['unsteady'] = 'on' SimInfo.solvers['AerogridLoader']['mstar'] = 2 SimInfo.solvers['AerogridLoader']['wake_shape_generator'] = 'StraightWake' SimInfo.solvers['AerogridLoader']['wake_shape_generator_input'] = {'u_inf':10., 'u_inf_direction': np.array([0., 1., 0.]), 'dt': 0.1} SimInfo.solvers['NonLinearStatic']['print_info'] = False SimInfo.solvers['StaticCoupled']['structural_solver'] = 'NonLinearStatic' SimInfo.solvers['StaticCoupled']['structural_solver_settings'] = SimInfo.solvers['NonLinearStatic'] SimInfo.solvers['StaticCoupled']['aero_solver'] = 'StaticUvlm' SimInfo.solvers['StaticCoupled']['aero_solver_settings'] = SimInfo.solvers['StaticUvlm'] SimInfo.solvers['WriteVariablesTime']['structure_nodes'] = np.array([0, int((nnodes1-1)/2), -1], dtype = int) SimInfo.solvers['WriteVariablesTime']['structure_variables'] = ['pos'] SimInfo.solvers['BeamPlot']['include_FoR'] = True SimInfo.solvers['NonLinearDynamicMultibody']['relaxation_factor'] = 0.0 SimInfo.solvers['NonLinearDynamicMultibody']['min_delta'] = 1e-5 SimInfo.solvers['NonLinearDynamicMultibody']['max_iterations'] = 200 SimInfo.solvers['NonLinearDynamicMultibody']['newmark_damp'] = 1e-3 # SimInfo.solvers['NonLinearDynamicMultibody']['gravity_on'] = 'off' SimInfo.solvers['NonLinearDynamicMultibody']['relaxation_factor'] = 0.0 SimInfo.solvers['DynamicCoupled']['structural_solver'] = 'NonLinearDynamicMultibody' SimInfo.solvers['DynamicCoupled']['structural_solver_settings'] = SimInfo.solvers['NonLinearDynamicMultibody'] SimInfo.solvers['DynamicCoupled']['aero_solver'] = 'StepUvlm' SimInfo.solvers['DynamicCoupled']['aero_solver_settings'] = SimInfo.solvers['StepUvlm'] SimInfo.solvers['DynamicCoupled']['postprocessors'] = ['WriteVariablesTime', 'BeamPlot', 'AerogridPlot'] SimInfo.solvers['DynamicCoupled']['postprocessors_settings'] = {'WriteVariablesTime': SimInfo.solvers['WriteVariablesTime'], 'BeamPlot': SimInfo.solvers['BeamPlot'], 'AerogridPlot': SimInfo.solvers['AerogridPlot']} ntimesteps = 10 SimInfo.define_num_steps(ntimesteps) # Define dynamic simulation SimInfo.with_forced_vel = False SimInfo.with_dynamic_forces = False LC2 = gc.LagrangeConstraint() LC2.behaviour = 'lin_vel_node_wrtG' LC2.velocity = np.zeros((ntimesteps, 3)) LC2.velocity[:int(ntimesteps/2),1] = 0.5 LC2.velocity[int(ntimesteps/2):,1] = -0.5 LC2.body_number = 0 LC2.node_number = int((nnodes1-1)/2) LC = [] # LC.append(LC1) LC.append(LC2) # Define the multibody infromation for the tower and the rotor MB1 = gc.BodyInformation() MB1.body_number = 0 MB1.FoR_position = np.zeros((6,),) MB1.FoR_velocity = np.zeros((6,),) MB1.FoR_acceleration = np.zeros((6,),) MB1.FoR_movement = 'free' MB1.quat = np.array([1.0,0.0,0.0,0.0]) MB = [] MB.append(MB1) gc.clean_test_files( SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case']) SimInfo.generate_solver_file() SimInfo.generate_dyn_file(ntimesteps) beam1.generate_h5_files( SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case']) gc.generate_multibody_file(LC, MB,SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case'])
def setUp(self): import sharpy.utils.generate_cases as gc deg2rad = np.pi/180. # Structural properties mass_per_unit_length = 1. mass_iner = 1e-4 EA = 1e9 GA = 1e9 GJ = 1e9 EI = 1e9 # Beam1 global nnodes1 nnodes1 = 11 l1 = 1.0 m1 = 1.0 theta_ini1 = 90.*deg2rad # Beam2 nnodes2 = nnodes1 l2 = l1 m2 = m1 theta_ini2 = 00.*deg2rad # airfoils airfoil = np.zeros((1,20,2),) airfoil[0,:,0] = np.linspace(0.,1.,20) # Simulation numtimesteps = 10 dt = 0.01 # Create the structure beam1 = gc.AeroelasticInformation() r1 = np.linspace(0.0, l1, nnodes1) node_pos1 = np.zeros((nnodes1,3),) node_pos1[:, 0] = r1*np.sin(theta_ini1) node_pos1[:, 2] = -r1*np.cos(theta_ini1) beam1.StructuralInformation.generate_uniform_sym_beam(node_pos1, mass_per_unit_length, mass_iner, EA, GA, GJ, EI, num_node_elem = 3, y_BFoR = 'y_AFoR', num_lumped_mass=1) beam1.StructuralInformation.body_number = np.zeros((beam1.StructuralInformation.num_elem,), dtype = int) beam1.StructuralInformation.boundary_conditions[0] = 1 beam1.StructuralInformation.boundary_conditions[-1] = -1 beam1.StructuralInformation.lumped_mass_nodes = np.array([nnodes1-1], dtype = int) beam1.StructuralInformation.lumped_mass = np.ones((1,))*m1 beam1.StructuralInformation.lumped_mass_inertia = np.zeros((1,3,3)) beam1.StructuralInformation.lumped_mass_position = np.zeros((1,3)) beam1.AerodynamicInformation.create_one_uniform_aerodynamics( beam1.StructuralInformation, chord = 1., twist = 0., sweep = 0., num_chord_panels = 4, m_distribution = 'uniform', elastic_axis = 0.25, num_points_camber = 20, airfoil = airfoil) beam2 = gc.AeroelasticInformation() r2 = np.linspace(0.0, l2, nnodes2) node_pos2 = np.zeros((nnodes2,3),) node_pos2[:, 0] = r2*np.sin(theta_ini2) + node_pos1[-1, 0] node_pos2[:, 2] = -r2*np.cos(theta_ini2) + node_pos1[-1, 2] beam2.StructuralInformation.generate_uniform_sym_beam(node_pos2, mass_per_unit_length, mass_iner, EA, GA, GJ, EI, num_node_elem = 3, y_BFoR = 'y_AFoR', num_lumped_mass=1) beam2.StructuralInformation.body_number = np.zeros((beam1.StructuralInformation.num_elem,), dtype = int) beam2.StructuralInformation.boundary_conditions[0] = 1 beam2.StructuralInformation.boundary_conditions[-1] = -1 beam2.StructuralInformation.lumped_mass_nodes = np.array([nnodes2-1], dtype = int) beam2.StructuralInformation.lumped_mass = np.ones((1,))*m2 beam2.StructuralInformation.lumped_mass_inertia = np.zeros((1,3,3)) beam2.StructuralInformation.lumped_mass_position = np.zeros((1,3)) beam2.AerodynamicInformation.create_one_uniform_aerodynamics( beam2.StructuralInformation, chord = 1., twist = 0., sweep = 0., num_chord_panels = 4, m_distribution = 'uniform', elastic_axis = 0.25, num_points_camber = 20, airfoil = airfoil) beam1.assembly(beam2) # Simulation details SimInfo = gc.SimulationInformation() SimInfo.set_default_values() SimInfo.define_uinf(np.array([0.0,1.0,0.0]), 1.) SimInfo.solvers['SHARPy']['flow'] = ['BeamLoader', 'AerogridLoader', # 'InitializeMultibody', 'DynamicCoupled'] global name name = 'double_pendulum_geradin' SimInfo.solvers['SHARPy']['case'] = 'double_pendulum_geradin' SimInfo.solvers['SHARPy']['write_screen'] = 'off' SimInfo.solvers['SHARPy']['route'] = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + '/' SimInfo.solvers['SHARPy']['log_folder'] = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + '/' SimInfo.set_variable_all_dicts('dt', dt) SimInfo.define_num_steps(numtimesteps) SimInfo.set_variable_all_dicts('rho', 0.0) SimInfo.set_variable_all_dicts('velocity_field_input', SimInfo.solvers['SteadyVelocityField']) SimInfo.set_variable_all_dicts('output', os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + '/output/') SimInfo.solvers['BeamLoader']['unsteady'] = 'on' SimInfo.solvers['AerogridLoader']['unsteady'] = 'on' SimInfo.solvers['AerogridLoader']['mstar'] = 2 SimInfo.solvers['WriteVariablesTime']['FoR_number'] = np.array([0, 1], dtype = int) SimInfo.solvers['WriteVariablesTime']['FoR_variables'] = ['mb_quat'] SimInfo.solvers['WriteVariablesTime']['structure_nodes'] = np.array([nnodes1-1, nnodes1+nnodes2-1], dtype = int) SimInfo.solvers['WriteVariablesTime']['structure_variables'] = ['pos'] SimInfo.solvers['NonLinearDynamicMultibody']['gravity_on'] = True SimInfo.solvers['NonLinearDynamicMultibody']['newmark_damp'] = 0.15 SimInfo.solvers['BeamPlot']['include_FoR'] = True SimInfo.solvers['DynamicCoupled']['structural_solver'] = 'NonLinearDynamicMultibody' SimInfo.solvers['DynamicCoupled']['structural_solver_settings'] = SimInfo.solvers['NonLinearDynamicMultibody'] SimInfo.solvers['DynamicCoupled']['aero_solver'] = 'StepUvlm' SimInfo.solvers['DynamicCoupled']['aero_solver_settings'] = SimInfo.solvers['StepUvlm'] SimInfo.solvers['DynamicCoupled']['postprocessors'] = ['WriteVariablesTime', 'BeamPlot', 'AerogridPlot'] SimInfo.solvers['DynamicCoupled']['postprocessors_settings'] = {'WriteVariablesTime': SimInfo.solvers['WriteVariablesTime'], 'BeamPlot': SimInfo.solvers['BeamPlot'], 'AerogridPlot': SimInfo.solvers['AerogridPlot']} SimInfo.solvers['DynamicCoupled']['postprocessors_settings']['WriteVariablesTime']['folder'] = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + '/output/' SimInfo.solvers['DynamicCoupled']['postprocessors_settings']['BeamPlot']['folder'] = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + '/output/' SimInfo.solvers['DynamicCoupled']['postprocessors_settings']['AerogridPlot']['folder'] = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + '/output/' SimInfo.with_forced_vel = False SimInfo.with_dynamic_forces = False # Create the MB and BC files LC1 = gc.LagrangeConstraint() LC1.behaviour = 'hinge_FoR' LC1.body_FoR = 0 LC1.rot_axis_AFoR = np.array([0.0,1.0,0.0]) LC2 = gc.LagrangeConstraint() LC2.behaviour = 'hinge_node_FoR' LC2.node_in_body = nnodes1-1 LC2.body = 0 LC2.body_FoR = 1 LC2.rot_axisB = np.array([0.0,1.0,0.0]) LC = [] LC.append(LC1) LC.append(LC2) MB1 = gc.BodyInformation() MB1.body_number = 0 MB1.FoR_position = np.zeros((6,),) MB1.FoR_velocity = np.zeros((6,),) MB1.FoR_acceleration = np.zeros((6,),) MB1.FoR_movement = 'free' MB1.quat = np.array([1.0,0.0,0.0,0.0]) MB2 = gc.BodyInformation() MB2.body_number = 1 MB2.FoR_position = np.array([node_pos2[0, 0], node_pos2[0, 1], node_pos2[0, 2], 0.0, 0.0, 0.0]) MB2.FoR_velocity = np.zeros((6,),) MB2.FoR_acceleration = np.zeros((6,),) MB2.FoR_movement = 'free' MB2.quat = np.array([1.0,0.0,0.0,0.0]) MB = [] MB.append(MB1) MB.append(MB2) # Write files gc.clean_test_files(SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case']) SimInfo.generate_solver_file() SimInfo.generate_dyn_file(numtimesteps) beam1.generate_h5_files(SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case']) gc.generate_multibody_file(LC, MB,SimInfo.solvers['SHARPy']['route'], SimInfo.solvers['SHARPy']['case'])
def rotor_from_excel_type02( chord_panels, rotation_velocity, pitch_deg, excel_file_name='database_excel_type02.xlsx', excel_sheet_parameters='parameters', excel_sheet_structural_blade='structural_blade', excel_sheet_discretization_blade='discretization_blade', excel_sheet_aero_blade='aero_blade', excel_sheet_airfoil_info='airfoil_info', excel_sheet_airfoil_coord='airfoil_coord', m_distribution='uniform', h5_cross_sec_prop=None, n_points_camber=100, tol_remove_points=1e-3, user_defined_m_distribution_type=None, camber_effect_on_twist=False, wsp=0., dt=0.): """ generate_from_excel_type02_db Function needed to generate a wind turbine from an excel database type02 Args: chord_panels (int): Number of panels on the blade surface in the chord direction rotation_velocity (float): Rotation velocity of the rotor pitch_deg (float): pitch angle in degrees excel_file_name (str): excel_sheet_structural_blade (str): excel_sheet_discretization_blade (str): excel_sheet_aero_blade (str): excel_sheet_airfoil_info (str): excel_sheet_airfoil_coord (str): excel_sheet_parameters (str): h5_cross_sec_prop (str): h5 containing mass and stiffness matrices along the blade. m_distribution (str): n_points_camber (int): number of points to define the camber of the airfoil, tol_remove_points (float): maximum distance to remove adjacent points user_defined_m_distribution_type (string): type of distribution of the chordwise panels when 'm_distribution' == 'user_defined' camber_effects_on_twist (bool): When true plain airfoils are used and the blade is twisted and preloaded based on thin airfoil theory wsp (float): wind speed (It may be needed for discretisation purposes) dt (float): time step (It may be needed for discretisation purposes) Returns: rotor (sharpy.utils.generate_cases.AeroelasticInfromation): Aeroelastic information of the rotor Note: - h5_cross_sec_prop is a path to a h5 containing the following groups: - str_prop: with: - K: list of 6x6 stiffness matrices - M: list of 6x6 mass matrices - radius: radial location (including hub) of K and M matrices - when h5_cross_sec_prop is not None, mass and stiffness properties are interpolated at BlFract location specified in "excel_sheet_structural_blade" """ ###################################################################### ## BLADE ###################################################################### blade = gc.AeroelasticInformation() ###################################################################### ### STRUCTURE ###################################################################### # Read blade structural information from excel file rR_structural = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'rR') OutPElAxis = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'OutPElAxis') InPElAxis = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'InPElAxis') ElAxisAftLEc = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'ElAxisAftLEc') StrcTwst = gc.read_column_sheet_type01( excel_file_name, excel_sheet_structural_blade, 'StrcTwst') * deg2rad BMassDen = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'BMassDen') FlpStff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'FlpStff') EdgStff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'EdgStff') FlapEdgeStiff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'FlapEdgeStiff') GJStff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'GJStff') EAStff = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'EAStff') FlpIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'FlpIner') EdgIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'EdgIner') FlapEdgeIner = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'FlapEdgeIner') PrebendRef = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'PrebendRef') PreswpRef = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'PreswpRef') OutPcg = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'OutPcg') InPcg = gc.read_column_sheet_type01(excel_file_name, excel_sheet_structural_blade, 'InPcg') # Blade parameters TipRad = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'TipRad') # HubRad = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'HubRad') # Discretization points rR = gc.read_column_sheet_type01(excel_file_name, excel_sheet_discretization_blade, 'rR') # Interpolate excel variables into the correct locations # Geometry if rR[0] < rR_structural[0]: rR_structural = np.concatenate((np.array([0.]), rR_structural), ) OutPElAxis = np.concatenate((np.array([OutPElAxis[0]]), OutPElAxis), ) InPElAxis = np.concatenate((np.array([InPElAxis[0]]), InPElAxis), ) ElAxisAftLEc = np.concatenate( (np.array([ElAxisAftLEc[0]]), ElAxisAftLEc), ) StrcTwst = np.concatenate((np.array([StrcTwst[0]]), StrcTwst), ) BMassDen = np.concatenate((np.array([BMassDen[0]]), BMassDen), ) FlpStff = np.concatenate((np.array([FlpStff[0]]), FlpStff), ) EdgStff = np.concatenate((np.array([EdgStff[0]]), EdgStff), ) FlapEdgeStiff = np.concatenate( (np.array([FlapEdgeStiff[0]]), FlapEdgeStiff), ) GJStff = np.concatenate((np.array([GJStff[0]]), GJStff), ) EAStff = np.concatenate((np.array([EAStff[0]]), EAStff), ) FlpIner = np.concatenate((np.array([FlpIner[0]]), FlpIner), ) EdgIner = np.concatenate((np.array([EdgIner[0]]), EdgIner), ) FlapEdgeIner = np.concatenate( (np.array([FlapEdgeIner[0]]), FlapEdgeIner), ) PrebendRef = np.concatenate((np.array([PrebendRef[0]]), PrebendRef), ) PreswpRef = np.concatenate((np.array([PreswpRef[0]]), PreswpRef), ) OutPcg = np.concatenate((np.array([OutPcg[0]]), OutPcg), ) InPcg = np.concatenate((np.array([InPcg[0]]), InPcg), ) # Base parameters use_excel_struct_as_elem = False if use_excel_struct_as_elem: blade.StructuralInformation.num_node_elem = 3 blade.StructuralInformation.num_elem = len(rR) - 2 blade.StructuralInformation.compute_basic_num_node() node_r, elem_r = create_node_radial_pos_from_elem_centres( rR * TipRad, blade.StructuralInformation.num_node, blade.StructuralInformation.num_elem, blade.StructuralInformation.num_node_elem) else: # Use excel struct as nodes # Check the number of nodes blade.StructuralInformation.num_node_elem = 3 blade.StructuralInformation.num_node = len(rR) if ((len(rR) - 1) % (blade.StructuralInformation.num_node_elem - 1)) == 0: blade.StructuralInformation.num_elem = int( (len(rR) - 1) / (blade.StructuralInformation.num_node_elem - 1)) node_r = rR * TipRad elem_rR = rR[1::2] + 0. elem_r = rR[1::2] * TipRad + 0. else: print("ERROR: Cannot build ", blade.StructuralInformation.num_node_elem, "-noded elements from ", blade.StructuralInformation.num_node, "nodes") node_y = np.interp(rR, rR_structural, InPElAxis) + np.interp( rR, rR_structural, PreswpRef) node_z = -np.interp(rR, rR_structural, OutPElAxis) - np.interp( rR, rR_structural, PrebendRef) node_twist = -1.0 * np.interp(rR, rR_structural, StrcTwst) coordinates = create_blade_coordinates( blade.StructuralInformation.num_node, node_r, node_y, node_z) if h5_cross_sec_prop is None: # Stiffness elem_EA = np.interp(elem_rR, rR_structural, EAStff) elem_EIy = np.interp(elem_rR, rR_structural, FlpStff) elem_EIz = np.interp(elem_rR, rR_structural, EdgStff) elem_EIyz = np.interp(elem_rR, rR_structural, FlapEdgeStiff) elem_GJ = np.interp(elem_rR, rR_structural, GJStff) # Stiffness: estimate unknown properties print('WARNING: The poisson cofficient is assumed equal to 0.3') print('WARNING: Cross-section area is used as shear area') poisson_coef = 0.3 elem_GAy = elem_EA / 2.0 / (1.0 + poisson_coef) elem_GAz = elem_EA / 2.0 / (1.0 + poisson_coef) # Inertia elem_pos_cg_B = np.zeros((blade.StructuralInformation.num_elem, 3), ) elem_pos_cg_B[:, 1] = np.interp(elem_rR, rR_structural, InPcg) elem_pos_cg_B[:, 2] = -np.interp(elem_rR, rR_structural, OutPcg) elem_mass_per_unit_length = np.interp(elem_rR, rR_structural, BMassDen) elem_mass_iner_y = np.interp(elem_rR, rR_structural, FlpIner) elem_mass_iner_z = np.interp(elem_rR, rR_structural, EdgIner) elem_mass_iner_yz = np.interp(elem_rR, rR_structural, FlapEdgeIner) # Inertia: estimate unknown properties print( 'WARNING: Using perpendicular axis theorem to compute the inertia around xB' ) elem_mass_iner_x = elem_mass_iner_y + elem_mass_iner_z # Generate blade structural properties blade.StructuralInformation.create_mass_db_from_vector( elem_mass_per_unit_length, elem_mass_iner_x, elem_mass_iner_y, elem_mass_iner_z, elem_pos_cg_B, elem_mass_iner_yz) blade.StructuralInformation.create_stiff_db_from_vector( elem_EA, elem_GAy, elem_GAz, elem_GJ, elem_EIy, elem_EIz, elem_EIyz) else: # read Mass/Stiffness from database cross_prop = h5.readh5(h5_cross_sec_prop).str_prop # create mass_db/stiffness_db (interpolate at mid-node of each element) blade.StructuralInformation.mass_db = scint.interp1d( cross_prop.radius, cross_prop.M, kind='cubic', copy=False, assume_sorted=True, axis=0, bounds_error=False, fill_value='extrapolate')(node_r[1::2]) blade.StructuralInformation.stiffness_db = scint.interp1d( cross_prop.radius, cross_prop.K, kind='cubic', copy=False, assume_sorted=True, axis=0, bounds_error=False, fill_value='extrapolate')(node_r[1::2]) blade.StructuralInformation.generate_1to1_from_vectors( num_node_elem=blade.StructuralInformation.num_node_elem, num_node=blade.StructuralInformation.num_node, num_elem=blade.StructuralInformation.num_elem, coordinates=coordinates, stiffness_db=blade.StructuralInformation.stiffness_db, mass_db=blade.StructuralInformation.mass_db, frame_of_reference_delta='y_AFoR', vec_node_structural_twist=node_twist, num_lumped_mass=0) # Boundary conditions blade.StructuralInformation.boundary_conditions = np.zeros( (blade.StructuralInformation.num_node), dtype=int) blade.StructuralInformation.boundary_conditions[0] = 1 blade.StructuralInformation.boundary_conditions[-1] = -1 ###################################################################### ### AERODYNAMICS ###################################################################### # Read blade aerodynamic information from excel file rR_aero = gc.read_column_sheet_type01(excel_file_name, excel_sheet_aero_blade, 'rR') chord_aero = gc.read_column_sheet_type01(excel_file_name, excel_sheet_aero_blade, 'BlChord') thickness_aero = gc.read_column_sheet_type01(excel_file_name, excel_sheet_aero_blade, 'BlThickness') pure_airfoils_names = gc.read_column_sheet_type01( excel_file_name, excel_sheet_airfoil_info, 'Name') pure_airfoils_thickness = gc.read_column_sheet_type01( excel_file_name, excel_sheet_airfoil_info, 'Thickness') node_ElAxisAftLEc = np.interp(node_r, rR_structural * TipRad, ElAxisAftLEc) # Read coordinates of the pure airfoils n_pure_airfoils = len(pure_airfoils_names) pure_airfoils_camber = np.zeros((n_pure_airfoils, n_points_camber, 2), ) xls = pd.ExcelFile(excel_file_name) excel_db = pd.read_excel(xls, sheet_name=excel_sheet_airfoil_coord) for iairfoil in range(n_pure_airfoils): # Look for the NaN icoord = 2 while (not (math.isnan( excel_db["%s_x" % pure_airfoils_names[iairfoil]][icoord]))): icoord += 1 if (icoord == len(excel_db["%s_x" % pure_airfoils_names[iairfoil]])): break # Compute the camber of the airfoils at the defined chord points pure_airfoils_camber[iairfoil, :, 0], pure_airfoils_camber[ iairfoil, :, 1] = gc.get_airfoil_camber( excel_db["%s_x" % pure_airfoils_names[iairfoil]][2:icoord], excel_db["%s_y" % pure_airfoils_names[iairfoil]][2:icoord], n_points_camber) # Basic variables surface_distribution = np.zeros((blade.StructuralInformation.num_elem), dtype=int) # Interpolate in the correct positions node_chord = np.interp(node_r, rR_aero * TipRad, chord_aero) # Define the nodes with aerodynamic properties # Look for the first element that is goint to be aerodynamic first_aero_elem = 0 while (elem_r[first_aero_elem] <= rR_aero[0] * TipRad): first_aero_elem += 1 first_aero_node = first_aero_elem * ( blade.StructuralInformation.num_node_elem - 1) aero_node = np.zeros((blade.StructuralInformation.num_node, ), dtype=bool) aero_node[first_aero_node:] = np.ones( (blade.StructuralInformation.num_node - first_aero_node, ), dtype=bool) # Define the airfoil at each stage # airfoils = blade.AerodynamicInformation.interpolate_airfoils_camber(pure_airfoils_camber,excel_aero_r, node_r, n_points_camber) node_thickness = np.interp(node_r, rR_aero * TipRad, thickness_aero) airfoils = blade.AerodynamicInformation.interpolate_airfoils_camber_thickness( pure_airfoils_camber, pure_airfoils_thickness, node_thickness, n_points_camber) airfoil_distribution = np.linspace(0, blade.StructuralInformation.num_node - 1, blade.StructuralInformation.num_node, dtype=int) # User defined m distribution if (m_distribution == 'user_defined') and (user_defined_m_distribution_type == 'last_geometric'): blade_nodes = blade.StructuralInformation.num_node udmd_by_nodes = np.zeros((blade_nodes, chord_panels[0] + 1)) for inode in range(blade_nodes): r = np.linalg.norm( blade.StructuralInformation.coordinates[inode, :]) vrel = np.sqrt(rotation_velocity**2 * r**2 + wsp**2) last_length = vrel * dt / node_chord[inode] last_length = np.minimum(last_length, 0.5) if last_length <= 0.5: ratio = gc.get_factor_geometric_progression( last_length, 1., chord_panels) udmd_by_nodes[inode, -1] = 1. udmd_by_nodes[inode, 0] = 0. for im in range(chord_panels[0] - 1, 0, -1): udmd_by_nodes[inode, im] = udmd_by_nodes[inode, im + 1] - last_length last_length *= ratio # Check if (np.diff(udmd_by_nodes[inode, :]) < 0.).any(): sys.error( "ERROR in the panel discretization of the blade in node %d" % (inode)) else: print("ERROR: cannot match the last panel size for node:", inode) udmd_by_nodes[inode, :] = np.linspace(0, 1, chord_panels + 1) else: udmd_by_nodes = None node_twist = np.zeros_like(node_chord) if camber_effect_on_twist: print( "WARNING: The steady applied Mx should be manually multiplied by the density" ) for inode in range(blade.StructuralInformation.num_node): node_twist[inode] = gc.get_aoacl0_from_camber( airfoils[inode, :, 0], airfoils[inode, :, 1]) mu0 = gc.get_mu0_from_camber(airfoils[inode, :, 0], airfoils[inode, :, 1]) r = np.linalg.norm( blade.StructuralInformation.coordinates[inode, :]) vrel = np.sqrt(rotation_velocity**2 * r**2 + wsp**2) if inode == 0: dr = 0.5 * np.linalg.norm( blade.StructuralInformation.coordinates[1, :] - blade.StructuralInformation.coordinates[0, :]) elif inode == len(blade.StructuralInformation.coordinates[:, 0]) - 1: dr = 0.5 * np.linalg.norm( blade.StructuralInformation.coordinates[-1, :] - blade.StructuralInformation.coordinates[-2, :]) else: dr = 0.5 * np.linalg.norm( blade.StructuralInformation.coordinates[inode + 1, :] - blade.StructuralInformation.coordinates[inode - 1, :]) moment_factor = 0.5 * vrel**2 * node_chord[inode]**2 * dr # print("node", inode, "mu0", mu0, "CMc/4", 2.*mu0 + np.pi/2*node_twist[inode]) blade.StructuralInformation.app_forces[ inode, 3] = (2. * mu0 + np.pi / 2 * node_twist[inode]) * moment_factor airfoils[inode, :, 1] *= 0. # Write SHARPy format blade.AerodynamicInformation.create_aerodynamics_from_vec( blade.StructuralInformation, aero_node, node_chord, node_twist, np.pi * np.ones_like(node_chord), chord_panels, surface_distribution, m_distribution, node_ElAxisAftLEc, airfoil_distribution, airfoils, udmd_by_nodes) ###################################################################### ## ROTOR ###################################################################### # Read from excel file numberOfBlades = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'NumBl') tilt = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'ShftTilt') * deg2rad cone = gc.read_column_sheet_type01(excel_file_name, excel_sheet_parameters, 'Cone') * deg2rad # pitch = gc.read_column_sheet_type01(excel_file_name, excel_sheet_rotor, 'Pitch')*deg2rad # Apply pitch blade.StructuralInformation.rotate_around_origin(np.array([1., 0., 0.]), -pitch_deg * deg2rad) # Apply coning blade.StructuralInformation.rotate_around_origin(np.array([0., 1., 0.]), -cone) # Build the whole rotor rotor = blade.copy() for iblade in range(numberOfBlades - 1): blade2 = blade.copy() blade2.StructuralInformation.rotate_around_origin( np.array([0., 0., 1.]), (iblade + 1) * (360.0 / numberOfBlades) * deg2rad) rotor.assembly(blade2) blade2 = None rotor.remove_duplicated_points(tol_remove_points) # Apply tilt rotor.StructuralInformation.rotate_around_origin(np.array([0., 1., 0.]), tilt) return rotor