def screen_output(self): line = '' cout.cout_wrap.print_separator() # output header if self.settings['coefficients']: line = "{0:5s} | {1:10s} | {2:10s} | {3:10s} | {4:10s} | {5:10s} | {6:10s}".format( 'tstep', ' fx_g', ' fy_g', ' fz_g', ' Cfx_g', ' Cfy_g', ' Cfz_g') cout.cout_wrap(line, 1) for self.ts in range(self.ts_max): fx = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 0], 0) + \ np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 0], 0) fy = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 1], 0) + \ np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 1], 0) fz = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 2], 0) + \ np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 2], 0) Cfx, Cfy, Cfz = self.calculate_coefficients(fx, fy, fz) line = "{0:5d} | {1: 8.3e} | {2: 8.3e} | {3: 8.3e} | {4: 8.3e} | {5: 8.3e} | {6: 8.3e}".format( self.ts, fx, fy, fz, Cfx, Cfy, Cfz) cout.cout_wrap(line, 1) else: line = "{0:5s} | {1:10s} | {2:10s} | {3:10s}".format( 'tstep', ' fx_g', ' fy_g', ' fz_g') cout.cout_wrap(line, 1) for self.ts in range(self.ts_max): fx = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 0], 0) + \ np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 0], 0) fy = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 1], 0) + \ np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 1], 0) fz = np.sum(self.data.aero.timestep_info[self.ts].inertial_steady_forces[:, 2], 0) + \ np.sum(self.data.aero.timestep_info[self.ts].inertial_unsteady_forces[:, 2], 0) line = "{0:5d} | {1: 8.3e} | {2: 8.3e} | {3: 8.3e}".format( self.ts, fx, fy, fz) cout.cout_wrap(line, 1)
def run(self): prescribed_motion = False try: prescribed_motion = self.settings['prescribed_motion'].value except KeyError: pass if prescribed_motion is True: cout.cout_wrap('Running non linear dynamic solver...', 2) # xbeamlib.cbeam3_solv_nlndyn(self.data.beam, self.settings) else: cout.cout_wrap('Running non linear dynamic solver with RB...', 2) xbeamlib.xbeam_solv_couplednlndyn(self.data.structure, self.settings) cout.cout_wrap('...Finished', 2) return self.data
def initialise(self, in_dict): self.in_dict = in_dict settings.to_custom_types(self.in_dict, self.settings_types, self.settings_default, no_ctype=True) self.settings = self.in_dict self.x_grid, self.y_grid, self.z_grid, self.vel = self.read_turbsim_bts( self.settings['turbulent_field'], self.settings['case_with_tower']) if not self.settings['new_orientation'] == 'xyz': # self.settings['new_orientation'] = 'zyx' self.x_grid, self.y_grid, self.z_grid, self.vel = self.change_orientation( self.x_grid, self.y_grid, self.z_grid, self.vel, self.settings['new_orientation']) self.bbox = self.get_field_bbox(self.x_grid, self.y_grid, self.z_grid) if self.settings['print_info']: cout.cout_wrap('The domain bbox is:', 1) cout.cout_wrap( ' x = [' + str(self.bbox[0, 0]) + ', ' + str(self.bbox[0, 1]) + ']', 1) cout.cout_wrap( ' y = [' + str(self.bbox[1, 0]) + ', ' + str(self.bbox[1, 1]) + ']', 1) cout.cout_wrap( ' z = [' + str(self.bbox[2, 0]) + ', ' + str(self.bbox[2, 1]) + ']', 1) self.dist_to_recirculate = 0. self.grid_size_vec = np.array([ np.max(self.x_grid) - np.min(self.x_grid), np.max(self.y_grid) - np.min(self.y_grid), np.max(self.z_grid) - np.min(self.z_grid) ]) self.grid_size_ufed_dir = np.dot( self.grid_size_vec, self.settings['u_fed'] / np.linalg.norm(self.settings['u_fed']))
def run(self): """ Run the time stepping procedure with controllers and postprocessors included. """ # dynamic simulations start at tstep == 1, 0 is reserved for the initial state for self.data.ts in range( len(self.data.structure.timestep_info), self.settings['n_time_steps'].value + len(self.data.structure.timestep_info)): initial_time = time.perf_counter() structural_kstep = self.data.structure.timestep_info[-1].copy() aero_kstep = self.data.aero.timestep_info[-1].copy() # Add the controller here if self.with_controllers: state = {'structural': structural_kstep, 'aero': aero_kstep} for k, v in self.controllers.items(): state = v.control(self.data, state) # this takes care of the changes in options for the solver structural_kstep, aero_kstep = self.process_controller_output( state) self.time_aero = 0.0 self.time_struc = 0.0 # Copy the controlled states so that the interpolation does not # destroy the previous information controlled_structural_kstep = structural_kstep.copy() controlled_aero_kstep = aero_kstep.copy() k = 0 for k in range(self.settings['fsi_substeps'].value + 1): if (k == self.settings['fsi_substeps'].value and self.settings['fsi_substeps']): cout.cout_wrap('The FSI solver did not converge!!!') break # generate new grid (already rotated) aero_kstep = controlled_aero_kstep.copy() self.aero_solver.update_custom_grid(structural_kstep, aero_kstep) # compute unsteady contribution force_coeff = 0.0 unsteady_contribution = False if self.settings['include_unsteady_force_contribution'].value: if self.data.ts > self.settings[ 'steps_without_unsteady_force'].value: unsteady_contribution = True if k < self.settings[ 'pseudosteps_ramp_unsteady_force'].value: force_coeff = k / self.settings[ 'pseudosteps_ramp_unsteady_force'].value else: force_coeff = 1. # run the solver ini_time_aero = time.perf_counter() self.data = self.aero_solver.run( aero_kstep, structural_kstep, convect_wake=True, unsteady_contribution=unsteady_contribution) self.time_aero += time.perf_counter() - ini_time_aero previous_kstep = structural_kstep.copy() structural_kstep = controlled_structural_kstep.copy() # move the aerodynamic surface according the the structural one self.aero_solver.update_custom_grid(structural_kstep, aero_kstep) self.map_forces(aero_kstep, structural_kstep, force_coeff) # relaxation relax_factor = self.relaxation_factor(k) relax(self.data.structure, structural_kstep, previous_kstep, relax_factor) # check if nan anywhere. # if yes, raise exception if np.isnan(structural_kstep.steady_applied_forces).any(): raise exc.NotConvergedSolver( 'NaN found in steady_applied_forces!') if np.isnan(structural_kstep.unsteady_applied_forces).any(): raise exc.NotConvergedSolver( 'NaN found in unsteady_applied_forces!') copy_structural_kstep = structural_kstep.copy() ini_time_struc = time.perf_counter() for i_substep in range( self.settings['structural_substeps'].value + 1): # run structural solver coeff = ((i_substep + 1) / (self.settings['structural_substeps'].value + 1)) structural_kstep = self.interpolate_timesteps( step0=self.data.structure.timestep_info[-1], step1=copy_structural_kstep, out_step=structural_kstep, coeff=coeff) self.data = self.structural_solver.run( structural_step=structural_kstep, dt=self.substep_dt) self.time_struc += time.perf_counter() - ini_time_struc # check convergence if self.convergence(k, structural_kstep, previous_kstep): # move the aerodynamic surface according to the structural one self.aero_solver.update_custom_grid( structural_kstep, aero_kstep) break # move the aerodynamic surface according the the structural one self.aero_solver.update_custom_grid(structural_kstep, aero_kstep) self.aero_solver.add_step() self.data.aero.timestep_info[-1] = aero_kstep.copy() self.structural_solver.add_step() self.data.structure.timestep_info[-1] = structural_kstep.copy() final_time = time.perf_counter() if self.print_info: self.residual_table.print_line([ self.data.ts, self.data.ts * self.dt.value, k, self.time_struc / (self.time_aero + self.time_struc), final_time - initial_time, np.log10(self.res_dqdt), structural_kstep.for_vel[0], structural_kstep.for_vel[2], np.sum(structural_kstep.steady_applied_forces[:, 0]), np.sum(structural_kstep.steady_applied_forces[:, 2]) ]) self.structural_solver.extract_resultants() # run postprocessors if self.with_postprocessors: for postproc in self.postprocessors: self.data = self.postprocessors[postproc].run(online=True) if self.print_info: cout.cout_wrap('...Finished', 1) return self.data
def initialise(self, data, in_dict=None): self.in_dict = in_dict # For backwards compatibility if len(self.in_dict.keys()) == 0: cout.cout_wrap( "WARNING: The code will run for backwards compatibility. \ In future releases you will need to define a 'wake_shape_generator' in ``AerogridLoader''. \ Please, check the documentation", 3) # Look for an aerodynamic solver if 'StaticUvlm' in data.settings: aero_solver_name = 'StaticUvlm' aero_solver_settings = data.settings['StaticUvlm'] elif 'SHWUvlm' in data.settings: aero_solver_name = 'SHWUvlm' aero_solver_settings = data.settings['SHWUvlm'] elif 'StaticCoupled' in data.settings: aero_solver_name = data.settings['StaticCoupled'][ 'aero_solver'] aero_solver_settings = data.settings['StaticCoupled'][ 'aero_solver_settings'] elif 'StaticCoupledRBM' in data.settings: aero_solver_name = data.settings['StaticCoupledRBM'][ 'aero_solver'] aero_solver_settings = data.settings['StaticCoupledRBM'][ 'aero_solver_settings'] elif 'DynamicCoupled' in data.settings: aero_solver_name = data.settings['DynamicCoupled'][ 'aero_solver'] aero_solver_settings = data.settings['DynamicCoupled'][ 'aero_solver_settings'] elif 'StepUvlm' in data.settings: aero_solver_name = 'StepUvlm' aero_solver_settings = data.settings['StepUvlm'] else: raise RuntimeError("ERROR: aerodynamic solver not found") # Get the minimum parameters needed to define the wake aero_solver = solver_interface.solver_from_string(aero_solver_name) settings.to_custom_types(aero_solver_settings, aero_solver.settings_types, aero_solver.settings_default) if 'dt' in aero_solver_settings.keys(): dt = aero_solver_settings['dt'].value elif 'rollup_dt' in aero_solver_settings.keys(): dt = aero_solver_settings['rollup_dt'].value else: # print(aero_solver['velocity_field_input']['u_inf']) dt = 1. / aero_solver_settings['velocity_field_input'][ 'u_inf'].value self.in_dict = { 'u_inf': aero_solver_settings['velocity_field_input']['u_inf'], 'u_inf_direction': aero_solver_settings['velocity_field_input'] ['u_inf_direction'], 'dt': dt } settings.to_custom_types(self.in_dict, self.settings_types, self.settings_default, no_ctype=True) self.u_inf = self.in_dict['u_inf'] self.u_inf_direction = self.in_dict['u_inf_direction'] self.dt = self.in_dict['dt'] if self.in_dict['dx1'] == -1: self.dx1 = self.u_inf * self.dt else: self.dx1 = self.in_dict['dx1'] self.ndx1 = self.in_dict['ndx1'] self.r = self.in_dict['r'] if self.in_dict['dxmax'] == -1: self.dxmax = self.dx1
def initialise_solver(solver_name, print_info=True): if print_info: cout.cout_wrap('Generating an instance of %s' % solver_name, 2) cls_type = solver_from_string(solver_name) solver = cls_type() return solver
def check_stability(self, restart_arnoldi=False): r""" Checks the stability of the ROM by computing its eigenvalues. If the resulting system is unstable, the Arnoldi procedure can be restarted to eliminate the eigenvalues outside the stability boundary. However, if this is the case, the ROM no longer matches the moments of the original system at the specific frequencies since now the approximation is done with respect to a system of the form: .. math:: \Sigma = \left(\begin{array}{c|c} \mathbf{A} & \mathbf{\bar{B}} \\ \hline \mathbf{C} & \ \end{array}\right) where :math:`\mathbf{\bar{B}} = (\mu \mathbf{I}_n - \mathbf{A})\mathbf{B}` Args: restart_arnoldi (bool): Restart the relevant Arnoldi algorithm with the unstable eigenvalues removed. """ assert self.ssrom is not None, 'ROM not calculated yet' eigs = sclalg.eigvals(self.ssrom.A) eigs_abs = np.abs(eigs) order = np.argsort(eigs_abs)[::-1] eigs = eigs[order] eigs_abs = eigs_abs[order] unstable = False if self.sstype == 'dt': if any(eigs_abs > 1.): unstable = True unstable_eigenvalues = eigs[eigs_abs > 1.] try: cout.cout_wrap( 'Unstable ROM - %d Eigenvalues with |r| > 1' % len(unstable_eigenvalues)) except ValueError: pass for mu in unstable_eigenvalues: try: cout.cout_wrap('\tmu = %f + %fj' % (mu.real, mu.imag)) except ValueError: pass else: try: cout.cout_wrap('ROM is stable') cout.cout_wrap('\tDT Eigenvalues:') cout.cout_wrap( len(eigs_abs) * '\t\tmu = %4f + %4fj\n' % tuple(eigs.view(float))) except ValueError: pass else: if any(eigs.real > 0): unstable = True cout.cout_wrap('Unstable ROM', 3) unstable_eigenvalues = eigs[eigs.real > 0] else: cout.cout_wrap('ROM is stable') # Restarted Arnoldi # Modify the B matrix in the full state system -> maybe better to have a copy if unstable and restart_arnoldi: print( 'Restarting the Arnoldi method - Reducing ROM order from r = %d to r = %d' % (self.r, self.r - 1)) self.ss_original = self.ss remove_unstable = np.eye(self.ss.states) for mu in unstable_eigenvalues: remove_unstable = np.matmul( remove_unstable, mu * np.eye(self.ss.states) - self.ss.A) self.ss.B = remove_unstable.dot(self.ss.B) # self.ss.C = self.ss.C.dot(remove_unstable.T) if self.r > 1: self.r -= 1 self.run(self.algorithm, self.r, self.frequency) else: print( 'Unable to reduce ROM any further - ROM still unstable...') return not unstable
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
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 run(self): cout.cout_wrap('Running static coupled solver...', 1) for i_step in range(self.settings['n_load_steps'].value): # load step coefficient if not self.settings['n_load_steps'].value == 0: load_step_multiplier = ( i_step + 1.0) / self.settings['n_load_steps'].value else: load_step_multiplier = 1.0 # new storage every load step if i_step > 0: self.increase_ts() for i_iter in range(self.settings['max_iter'].value): cout.cout_wrap('i_step: %u, i_iter: %u' % (i_step, i_iter)) # run aero self.data = self.aero_solver.run() print('Beam last node: ') print( self.data.structure.timestep_info[self.data.ts].pos[20, :]) # map force struct_forces = mapping.aero2struct_force_mapping( self.data.aero.timestep_info[self.data.ts].forces, self.data.aero.struct2aero_mapping, self.data.aero.timestep_info[self.data.ts].zeta, self.data.structure.timestep_info[self.data.ts].pos, self.data.structure.timestep_info[self.data.ts].psi, self.data.structure.node_master_elem, self.data.structure.master, algebra.quat2rot(self.data.structure.timestep_info[ self.data.ts].quat).T) if not self.settings['relaxation_factor'].value == 0.: if i_iter == 0: self.previous_force = struct_forces.copy() temp = struct_forces.copy() struct_forces = ( (1.0 - self.settings['relaxation_factor'].value) * struct_forces + self.settings['relaxation_factor'].value * self.previous_force) self.previous_force = temp # copy force in beam temp1 = load_step_multiplier * struct_forces self.data.structure.timestep_info[ self.data.ts].steady_applied_forces = temp1.astype( dtype=ct.c_double, order='F') # update gravity direction # TODO # run beam self.data = self.structural_solver.run() # update grid self.aero_solver.update_step() # convergence if self.convergence(i_iter, i_step): break # for i_step in range(self.settings['n_load_steps']): # coeff = ct.c_double((i_step + 1.0)/(self.settings['n_load_steps'])) # for i_iter in range(self.settings['max_iter']): # cout.cout_wrap('Iter: %u, step: %u' % (i_iter, i_step), 2) # # self.aero_solver.initialise(self.data, update_flightcon=False, quiet=True) # self.data = self.aero_solver.run() # # struct_forces = mapping.aero2struct_force_mapping( # self.data.grid.timestep_info[self.ts].forces, # self.data.grid.struct2aero_mapping, # self.data.grid.timestep_info[self.ts].zeta, # self.data.beam.timestep_info[self.ts].pos_def, # self.data.beam.timestep_info[self.ts].psi_def, # self.data.beam.node_master_elem, # self.data.beam.master, # self.data.grid.inertial2aero) # # if self.relaxation_factor > 0.0: # struct_forces = ((1.0 - self.relaxation_factor)*struct_forces + # self.relaxation_factor*self.previous_forces) # # self.previous_forces = struct_forces # self.data.beam.update_forces(struct_forces) # self.structural_solver.initialise(self.data) # self.data = self.structural_solver.run(coeff) # # if self.convergence(i_iter): # self.data.flightconditions['FlightCon']['u_inf'] = self.original_u_inf # self.aero_solver.initialise(self.data, quiet=True) # self.data = self.aero_solver.run() # if i_step == self.settings['n_load_steps'] - 1: # cout.cout_wrap('Converged in %u iterations' % (i_iter + 1), 2) # break cout.cout_wrap('...Finished', 1) return self.data
def run(self, online=False): # Use the following statement in case the ct types are not defined and # you need them on uvlm3d # self.data.aero.timestep_info[-1].generate_ctypes_pointers() if self.settings['format'] == 'h5': file_exists = os.path.isfile(self.filename) hdfile = h5py.File(self.filename, 'a') if (online and file_exists): self.save_timestep(self.data, self.settings, self.data.ts, hdfile) else: skip_attr_init = copy.deepcopy(self.settings['skip_attr']) skip_attr_init.append('timestep_info') h5utils.add_as_grp(self.data, hdfile, grpname='data', ClassesToSave=self.ClassesToSave, SkipAttr=skip_attr_init, compress_float=self.settings['compress_float']) if self.settings['save_struct']: h5utils.add_as_grp(list(), hdfile['data']['structure'], grpname='timestep_info') if self.settings['save_aero']: h5utils.add_as_grp(list(), hdfile['data']['aero'], grpname='timestep_info') for it in range(len(self.data.structure.timestep_info)): tstep_p = self.data.structure.timestep_info[it] if tstep_p is not None: self.save_timestep(self.data, self.settings, it, hdfile) hdfile.close() if self.settings['save_linear_uvlm']: linhdffile = h5py.File(self.filename.replace('.data.h5', '.uvlmss.h5'), 'a') h5utils.add_as_grp(self.data.linear.linear_system.uvlm.ss, linhdffile, grpname='ss', ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], compress_float=self.settings['compress_float']) h5utils.add_as_grp(self.data.linear.linear_system.linearisation_vectors, linhdffile, grpname='linearisation_vectors', ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], compress_float=self.settings['compress_float']) linhdffile.close() if self.settings['save_linear']: with h5py.File(self.filename_linear, 'a') as linfile: h5utils.add_as_grp(self.data.linear.linear_system.linearisation_vectors, linfile, grpname='linearisation_vectors', ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], compress_float=self.settings['compress_float']) h5utils.add_as_grp(self.data.linear.ss, linfile, grpname='ss', ClassesToSave=self.ClassesToSave, SkipAttr=self.settings['skip_attr'], compress_float=self.settings['compress_float']) if self.settings['save_rom']: try: for k, rom in self.data.linear.linear_system.uvlm.rom.items(): rom.save(self.filename.replace('.data.h5', '_{:s}.rom.h5'.format(k.lower()))) except AttributeError: cout.cout_wrap('Could not locate a reduced order model to save') elif self.settings['format'] == 'mat': from scipy.io import savemat if self.settings['save_linear']: # reference-forces linearisation_vectors = self.data.linear.linear_system.linearisation_vectors matfilename = self.filename.replace('.data.h5', '.linss.mat') A, B, C, D = self.data.linear.ss.get_mats() savedict = {'A': A, 'B': B, 'C': C, 'D': D} for k, v in linearisation_vectors.items(): savedict[k] = v try: dt = self.data.linear.ss.dt savedict['dt'] = dt except AttributeError: pass savemat(matfilename, savedict) if self.settings['save_linear_uvlm']: matfilename = self.filename.replace('.data.h5', '.uvlmss.mat') linearisation_vectors = self.data.linear.linear_system.uvlm.linearisation_vectors A, B, C, D = self.data.linear.linear_system.uvlm.ss.get_mats() savedict = {'A': A, 'B': B, 'C': C, 'D': D} for k, v in linearisation_vectors.items(): savedict[k] = v try: dt = self.data.linear.ss.dt savedict['dt'] = dt except AttributeError: pass savemat(matfilename, savedict) return self.data
def generate_fem_file(route, case_name, num_elem, num_node_elem=3): global num_node num_node = (num_node_elem - 1) * num_elem + 1 # import pdb; pdb.set_trace() angle = 0 * np.pi / 180.0 x = (np.linspace(0, length, num_node)) * np.cos(angle) y = (np.linspace(0, length, num_node)) * np.sin(angle) z = np.zeros((num_node, )) structural_twist = np.zeros((num_elem, num_node_elem)) frame_of_reference_delta = np.zeros((num_elem, num_node_elem, 3)) for ielem in range(num_elem): for inode in range(num_node_elem): frame_of_reference_delta[ielem, inode, :] = [ -np.sin(angle), np.cos(angle), 0 ] scale = 1 x *= scale y *= scale z *= scale conn = np.zeros((num_elem, num_node_elem), dtype=int) for ielem in range(num_elem): conn[ielem, :] = (np.ones( (3, )) * ielem * (num_node_elem - 1) + [0, 2, 1]) # stiffness array # import pdb; pdb.set_trace() num_stiffness = 1 ea = E * A # APPROXIMATION!!! cout.cout_wrap("Assuming isotropic material", 2) G = E / 2.0 / (1.0 + 0.3) cout.cout_wrap("Using total cross-section area as shear area", 2) ga = G * A cout.cout_wrap("Assuming planar cross-sections", 2) J = 2.0 * I gj = G * J base_stiffness = np.diag([ea, ga, ga, gj, ei, ei]) stiffness = np.zeros((num_stiffness, 6, 6)) # import pdb; pdb.set_trace() for i in range(num_stiffness): stiffness[i, :, :] = base_stiffness # element stiffness elem_stiffness = np.zeros((num_elem, ), dtype=int) # mass array num_mass = 1 base_mass = m_bar * np.diag([1.0, 1.0, 1.0, J / A, I / A, I / A]) # base_mass = m_bar*np.diag([1.0, 1.0, 1.0, 1.0,1.0,1.0]) mass = np.zeros((num_mass, 6, 6)) for i in range(num_mass): mass[i, :, :] = base_mass # element masses elem_mass = np.zeros((num_elem, ), dtype=int) # bocos boundary_conditions = np.zeros((num_node, 1), dtype=int) boundary_conditions[0] = 1 boundary_conditions[-1] = -1 # beam number beam_number = np.zeros((num_node, 1), dtype=int) # new app forces scheme (only follower) app_forces = np.zeros((num_node, 6)) # app_forces[0, :] = [0, 0, 3000000, 0, 0, 0] # lumped masses input n_lumped_mass = 1 lumped_mass_nodes = np.array([num_node - 1], dtype=int) lumped_mass = np.zeros((n_lumped_mass, )) lumped_mass[0] = 0.0 lumped_mass_inertia = np.zeros((n_lumped_mass, 3, 3)) lumped_mass_position = np.zeros((n_lumped_mass, 3)) #n_lumped_mass = 1 #lumped_mass_nodes = np.ones((num_node,), dtype=int) #lumped_mass = np.zeros((n_lumped_mass, )) #lumped_mass[0] = m_bar*length/num_elem/(num_node_elem-1) #lumped_mass_inertia[0,:,:] = np.diag([J, I, I]) #lumped_mass_position = np.zeros((n_lumped_mass, 3)) with h5.File(route + '/' + case_name + '.fem.h5', 'a') as h5file: # CHECKING if (elem_stiffness.shape[0] != num_elem): sys.exit( "ERROR: Element stiffness must be defined for each element") if (elem_mass.shape[0] != num_elem): sys.exit("ERROR: Element mass must be defined for each element") if (frame_of_reference_delta.shape[0] != num_elem): sys.exit( "ERROR: The first dimension of FoR does not match the number of elements" ) if (frame_of_reference_delta.shape[1] != num_node_elem): sys.exit( "ERROR: The second dimension of FoR does not match the number of nodes element" ) if (frame_of_reference_delta.shape[2] != 3): sys.exit("ERROR: The third dimension of FoR must be 3") if (structural_twist.shape[0] != num_node): sys.exit( "ERROR: The structural twist must be defined for each node") if (boundary_conditions.shape[0] != num_node): sys.exit( "ERROR: The boundary conditions must be defined for each node") if (beam_number.shape[0] != num_node): sys.exit("ERROR: The beam number must be defined for each node") if (app_forces.shape[0] != num_node): sys.exit( "ERROR: The first dimension of the applied forces matrix does not match the number of nodes" ) if (app_forces.shape[1] != 6): sys.exit( "ERROR: The second dimension of the applied forces matrix must be 6" ) coordinates = h5file.create_dataset('coordinates', data=np.column_stack((x, y, z))) conectivities = h5file.create_dataset('connectivities', data=conn) num_nodes_elem_handle = h5file.create_dataset('num_node_elem', data=num_node_elem) num_nodes_handle = h5file.create_dataset('num_node', data=num_node) num_elem_handle = h5file.create_dataset('num_elem', data=num_elem) stiffness_db_handle = h5file.create_dataset('stiffness_db', data=stiffness) stiffness_handle = h5file.create_dataset('elem_stiffness', data=elem_stiffness) mass_db_handle = h5file.create_dataset('mass_db', data=mass) mass_handle = h5file.create_dataset('elem_mass', data=elem_mass) frame_of_reference_delta_handle = h5file.create_dataset( 'frame_of_reference_delta', data=frame_of_reference_delta) structural_twist_handle = h5file.create_dataset('structural_twist', data=structural_twist) bocos_handle = h5file.create_dataset('boundary_conditions', data=boundary_conditions) beam_handle = h5file.create_dataset('beam_number', data=beam_number) app_forces_handle = h5file.create_dataset('app_forces', data=app_forces) lumped_mass_nodes_handle = h5file.create_dataset( 'lumped_mass_nodes', data=lumped_mass_nodes) lumped_mass_handle = h5file.create_dataset('lumped_mass', data=lumped_mass) lumped_mass_inertia_handle = h5file.create_dataset( 'lumped_mass_inertia', data=lumped_mass_inertia) lumped_mass_position_handle = h5file.create_dataset( 'lumped_mass_position', data=lumped_mass_position) return num_node, coordinates
def run(self): # previous_kstep = self.data.structure.timestep_info[-1].copy() # dynamic simulations start at tstep == 1, 0 is reserved for the initial state for self.data.ts in range( len(self.data.structure.timestep_info), self.settings['n_time_steps'].value + len(self.data.structure.timestep_info)): # aero_kstep = self.data.aero.timestep_info[-1].copy() structural_kstep = self.data.structure.timestep_info[-1].copy() # previous_kstep = self.data.structure.timestep_info[-1].copy() k = 0 for k in range(self.settings['fsi_substeps'].value + 1): if k == self.settings[ 'fsi_substeps'].value and not self.settings[ 'fsi_substeps'] == 0: cout.cout_wrap('The FSI solver did not converge!!!') print('K step q:') print(structural_kstep.q) print('K step dq:') print(structural_kstep.dqdt) print('K step ddq:') print(structural_kstep.dqddt) a = 1 break # generate new grid (already rotated) aero_kstep = self.data.aero.timestep_info[-1].copy() self.aero_solver.update_custom_grid(structural_kstep, aero_kstep) # run the solver self.data = self.aero_solver.run(aero_kstep, structural_kstep, convect_wake=True) previous_kstep = structural_kstep.copy() structural_kstep = self.data.structure.timestep_info[-1].copy() # map forces force_coeff = 0.0 if self.settings['include_unsteady_force_contribution']: force_coeff = -1.0 self.map_forces(aero_kstep, structural_kstep, force_coeff) # relaxation relax_factor = self.relaxation_factor(k) relax(self.data.structure, structural_kstep, previous_kstep, relax_factor) if k > 0.9 * self.settings['fsi_substeps'].value: relax_factor = 0.3 elif k > 0.8 * self.settings['fsi_substeps'].value: relax_factor = 0.8 # run structural solver self.data = self.structural_solver.run( structural_step=structural_kstep) # check convergence if self.convergence(k, structural_kstep, previous_kstep): break self.aero_solver.add_step() self.data.aero.timestep_info[-1] = aero_kstep.copy() self.structural_solver.add_step() self.data.structure.timestep_info[-1] = structural_kstep.copy() self.data.structure.integrate_position(-1, self.settings['dt'].value) if self.print_info: self.residual_table.print_line([ self.data.ts, self.data.ts * self.dt.value, k, np.log10(self.res), np.log10(self.res_dqdt), np.log10(self.res_dqddt), structural_kstep.for_vel[2] ]) self.structural_solver.extract_resultants() # run postprocessors if self.with_postprocessors: for postproc in self.postprocessors: self.data = self.postprocessors[postproc].run(online=True) if self.print_info: cout.cout_wrap('...Finished', 1) return self.data
'AerogridPlot': settings['AerogridPlot'] } } settings['Modal'] = { 'folder': route + '/output', 'include_rbm': 'on', 'NumLambda': 10000, 'num_steps': 1, 'print_matrices': 'on' } import configobj config = configobj.ConfigObj() config.filename = file_name for k, v in settings.items(): config[k] = v config.write() # run everything clean_test_files() generate_fem_file(route, case_name, num_elem, num_node_elem) generate_aero_file() generate_dyn_file() generate_solver_file() cout.cout_wrap( 'Reference for validation: "Rotary wing structural dynamics and aeroelasticity", R.L. Bielawa. AIAA education series. Second edition', 1)
def __init__(self, solver_name, n_iter=None, message=''): super().__init__(message) cout.cout_wrap("The solver " + solver_name + " did not converge in " + str(n_iter) + " iterations.", 3)
def __init__(self, solver_name, message=''): super().__init__(message) if cout.cout_wrap is None: print("The solver " + solver_name + " is not implemented. Check the list of available solvers when starting SHARPy") else: cout.cout_wrap("The solver " + solver_name + " is not implemented. Check the list of available solvers when starting SHARPy", 3)
def print_info(self): cout.cout_wrap('Trajectory information:', 2) cout.cout_wrap('\t-travel time: {}'.format(self.travel_time), 2) cout.cout_wrap('\t-curve length: {}'.format(self.curve_length), 2) cout.cout_wrap('-------------------------------', 2)
def stable_realisation(self, *args, **kwargs): r"""Remove unstable poles left after reduction Using a Schur decomposition of the reduced plant matrix :math:`\mathbf{A}_m\in\mathbb{C}^{m\times m}`, the method removes the unstable eigenvalues that could have appeared after the moment-matching reduction. The oblique projection matrices :math:`\mathbf{T}_L\in\mathbb{C}^{m \times p}` and :math:`\mathbf{T}_R\in\mathbb{C}^{m \times p}`` result in a stable realisation .. math:: \mathbf{A}_s = \mathbf{T}_L^\top\mathbf{AT}_R \in \mathbb{C}^{p\times p}. Args: A (np.ndarray): plant matrix (if not provided ``self.ssrom.A`` will be used). Returns: tuple: Left and right projection matrices :math:`\mathbf{T}_L\in\mathbb{C}^{m \times p}` and :math:`\mathbf{T}_R\in\mathbb{C}^{m \times p}` References: Jaimoukha, I. M., Kasenally, E. D.. Implicitly Restarted Krylov Subspace Methods for Stable Partial Realizations. SIAM Journal of Matrix Analysis and Applications, 1997. See Also: The method employs :func:`sharpy.rom.utils.krylovutils.schur_ordered()` and :func:`sharpy.rom.utils.krylovutils.remove_a12`. """ cout.cout_wrap( 'Stabilising system by removing unstable eigenvalues using a Schur decomposition', 1) if self.ssrom is None: A = args[0] ct = kwargs['ct'] assert type(ct) == bool, 'CT system flag should be a bool' else: A = self.ssrom.A if self.sstype == 'ct': ct = True else: ct = False m = A.shape[0] As, T1, n_stable = krylovutils.schur_ordered(A, ct=ct) # Remove the (1,2) block of the Schur ordered matrix T2, X = krylovutils.remove_a12(As, n_stable) T3 = np.eye(m, n_stable) TL = T3.T.dot(T2.dot(np.conj(T1))) TR = T1.T.dot(np.linalg.inv(T2).dot(T3)) cout.cout_wrap('System reduced to %g states' % n_stable, 1) # for debugging # import matplotlib.pyplot as plt Ar = TL.dot(A.dot(TR)) eigsA = np.linalg.eigvals(A) eigsAr = np.linalg.eigvals(Ar) # # plt.scatter(eigsA.real, eigsA.imag) # plt.scatter(eigsAr.real, eigsAr.imag) # plt.show() if ct: assert np.sum( np.real(eigsAr) <= 0. ) == n_stable, 'Number of stable eigvals computed not equal to those in Ar' else: assert np.sum( np.abs(eigsAr) <= 1. ) == n_stable, 'Number of stable eigvals computed not equal to those in Ar' return TL.T, TR
def convergence(self, i_iter, i_step): if i_iter == self.settings['max_iter'].value - 1: cout.cout_wrap('StaticCoupled did not converge!', 0) # quit(-1) return_value = None if i_iter == 0: self.initial_residual = np.linalg.norm( self.data.structure.timestep_info[self.data.ts].pos) self.previous_residual = self.initial_residual self.current_residual = self.initial_residual if self.print_info: forces = self.data.structure.timestep_info[ self.data.ts].total_forces self.residual_table.print_line([ i_iter, i_step, 0.0, forces[0], forces[1], forces[2], forces[3], forces[4], forces[5], ]) return False self.current_residual = np.linalg.norm( self.data.structure.timestep_info[self.data.ts].pos) if self.print_info: forces = self.data.structure.timestep_info[ self.data.ts].total_forces res_print = np.NINF if (np.abs(self.current_residual - self.previous_residual) > sys.float_info.epsilon * 10): res_print = np.log10( np.abs(self.current_residual - self.previous_residual) / self.initial_residual) self.residual_table.print_line([ i_iter, i_step, res_print, forces[0], forces[1], forces[2], forces[3], forces[4], forces[5], ]) if return_value is None: if np.abs( self.current_residual - self.previous_residual ) / self.initial_residual < self.settings['tolerance'].value: return_value = True else: self.previous_residual = self.current_residual return_value = False if return_value is None: return_value = False return return_value
def run(self): n_steps = self.settings['n_tsteps'].value x0 = self.input_data_dict['x0'] u = self.input_data_dict['u'] ss = self.data.linear.ss if len(x0) != ss.states: warnings.warn( 'Number of states in the initial state vector not equal to the number of states' ) x0 = np.zeros(ss.states) if u.shape[1] != ss.inputs: warnings.warn( 'Dimensions of the input vector not equal to the number of inputs' ) cout.cout_wrap('Number of inputs: %g' % ss.inputs, 3) cout.cout_wrap('Number of timesteps: %g' % n_steps, 3) cout.cout_wrap( 'Number of UVLM inputs: %g' % self.data.linear.linear_system.uvlm.ss.inputs, 3) cout.cout_wrap( 'Number of beam inputs: %g' % self.data.linear.linear_system.beam.ss.inputs, 3) breakpoint() try: dt = ss.dt except AttributeError: dt = self.settings['dt'].value # Total time to run T = n_steps * dt u_ref = self.settings['reference_velocity'].value # If the system is scaled: if u_ref != 1.: scaling_factors = self.data.linear.linear_system.uvlm.sys.ScalingFacts dt_dimensional = scaling_factors['length'] / u_ref T_dimensional = n_steps * dt_dimensional T = T_dimensional / scaling_factors['time'] ss = self.data.linear.linear_system.update( self.settings['reference_velocity'].value) t_dom = np.linspace(0, T, n_steps) # Use the scipy linear solver sys = libss.ss_to_scipy(ss) cout.cout_wrap('Solving linear system using scipy...') t0 = time.time() # breakpoint() out = sys.output(u, t=t_dom, x0=x0) ts = time.time() - t0 cout.cout_wrap('\tSolved in %.2fs' % ts, 1) t_out = out[0] x_out = out[2] y_out = out[1] if self.settings['write_dat']: cout.cout_wrap( 'Writing linear simulation output .dat files to %s' % self.folder) if 'y' in self.settings['write_dat']: np.savetxt(self.folder + '/y_out.dat', y_out) cout.cout_wrap('Output vector written', 2) if 'x' in self.settings['write_dat']: np.savetxt(self.folder + '/x_out.dat', x_out) cout.cout_wrap('State vector written', 2) if 'u' in self.settings['write_dat']: np.savetxt(self.folder + '/u_out.dat', u) cout.cout_wrap('Input vector written', 2) if 't' in self.settings['write_dat']: np.savetxt(self.folder + '/t_out.dat', t_out) cout.cout_wrap('Time domain written', 2) cout.cout_wrap('Success', 1) # Pack state variables into linear timestep info cout.cout_wrap('Plotting results...') for n in range(len(t_out) - 1): tstep = LinearTimeStepInfo() tstep.x = x_out[n, :] tstep.y = y_out[n, :] tstep.t = t_out[n] tstep.u = u[n, :] self.data.linear.timestep_info.append(tstep) # TODO: option to save to h5 # Pack variables into respective aero or structural time step infos (with the + f0 from lin) # Need to obtain information from the variables in a similar fashion as done with the database # for the beam case aero_tstep, struct_tstep = state_to_timestep( self.data, tstep.x, tstep.u, tstep.y) self.data.aero.timestep_info.append(aero_tstep) self.data.structure.timestep_info.append(struct_tstep) # run postprocessors if self.with_postprocessors: for postproc in self.postprocessors: self.data = self.postprocessors[postproc].run(online=True) return self.data
def initialise(self, data, custom_settings=None, caller=None): self.data = data if custom_settings is None: self.settings = data.settings[self.solver_id] else: self.settings = custom_settings settings.to_custom_types(self.settings, self.settings_types, self.settings_default, options=self.settings_options) # Add these anyway - therefore if you add your own skip_attr you don't have to retype all of these self.settings['skip_attr'].extend(['fortran', 'airfoils', 'airfoil_db', 'settings_types', 'ct_dynamic_forces_list', 'ct_forces_list', 'ct_gamma_dot_list', 'ct_gamma_list', 'ct_gamma_star_list', 'ct_normals_list', 'ct_u_ext_list', 'ct_u_ext_star_list', 'ct_zeta_dot_list', 'ct_zeta_list', 'ct_zeta_star_list', 'dynamic_input']) self.ts_max = self.data.ts + 1 # create folder for containing files if necessary if not os.path.exists(self.settings['folder']): os.makedirs(self.settings['folder']) self.folder = self.settings['folder'] + '/' + self.data.settings['SHARPy']['case'] + '/savedata/' if not os.path.exists(self.folder): os.makedirs(self.folder) self.filename = self.folder + self.data.settings['SHARPy']['case'] + '.data.h5' self.filename_linear = self.folder + self.data.settings['SHARPy']['case'] + '.linss.h5' if os.path.isfile(self.filename): os.remove(self.filename) # check that there is a linear system - else return setting to false if self.settings['save_linear'] or self.settings['save_linear_uvlm']: try: self.data.linear except AttributeError: cout.cout_wrap('SaveData variables ``save_linear`` and/or ``save_linear`` are True but no linear system' 'been found', 3) self.settings['save_linear'] = False self.settings['save_linear_uvlm'] = False if self.settings['format'] == 'h5': # allocate list of classes to be saved if self.settings['save_aero']: self.ClassesToSave += (sharpy.aero.models.aerogrid.Aerogrid, sharpy.utils.datastructures.AeroTimeStepInfo,) if self.settings['save_struct']: self.ClassesToSave += ( sharpy.structure.models.beam.Beam, sharpy.utils.datastructures.StructTimeStepInfo,) if self.settings['save_linear']: self.ClassesToSave += (sharpy.solvers.linearassembler.Linear, sharpy.linear.assembler.linearaeroelastic.LinearAeroelastic, sharpy.linear.assembler.linearbeam.LinearBeam, sharpy.linear.assembler.linearuvlm.LinearUVLM, sharpy.linear.src.libss.ss, sharpy.linear.src.lingebm.FlexDynamic,) if self.settings['save_linear_uvlm']: self.ClassesToSave += (sharpy.solvers.linearassembler.Linear, sharpy.linear.src.libss.ss) self.caller = caller
def main(args=None, sharpy_input_dict=None): """ Main ``SHARPy`` routine This is the main ``SHARPy`` routine. It starts the solution process by reading the settings that are included in the ``.sharpy`` file that is parsed as an argument, or an equivalent dictionary given as ``sharpy_input_dict``. It reads the solvers specific settings and runs them in order Args: args (str): ``.sharpy`` file with the problem information and settings sharpy_input_dict (dict): ``dict`` with the same contents as the ``solver.txt`` file would have. Returns: sharpy.presharpy.presharpy.PreSharpy: object containing the simulation results. """ import time import argparse import sharpy.utils.input_arg as input_arg import sharpy.utils.solver_interface as solver_interface from sharpy.presharpy.presharpy import PreSharpy from sharpy.utils.cout_utils import start_writer, finish_writer import logging import os import h5py import sharpy.utils.h5utils as h5utils # Loading solvers and postprocessors import sharpy.solvers import sharpy.postproc import sharpy.generators import sharpy.controllers # ------------ try: # output writer start_writer() # timing t = time.process_time() t0_wall = time.perf_counter() if sharpy_input_dict is None: parser = argparse.ArgumentParser(prog='SHARPy', description= """This is the executable for Simulation of High Aspect Ratio Planes.\n Imperial College London 2020""") parser.add_argument('input_filename', help='path to the *.sharpy input file', type=str, default='') parser.add_argument('-r', '--restart', help='restart the solution with a given snapshot', type=str, default=None) parser.add_argument('-d', '--docs', help='generates the solver documentation in the specified location. ' 'Code does not execute if running this flag', action='store_true') if args is not None: args = parser.parse_args(args[1:]) else: args = parser.parse_args() if args.docs: import subprocess import sharpy.utils.docutils as docutils import sharpy.utils.sharpydir as sharpydir docutils.generate_documentation() # run make cout.cout_wrap('Running make html in sharpy/docs') subprocess.Popen(['make', 'html'], stdout=None, cwd=sharpydir.SharpyDir + '/docs') return 0 if args.input_filename == '': parser.error('input_filename is a required argument of SHARPy.') settings = input_arg.read_settings(args) if args.restart is None: # run preSHARPy data = PreSharpy(settings) else: try: with open(args.restart, 'rb') as restart_file: data = pickle.load(restart_file) except FileNotFoundError: raise FileNotFoundError('The file specified for the snapshot \ restart (-r) does not exist. Please check.') # update the settings data.update_settings(settings) # Read again the dyn.h5 file data.structure.dynamic_input = [] dyn_file_name = data.case_route + '/' + data.case_name + '.dyn.h5' if os.path.isfile(dyn_file_name): fid = h5py.File(dyn_file_name, 'r') data.structure.dyn_dict = h5utils.load_h5_in_dict(fid) # for it in range(self.num_steps): # data.structure.dynamic_input.append(dict()) # Loop for the solvers specified in *.sharpy['SHARPy']['flow'] for solver_name in settings['SHARPy']['flow']: solver = solver_interface.initialise_solver(solver_name) solver.initialise(data) data = solver.run() cpu_time = time.process_time() - t wall_time = time.perf_counter() - t0_wall cout.cout_wrap('FINISHED - Elapsed time = %f6 seconds' % wall_time, 2) cout.cout_wrap('FINISHED - CPU process time = %f6 seconds' % cpu_time, 2) finish_writer() except Exception as e: try: logdir = settings['SHARPy']['log_folder'] except KeyError: logdir = './' except NameError: logdir = './' logdir = os.path.abspath(logdir) cout.cout_wrap(('Exception raised, writing error log in %s/error.log' % logdir), 4) logging.basicConfig(filename='%s/error.log' % logdir, filemode='w', format='%(asctime)s-%(levelname)s-%(message)s', datefmt='%d-%b-%y %H:%M:%S', level=logging.INFO) logging.info('SHARPy Error Log') logging.error("Exception occurred", exc_info=True) raise e return data
def assemble(self): r""" Assembly of the linearised aeroelastic system. The UVLM state-space system has already been assembled. Prior to assembling the beam's first order state-space, the damping and stiffness matrices have to be modified to include the damping and stiffenning terms that arise from the linearisation of the aeordynamic forces with respect to the A frame of reference. See :func:`sharpy.linear.src.lin_aeroela.get_gebm2uvlm_gains()` for details on the linearisation. Then the beam is assembled as per the given settings in normalised time if the aerodynamic system has been scaled. The discrete time systems of the UVLM and the beam must have the same time step. The UVLM inputs and outputs are then projected onto the structural degrees of freedom (obviously with the exception of external gusts and control surfaces). Hence, the gains :math:`\mathbf{K}_{sa}` and :math:`\mathbf{K}_{as}` are added to the output and input of the UVLM system, respectively. These gains perform the following relation: .. math:: \begin{bmatrix}\zeta \\ \zeta' \\ u_g \\ \delta \end{bmatrix} = \mathbf{K}_{as} \begin{bmatrix} \eta \\ \eta' \\ u_g \\ \delta \end{bmatrix} = .. math:: \mathbf{N}_{nodes} = \mathbf{K}_{sa} \mathbf{f}_{vertices} If the beam is expressed in modal form, the UVLM is further projected onto the beam's modes to have the following input/output structure: Returns: """ uvlm = self.uvlm beam = self.beam # Linearisation of the aerodynamic forces introduces stiffenning and damping terms into the beam matrices flex_nodes = self.beam.sys.num_dof_flex stiff_aero = np.zeros_like(beam.sys.Kstr) damping_aero = np.zeros_like(beam.sys.Cstr) stiff_aero[:flex_nodes, :flex_nodes] = self.Kss rigid_dof = beam.sys.Kstr.shape[0] - flex_nodes total_dof = flex_nodes + rigid_dof if rigid_dof > 0: rigid_dof = beam.sys.Kstr.shape[0] - self.Kss.shape[0] stiff_aero[flex_nodes:, :flex_nodes] = self.Krs damping_aero[:flex_nodes, flex_nodes:] = self.Csr damping_aero[flex_nodes:, flex_nodes:] = self.Crr damping_aero[flex_nodes:, :flex_nodes] = self.Crs beam.sys.Cstr += damping_aero beam.sys.Kstr += stiff_aero if uvlm.scaled: beam.assemble(t_ref=uvlm.sys.ScalingFacts['time']) else: beam.assemble() if not self.load_uvlm_from_file: # Projecting the UVLM inputs and outputs onto the structural degrees of freedom Ksa = self.Kforces[:beam.sys. num_dof, :] # maps aerodynamic grid forces to nodal forces # Map the nodal displacement and velocities onto the grid displacements and velocities Kas = np.zeros((uvlm.ss.inputs, 2 * beam.sys.num_dof + (uvlm.ss.inputs - 2 * self.Kdisp.shape[0]))) Kas[:2*self.Kdisp.shape[0], :2*beam.sys.num_dof] = \ np.block([[self.Kdisp[:, :beam.sys.num_dof], self.Kdisp_vel[:, :beam.sys.num_dof]], [self.Kvel_disp[:, :beam.sys.num_dof], self.Kvel_vel[:, :beam.sys.num_dof]]]) # Retain other inputs Kas[2 * self.Kdisp.shape[0]:, 2 * beam.sys.num_dof:] = np.eye(uvlm.ss.inputs - 2 * self.Kdisp.shape[0]) # Scaling if uvlm.scaled: Kas /= uvlm.sys.ScalingFacts['length'] uvlm.ss.addGain(Ksa, where='out') uvlm.ss.addGain(Kas, where='in') self.couplings['Ksa'] = Ksa self.couplings['Kas'] = Kas if self.settings['beam_settings']['modal_projection'] == True and \ self.settings['beam_settings']['inout_coords'] == 'modes': # Project UVLM onto modal space phi = beam.sys.U in_mode_matrix = np.zeros( (uvlm.ss.inputs, beam.ss.outputs + (uvlm.ss.inputs - 2 * beam.sys.num_dof))) in_mode_matrix[:2 * beam.sys.num_dof, :2 * beam.sys.num_modes] = sclalg.block_diag( phi, phi) in_mode_matrix[2 * beam.sys.num_dof:, 2 * beam.sys.num_modes:] = np.eye(uvlm.ss.inputs - 2 * beam.sys.num_dof) out_mode_matrix = phi.T uvlm.ss.addGain(in_mode_matrix, where='in') uvlm.ss.addGain(out_mode_matrix, where='out') # Reduce uvlm projected onto structural coordinates if uvlm.rom: if rigid_dof != 0: self.runrom_rbm(uvlm) else: for k, rom in uvlm.rom.items(): uvlm.ss = rom.run(uvlm.ss) else: uvlm.ss = self.load_uvlm(self.settings['uvlm_filename']) # Coupling matrices Tas = np.eye(uvlm.ss.inputs, beam.ss.outputs) Tsa = np.eye(beam.ss.inputs, uvlm.ss.outputs) # Scale coupling matrices if uvlm.scaled: Tsa *= uvlm.sys.ScalingFacts['force'] * uvlm.sys.ScalingFacts[ 'time']**2 if rigid_dof > 0: warnings.warn( 'Time scaling for problems with rigid body motion under development.' ) Tas[:flex_nodes + 6, :flex_nodes + 6] /= uvlm.sys.ScalingFacts['length'] Tas[total_dof:total_dof + flex_nodes + 6] /= uvlm.sys.ScalingFacts['length'] else: if not self.settings['beam_settings']['modal_projection']: Tas /= uvlm.sys.ScalingFacts['length'] ss = libss.couple(ss01=uvlm.ss, ss02=beam.ss, K12=Tas, K21=Tsa) # Conditioning of A matrix # cond_a = np.linalg.cond(ss.A) # if type(uvlm.ss.A) != np.ndarray: # cond_a_uvlm = np.linalg.cond(uvlm.ss.A.todense()) # else: # cond_a_uvlm = np.linalg.cond(uvlm.ss.A) # cond_a_beam = np.linalg.cond(beam.ss.A) # cout.cout_wrap('Matrix A condition = %e' % cond_a) # cout.cout_wrap('Matrix A_uvlm condition = %e' % cond_a_uvlm) # cout.cout_wrap('Matrix A_beam condition = %e' % cond_a_beam) self.couplings['Tas'] = Tas self.couplings['Tsa'] = Tsa self.state_variables = {'aero': uvlm.ss.states, 'beam': beam.ss.states} # Save zero force reference self.linearisation_vectors['forces_aero_beam_dof'] = Ksa.dot( self.linearisation_vectors['forces_aero']) if self.settings['beam_settings']['modal_projection'] == True and \ self.settings['beam_settings']['inout_coords'] == 'modes': self.linearisation_vectors[ 'forces_aero_beam_dof'] = out_mode_matrix.dot( self.linearisation_vectors['forces_aero_beam_dof']) cout.cout_wrap('Aeroelastic system assembled:') cout.cout_wrap('\tAerodynamic states: %g' % uvlm.ss.states, 1) cout.cout_wrap('\tStructural states: %g' % beam.ss.states, 1) cout.cout_wrap('\tTotal states: %g' % ss.states, 1) cout.cout_wrap('\tInputs: %g' % ss.inputs, 1) cout.cout_wrap('\tOutputs: %g' % ss.outputs, 1) return ss
def xbeam_solv_couplednlndyn(beam, settings): n_elem = ct.c_int(beam.num_elem) n_nodes = ct.c_int(beam.num_node) n_mass = ct.c_int(beam.n_mass) n_stiff = ct.c_int(beam.n_stiff) dt = settings['dt'].value n_tsteps = settings['num_steps'].value time = np.zeros((n_tsteps,), dtype=ct.c_double, order='F') for i in range(n_tsteps): time[i] = i*dt # deformation history matrices for_vel = np.zeros((n_tsteps + 1, 6), order='F') for_acc = np.zeros((n_tsteps + 1, 6), order='F') quat_history = np.zeros((n_tsteps, 4), order='F') dt = ct.c_double(dt) n_tsteps = ct.c_int(n_tsteps) xbopts = Xbopts() xbopts.PrintInfo = ct.c_bool(settings['print_info']) xbopts.Solution = ct.c_int(910) xbopts.OutInaframe = ct.c_bool(False) xbopts.MaxIterations = settings['max_iterations'] xbopts.NumLoadSteps = settings['num_load_steps'] # xbopts.NumGauss = ct.c_int(0) xbopts.DeltaCurved = settings['delta_curved'] xbopts.MinDelta = settings['min_delta'] xbopts.NewmarkDamp = settings['newmark_damp'] xbopts.gravity_on = settings['gravity_on'] xbopts.gravity = settings['gravity'] xbopts.gravity_dir_x = ct.c_double(settings['gravity_dir'][0]) xbopts.gravity_dir_y = ct.c_double(settings['gravity_dir'][1]) xbopts.gravity_dir_z = ct.c_double(settings['gravity_dir'][2]) pos_def_history = np.zeros((n_tsteps.value, beam.num_node, 3), order='F', dtype=ct.c_double) pos_dot_def_history = np.zeros((n_tsteps.value, beam.num_node, 3), order='F', dtype=ct.c_double) psi_def_history = np.zeros((n_tsteps.value, beam.num_elem, 3, 3), order='F', dtype=ct.c_double) psi_dot_def_history = np.zeros((n_tsteps.value, beam.num_elem, 3, 3), order='F', dtype=ct.c_double) dynamic_force = np.zeros((n_nodes.value, 6, n_tsteps.value), dtype=ct.c_double, order='F') for it in range(n_tsteps.value): dynamic_force[:, :, it] = beam.dynamic_input[it]['dynamic_forces'] # status flag success = ct.c_bool(True) # here we only need to set the flags at True, all the forces are follower xbopts.FollowerForce = ct.c_bool(True) xbopts.FollowerForceRig = ct.c_bool(True) import time as ti start_time = ti.time() f_xbeam_solv_couplednlndyn(ct.byref(n_elem), ct.byref(n_nodes), ct.byref(n_tsteps), time.ctypes.data_as(doubleP), beam.fortran['num_nodes'].ctypes.data_as(intP), beam.fortran['num_mem'].ctypes.data_as(intP), beam.fortran['connectivities'].ctypes.data_as(intP), beam.fortran['master'].ctypes.data_as(intP), ct.byref(n_mass), beam.fortran['mass'].ctypes.data_as(doubleP), beam.fortran['mass_indices'].ctypes.data_as(intP), ct.byref(n_stiff), beam.fortran['stiffness'].ctypes.data_as(doubleP), beam.fortran['inv_stiffness'].ctypes.data_as(doubleP), beam.fortran['stiffness_indices'].ctypes.data_as(intP), beam.fortran['frame_of_reference_delta'].ctypes.data_as(doubleP), beam.fortran['rbmass'].ctypes.data_as(doubleP), beam.fortran['node_master_elem'].ctypes.data_as(intP), beam.fortran['vdof'].ctypes.data_as(intP), beam.fortran['fdof'].ctypes.data_as(intP), ct.byref(xbopts), beam.ini_info.pos.ctypes.data_as(doubleP), beam.ini_info.psi.ctypes.data_as(doubleP), beam.timestep_info[0].steady_applied_forces.ctypes.data_as(doubleP), dynamic_force.ctypes.data_as(doubleP), for_vel.ctypes.data_as(doubleP), for_acc.ctypes.data_as(doubleP), pos_def_history.ctypes.data_as(doubleP), psi_def_history.ctypes.data_as(doubleP), pos_dot_def_history.ctypes.data_as(doubleP), psi_dot_def_history.ctypes.data_as(doubleP), quat_history.ctypes.data_as(doubleP), ct.byref(success)) cout.cout_wrap("\n--- %s seconds ---" % (ti.time() - start_time), 1) if not success: raise Exception('couplednlndyn did not converge') for_pos = np.zeros_like(for_vel) for_pos[:, 0] = sc.integrate.cumtrapz(for_vel[:, 0], dx=dt.value, initial=0) for_pos[:, 1] = sc.integrate.cumtrapz(for_vel[:, 1], dx=dt.value, initial=0) for_pos[:, 2] = sc.integrate.cumtrapz(for_vel[:, 2], dx=dt.value, initial=0) glob_pos_def = np.zeros_like(pos_def_history) for it in range(n_tsteps.value): rot = algebra.quat2rot(quat_history[it, :]).transpose() for inode in range(beam.num_node): glob_pos_def[it, inode, :] = np.dot(rot.T, pos_def_history[it, inode, :]) for i in range(n_tsteps.value - 1): beam.timestep_info[i + 1].pos[:] = pos_def_history[i+1, :] beam.timestep_info[i + 1].psi[:] = psi_def_history[i+1, :] beam.timestep_info[i + 1].pos_dot[:] = pos_dot_def_history[i+1, :] beam.timestep_info[i + 1].psi_dot[:] = psi_dot_def_history[i+1, :] beam.timestep_info[i + 1].quat[:] = quat_history[i+1, :] beam.timestep_info[i + 1].for_pos[:] = for_pos[i+1, :] beam.timestep_info[i + 1].for_vel[:] = for_vel[i+1, :]
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', 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, camber_effect_on_twist=False, wsp=0., dt=0.): # Warning for back compatibility cout.cout_wrap( 'rotor_from_excel_type02 is obsolete! rotor_from_excel_type03 instead!', 3) # Assign values to dictionaries op_params = {} op_params['rotation_velocity'] = rotation_velocity op_params['pitch_deg'] = pitch_deg op_params['wsp'] = wsp op_params['dt'] = dt geom_params = {} geom_params['chord_panels'] = chord_panels geom_params['tol_remove_points'] = tol_remove_points geom_params['n_points_camber'] = n_points_camber geom_params['h5_cross_sec_prop'] = h5_cross_sec_prop geom_params['m_distribution'] = m_distribution options = {} options['camber_effect_on_twist'] = camber_effect_on_twist options[ 'user_defined_m_distribution_type'] = user_defined_m_distribution_type options['include_polars'] = False excel_description = {} excel_description['excel_file_name'] = excel_file_name excel_description['excel_sheet_parameters'] = excel_sheet_parameters excel_description[ 'excel_sheet_structural_blade'] = excel_sheet_structural_blade excel_description[ 'excel_sheet_discretization_blade'] = excel_sheet_discretization_blade excel_description['excel_sheet_aero_blade'] = excel_sheet_aero_blade excel_description['excel_sheet_airfoil_info'] = excel_sheet_airfoil_info excel_description['excel_sheet_airfoil_chord'] = excel_sheet_airfoil_coord rotor = rotor_from_excel_type03(op_params, geom_params, excel_description, options) return rotor
def run(self, structural_step=None, dt=None): if structural_step is None: structural_step = self.data.structure.timestep_info[-1] if structural_step.mb_dict is not None: MBdict = structural_step.mb_dict else: MBdict = self.data.structure.ini_mb_dict if dt is None: dt = ct.c_float(0.) self.lc_list = lagrangeconstraints.initialize_constraints(MBdict) self.num_LM_eq = lagrangeconstraints.define_num_LM_eq(self.lc_list) # TODO: only working for constant forces MB_beam, MB_tstep = mb.split_multibody(self.data.structure, structural_step, MBdict, 0) q = np.zeros((self.sys_size + self.num_LM_eq, ), dtype=ct.c_double, order='F') dqdt = np.zeros((self.sys_size + self.num_LM_eq, ), dtype=ct.c_double, order='F') dqddt = np.zeros((self.sys_size + self.num_LM_eq, ), dtype=ct.c_double, order='F') mb.disp2state(MB_beam, MB_tstep, q, dqdt, dqddt) # Lagrange multipliers parameters num_LM_eq = self.num_LM_eq Lambda = np.zeros((num_LM_eq, ), dtype=ct.c_double, order='F') # Lambda_dot = np.zeros((num_LM_eq,), dtype=ct.c_double, order='F') Dq_old = 0. Dq = np.zeros((self.sys_size, )) for iLoadStep in range(0, self.settings['num_load_steps'].value + 1): iter = -1 # delta = settings.min_delta + 1. converged = False while converged == False: iter += 1 if (iter == self.settings['max_iterations'].value - 1): cout.cout_wrap(("Residual is: %f" % np.amax(np.abs(Dq))), 4) cout.cout_wrap("Static equations did not converge", 4) break MB_K, MB_Q = self.assembly_MB_eq_system( MB_beam, MB_tstep, Lambda, MBdict, iLoadStep) Dq = np.linalg.solve(MB_K, -MB_Q) # Dq *= 0.7 q += Dq mb.state2disp(q, dqdt, dqddt, MB_beam, MB_tstep) if (iter > 0): if (np.amax(np.abs(Dq)) < Dq_old): converged = True if iter == 0: Dq_old = np.amax(np.array([1., np.amax( np.abs(Dq))])) * self.settings['min_delta'].value lagrangeconstraints.postprocess(self.lc_list, MB_beam, MB_tstep, "static") self.compute_forces_constraints(MB_beam, MB_tstep, Lambda) if self.settings['gravity_on']: for ibody in range(len(MB_beam)): xbeamlib.cbeam3_correct_gravity_forces(MB_beam[ibody], MB_tstep[ibody], self.settings) mb.merge_multibody(MB_tstep, MB_beam, self.data.structure, structural_step, MBdict, 0.) # # Initialize # q = np.zeros((self.sys_size + num_LM_eq,), dtype=ct.c_double, order='F') # dqdt = np.zeros((self.sys_size + num_LM_eq,), dtype=ct.c_double, order='F') # dqddt = np.zeros((self.sys_size + num_LM_eq,), dtype=ct.c_double, order='F') # # # Predictor step # mb.disp2state(MB_beam, MB_tstep, q, dqdt, dqddt) # # q += dt*dqdt + (0.5 - self.beta)*dt*dt*dqddt # dqdt += (1.0 - self.gamma)*dt*dqddt # dqddt = np.zeros((self.sys_size + num_LM_eq,), dtype=ct.c_double, order='F') # if not num_LM_eq == 0: # Lambda = q[-num_LM_eq:].astype(dtype=ct.c_double, copy=True, order='F') # Lambda_dot = dqdt[-num_LM_eq:].astype(dtype=ct.c_double, copy=True, order='F') # else: # Lambda = 0 # Lambda_dot = 0 # # # Newmark-beta iterations # old_Dq = 1.0 # LM_old_Dq = 1.0 # # converged = False # for iter in range(self.settings['max_iterations'].value): # # Check if the maximum of iterations has been reached # if (iter == self.settings['max_iterations'].value - 1): # print('Solver did not converge in ', iter, ' iterations.') # print('res = ', res) # print('LM_res = ', LM_res) # import pdb; pdb.set_trace() # break # # # Update positions and velocities # mb.state2disp(q, dqdt, dqddt, MB_beam, MB_tstep) # MB_Asys, MB_Q = self.assembly_MB_eq_system(MB_beam, MB_tstep, self.data.ts, dt, Lambda, Lambda_dot, MBdict) # # # Compute the correction # # ADC next line not necessary # # Dq = np.zeros((self.sys_size+num_LM_eq,), dtype=ct.c_double, order='F') # # MB_Asys_balanced, T = scipy.linalg.matrix_balance(MB_Asys) # # invT = np.matrix(T).I # # MB_Q_balanced = np.dot(invT, MB_Q).T # # Dq = np.linalg.solve(MB_Asys, -MB_Q) # # least squares solver # # Dq = np.linalg.lstsq(np.dot(MB_Asys_balanced, invT), -MB_Q_balanced, rcond=None)[0] # # # Evaluate convergence # if (iter > 0): # res = np.max(np.abs(Dq[0:self.sys_size]))/old_Dq # if not num_LM_eq == 0: # LM_res = np.max(np.abs(Dq[self.sys_size:self.sys_size+num_LM_eq]))/LM_old_Dq # else: # LM_res = 0.0 # if (res < self.settings['min_delta'].value) and (LM_res < self.settings['min_delta'].value*1e-2): # converged = True # # # Compute variables from previous values and increments # # TODO:decide If I want other way of updating lambda # # this for least sq # # q[:, np.newaxis] += Dq # # dqdt[:, np.newaxis] += self.gamma/(self.beta*dt)*Dq # # dqddt[:, np.newaxis] += 1.0/(self.beta*dt*dt)*Dq # # # this for direct solver # q += Dq # dqdt += self.gamma/(self.beta*dt)*Dq # dqddt += 1.0/(self.beta*dt*dt)*Dq # # if not num_LM_eq == 0: # Lambda = q[-num_LM_eq:].astype(dtype=ct.c_double, copy=True, order='F') # Lambda_dot = dqdt[-num_LM_eq:].astype(dtype=ct.c_double, copy=True, order='F') # else: # Lambda = 0 # Lambda_dot = 0 # # if converged: # break # # if iter == 0: # old_Dq = np.max(np.abs(Dq[0:self.sys_size])) # if old_Dq < 1.0: # old_Dq = 1.0 # if num_LM_eq: # LM_old_Dq = np.max(np.abs(Dq[self.sys_size:self.sys_size+num_LM_eq])) # else: # LM_old_Dq = 1.0 # # mb.state2disp(q, dqdt, dqddt, MB_beam, MB_tstep) # # end: comment time stepping # # # End of Newmark-beta iterations # self.integrate_position(MB_beam, MB_tstep, dt) # # lagrangeconstraints.postprocess(self.lc_list, MB_beam, MB_tstep, MBdict, "dynamic") # lagrangeconstraints.postprocess(self.lc_list, MB_beam, MB_tstep, "dynamic") # self.compute_forces_constraints(MB_beam, MB_tstep, self.data.ts, dt, Lambda, Lambda_dot) # if self.settings['gravity_on']: # for ibody in range(len(MB_beam)): # xbeamlib.cbeam3_correct_gravity_forces(MB_beam[ibody], MB_tstep[ibody], self.settings) # mb.merge_multibody(MB_tstep, MB_beam, self.data.structure, structural_step, MBdict, dt) # structural_step.q[:] = q[:self.sys_size].copy() # structural_step.dqdt[:] = dqdt[:self.sys_size].copy() # structural_step.dqddt[:] = dqddt[:self.sys_size].copy() return self.data
def print_available_solvers(): cout.cout_wrap('The available solvers on this session are:', 2) for name, i_solver in dict_of_solvers.items(): cout.cout_wrap('%s ' % i_solver.solver_id, 2)
def solver_wrapper(x, x_info, solver_data, i_dim=-1): if solver_data.settings['print_info']: cout.cout_wrap('x = ' + str(x), 1) # print('x = ', x) alpha = x[x_info['i_alpha']] beta = x[x_info['i_beta']] roll = x[x_info['i_roll']] # change input data solver_data.data.structure.timestep_info[ solver_data.data.ts] = solver_data.data.structure.ini_info.copy() tstep = solver_data.data.structure.timestep_info[solver_data.data.ts] aero_tstep = solver_data.data.aero.timestep_info[solver_data.data.ts] orientation_quat = algebra.euler2quat(np.array([roll, alpha, beta])) tstep.quat[:] = orientation_quat # control surface deflection for i_cs in range(len(x_info['i_control_surfaces'])): solver_data.data.aero.aero_dict['control_surface_deflection'][ x_info['control_surfaces_id'][i_cs]] = x[ x_info['i_control_surfaces'][i_cs]] # thrust input tstep.steady_applied_forces[:] = 0.0 try: x_info['special_case'] except KeyError: for i_thrust in range(len(x_info['i_thrust'])): thrust = x[x_info['i_thrust'][i_thrust]] i_node = x_info['thrust_nodes'][i_thrust] solver_data.data.structure.ini_info.steady_applied_forces[ i_node, 0:3] = thrust * x_info['thrust_direction'][i_thrust] else: if x_info['special_case'] == 'differential_thrust': base_thrust = x[x_info['i_base_thrust']] pos_thrust = base_thrust * (1.0 + x[x_info['i_differential_parameter']]) neg_thrust = -base_thrust * (1.0 - x[x_info['i_differential_parameter']]) for i_base_node in x_info['base_thrust_nodes']: solver_data.data.structure.ini_info.steady_applied_forces[ i_base_node, 1] = base_thrust for i_pos_diff_node in x_info['positive_thrust_nodes']: solver_data.data.structure.ini_info.steady_applied_forces[ i_pos_diff_node, 1] = pos_thrust for i_neg_diff_node in x_info['negative_thrust_nodes']: solver_data.data.structure.ini_info.steady_applied_forces[ i_neg_diff_node, 1] = neg_thrust # run the solver solver_data.solver.run() # extract resultants forces, moments = solver_data.solver.extract_resultants() totals = np.zeros((6, )) totals[0:3] = forces totals[3:6] = moments if solver_data.settings['print_info']: cout.cout_wrap(' forces = ' + str(totals), 1) # print('total forces = ', totals) # try: # totals += x[x_info['i_none']] # except KeyError: # pass # return resultant forces and moments # return np.linalg.norm(totals) if i_dim >= 0: return totals[i_dim] elif i_dim == -1: # return [np.sum(totals[0:3]**2), np.sum(totals[4:6]**2)] return totals elif i_dim == -2: coeffs = np.array([1.0, 1.0, 1.0, 2, 2, 2]) # print('return = ', np.dot(coeffs*totals, coeffs*totals)) if solver_data.settings['print_info']: cout.cout_wrap( ' val = ' + str(np.dot(coeffs * totals, coeffs * totals)), 1) return np.dot(coeffs * totals, coeffs * totals)
def output_info(self): cout.cout_wrap( 'The aerodynamic grid contains %u surfaces' % self.n_surf, 1) for i_surf in range(self.n_surf): cout.cout_wrap( ' Surface %u, M=%u, N=%u' % (i_surf, self.aero_dimensions[i_surf, 0], self.aero_dimensions[i_surf, 1]), 1) cout.cout_wrap(' Wake %u, M=%u, N=%u' % (i_surf, self.aero_dimensions_star[i_surf, 0], self.aero_dimensions_star[i_surf, 1])) cout.cout_wrap( ' In total: %u bound panels' % (sum(self.aero_dimensions[:, 0] * self.aero_dimensions[:, 1]))) cout.cout_wrap(' In total: %u wake panels' % (sum(self.aero_dimensions_star[:, 0] * self.aero_dimensions_star[:, 1]))) cout.cout_wrap( ' Total number of panels = %u' % (sum(self.aero_dimensions[:, 0] * self.aero_dimensions[:, 1]) + sum(self.aero_dimensions_star[:, 0] * self.aero_dimensions_star[:, 1])))
def mode_sign_convention(bocos, eigenvectors, rigid_body_motion=False, use_euler=False): """ When comparing against different cases, it is important that the modes share a common sign convention. In this case, modes will be arranged such that the z-coordinate of the first free end is positive. If the z-coordinate is 0, then the y-coordinate is forced to be positive, then x, followed by the CRV in y, x and z. Returns: np.ndarray: Eigenvectors following the aforementioned sign convention. """ if use_euler: num_rigid_modes = 9 else: num_rigid_modes = 10 if rigid_body_motion: eigenvectors = order_rigid_body_modes(eigenvectors, use_euler) # A frame reference z_coord = -num_rigid_modes + 2 y_coord = -num_rigid_modes + 1 x_coord = -num_rigid_modes + 0 mz_coord = -num_rigid_modes + 5 my_coord = -num_rigid_modes + 4 mx_coord = -num_rigid_modes + 3 else: first_free_end_node = np.where(bocos == -1)[0][0] z_coord = 6 * (first_free_end_node - 1) + 2 y_coord = 6 * (first_free_end_node - 1) + 1 x_coord = 6 * (first_free_end_node - 1) + 0 my_coord = 6 * (first_free_end_node - 1) + 4 mz_coord = 6 * (first_free_end_node - 1) + 5 mx_coord = 6 * (first_free_end_node - 1) + 3 for i in range(0, eigenvectors.shape[1]): if np.abs(eigenvectors[z_coord, i]) > 1e-8: eigenvectors[:, i] = np.sign(eigenvectors[z_coord, i]) * eigenvectors[:, i] elif np.abs(eigenvectors[y_coord, i]) > 1e-8: eigenvectors[:, i] = np.sign(eigenvectors[y_coord, i]) * eigenvectors[:, i] elif np.abs(eigenvectors[x_coord, i]) > 1e-8: eigenvectors[:, i] = np.sign(eigenvectors[x_coord, i]) * eigenvectors[:, i] elif np.abs(eigenvectors[my_coord, i]) > 1e-8: eigenvectors[:, i] = np.sign(eigenvectors[my_coord, i]) * eigenvectors[:, i] elif np.abs(eigenvectors[mx_coord, i]) > 1e-8: eigenvectors[:, i] = np.sign(eigenvectors[mx_coord, i]) * eigenvectors[:, i] elif np.abs(eigenvectors[mz_coord, i]) > 1e-8: eigenvectors[:, i] = np.sign(eigenvectors[mz_coord, i]) * eigenvectors[:, i] else: if rigid_body_motion: if not np.max(np.abs(eigenvectors[-num_rigid_modes + 6:, i]) ) == 1.0: # orientation mode, either euler/quat cout.cout_wrap( 'Implementing mode sign convention. Mode {:g} component at the A frame is 0.' .format(i), 3) else: # cout.cout_wrap('Mode component at the first free end (node {:g}) is 0.'.format(first_free_end_node), 3) # this will be the case for symmetric clamped structures, where modes will be present for the left and # right wings. Method should be called again when symmetric modes are removed. pass return eigenvectors