def get_triad(self): """ Generates two unit vectors in body FoR that define the local FoR for a beam element. These vectors are calculated using `frame_of_reference_delta` :return: """ # now, calculate tangent vector (and coefficients of the polynomial # fit just in case) tangent, polyfit = algebra.tangent_vector(self.coordinates_def, Element.ordering) normal = np.zeros_like(tangent) binormal = np.zeros_like(tangent) # v_vector is the vector with origin the FoR node and delta # equals frame_of_reference_delta for inode in range(self.n_nodes): v_vector = self.frame_of_reference_delta[inode, :] normal[inode, :] = algebra.unit_vector( np.cross(tangent[inode, :], v_vector)) binormal[inode, :] = -algebra.unit_vector( np.cross(tangent[inode, :], normal[inode, :])) # we apply twist now for inode in range(self.n_nodes): if not self.structural_twist[inode] == 0.0: rotation_mat = algebra.rotation_matrix_around_axis( tangent[inode, :], self.structural_twist[inode]) normal[inode, :] = np.dot(rotation_mat, normal[inode, :]) binormal[inode, :] = np.dot(rotation_mat, binormal[inode, :]) return tangent, binormal, normal
def test_unit_vector(self): """ Tests the routine for normalising vectors :return: """ vector_in = 1 result = algebra.unit_vector(vector_in) self.assertAlmostEqual(np.linalg.norm(result), 1.0, 5) vector_in = 0 result = algebra.unit_vector(vector_in) self.assertAlmostEqual(np.linalg.norm(result), 0.0, 5) vector_in = np.array([1, 0, 0]) result = algebra.unit_vector(vector_in) self.assertAlmostEqual(np.linalg.norm(result), 1.0, 5) vector_in = np.array([2, -1, 1]) result = algebra.unit_vector(vector_in) self.assertAlmostEqual(np.linalg.norm(result), 1.0, 5) vector_in = np.array([1e-8, 0, 0]) result = algebra.unit_vector(vector_in) self.assertAlmostEqual(np.linalg.norm(result), 1e-8, 5) vector_in = 'aa' with self.assertRaises(ValueError): algebra.unit_vector(vector_in)
def change_trim(self, alpha, thrust, thrust_nodes, tail_deflection, tail_cs_index): # self.cleanup_timestep_info() self.data.structure.timestep_info = [] self.data.structure.timestep_info.append( self.data.structure.ini_info.copy()) aero_copy = self.data.aero.timestep_info[-1] self.data.aero.timestep_info = [] self.data.aero.timestep_info.append(aero_copy) self.data.ts = 0 # alpha orientation_quat = algebra.euler2quat(np.array([0.0, alpha, 0.0])) self.data.structure.timestep_info[0].quat[:] = orientation_quat[:] try: self.force_orientation except AttributeError: self.force_orientation = np.zeros((len(thrust_nodes), 3)) for i_node, node in enumerate(thrust_nodes): self.force_orientation[i_node, :] = (algebra.unit_vector( self.data.structure.ini_info.steady_applied_forces[node, 0:3])) # print(self.force_orientation) # thrust # thrust is scaled so that the direction of the forces is conserved # in all nodes. # the `thrust` parameter is the force PER node. # if there are two or more nodes in thrust_nodes, the total forces # is n_nodes_in_thrust_nodes*thrust # thrust forces have to be indicated in structure.ini_info # print(algebra.unit_vector(self.data.structure.ini_info.steady_applied_forces[0, 0:3])*thrust) for i_node, node in enumerate(thrust_nodes): # self.data.structure.ini_info.steady_applied_forces[i_node, 0:3] = ( # algebra.unit_vector(self.data.structure.ini_info.steady_applied_forces[i_node, 0:3])*thrust) self.data.structure.ini_info.steady_applied_forces[node, 0:3] = ( self.force_orientation[i_node, :] * thrust) self.data.structure.timestep_info[0].steady_applied_forces[ node, 0:3] = (self.force_orientation[i_node, :] * thrust) # tail deflection try: self.data.aero.aero_dict['control_surface_deflection'][ tail_cs_index] = tail_deflection except KeyError: raise Exception('This model has no control surfaces') except IndexError: raise Exception( 'The tail control surface index > number of surfaces') # update grid self.aero_solver.update_step()
def normalise_quaternion(tstep): tstep.dqdt[-4:] = algebra.unit_vector(tstep.dqdt[-4:]) tstep.quat = tstep.dqdt[-4:].astype(dtype=ct.c_double, order='F', copy=True)
def polars(data, aero_kstep, structural_kstep, struct_forces, **kwargs): r""" This function corrects the aerodynamic forces from UVLM based on the airfoil polars provided by the user in the aero.h5 file These are the steps needed to correct the forces: * The force coming from UVLM is divided into induced drag (parallel to the incoming flow velocity) and lift (the remaining force). * The angle of attack is computed based on that lift force and the angle of zero lift computed form the airfoil polar and assuming a slope of :math:`2 \pi` * The drag force is computed based on the angle of attack and the polars provided by the user Args: data (:class:`sharpy.PreSharpy`): SHARPy data aero_kstep (:class:`sharpy.utils.datastructures.AeroTimeStepInfo`): Current aerodynamic substep structural_kstep (:class:`sharpy.utils.datastructures.StructTimeStepInfo`): Current structural substep struct_forces (np.array): Array with the aerodynamic forces mapped on the structure in the B frame of reference Keyword Arguments: rho (float): air density correct_lift (bool): correct also lift coefficient according to the polars cd_from_cl (bool): interpolate drag from lift instead of computing the AoA first Returns: np.ndarray: corresponding aerodynamic force at the structural node from the force and moment at a grid vertex """ aerogrid = data.aero beam = data.structure rho = kwargs.get('rho', 1.225) correct_lift = kwargs.get('correct_lift', False) cd_from_cl = kwargs.get('cd_from_cl', False) aero_dict = aerogrid.aero_dict if aerogrid.polars is None: return struct_forces new_struct_forces = np.zeros_like(struct_forces) nnode = struct_forces.shape[0] for inode in range(nnode): new_struct_forces[inode, :] = struct_forces[inode, :].copy() if aero_dict['aero_node'][inode]: ielem, inode_in_elem = beam.node_master_elem[inode] iairfoil = aero_dict['airfoil_distribution'][ielem, inode_in_elem] isurf = aerogrid.struct2aero_mapping[inode][0]['i_surf'] i_n = aerogrid.struct2aero_mapping[inode][0]['i_n'] N = aerogrid.aero_dimensions[isurf, 1] polar = aerogrid.polars[iairfoil] cab = algebra.crv2rotation(structural_kstep.psi[ielem, inode_in_elem, :]) cga = algebra.quat2rotation(structural_kstep.quat) cgb = np.dot(cga, cab) # Deal with the extremes if i_n == 0: node1 = 0 node2 = 1 elif i_n == N: node1 = nnode - 1 node2 = nnode - 2 else: node1 = inode + 1 node2 = inode - 1 # Define the span and the span direction dir_span = 0.5*np.dot(cga, structural_kstep.pos[node1, :] - structural_kstep.pos[node2, :]) span = np.linalg.norm(dir_span) dir_span = algebra.unit_vector(dir_span) # Define the chord and the chord direction dir_chord = aero_kstep.zeta[isurf][:, -1, i_n] - aero_kstep.zeta[isurf][:, 0, i_n] chord = np.linalg.norm(dir_chord) dir_chord = algebra.unit_vector(dir_chord) # Define the relative velocity and its direction urel = (structural_kstep.pos_dot[inode, :] + structural_kstep.for_vel[0:3] + np.cross(structural_kstep.for_vel[3:6], structural_kstep.pos[inode, :])) urel = -np.dot(cga, urel) urel += np.average(aero_kstep.u_ext[isurf][:, :, i_n], axis=1) # uind = uvlmlib.uvlm_calculate_total_induced_velocity_at_points(aero_kstep, # np.array([structural_kstep.pos[inode, :] - np.array([0, 0, 1])]), # structural_kstep.for_pos, # ct.c_uint(8))[0] # print(inode, urel, uind) # urel -= uind dir_urel = algebra.unit_vector(urel) # Force in the G frame of reference force = np.dot(cgb, struct_forces[inode, 0:3]) dir_force = algebra.unit_vector(force) # Coefficient to change from aerodynamic coefficients to forces (and viceversa) coef = 0.5*rho*np.linalg.norm(urel)**2*chord*span # Divide the force in drag and lift drag_force = np.dot(force, dir_urel)*dir_urel lift_force = force - drag_force # Compute the associated lift cl = np.linalg.norm(lift_force)/coef cd_sharpy = np.linalg.norm(drag_force)/coef if cd_from_cl: # Compute the drag from the lift cd, cm = polar.get_cdcm_from_cl(cl) else: # Compute the angle of attack assuming that UVLM gives a 2pi polar aoa_deg_2pi = polar.get_aoa_deg_from_cl_2pi(cl) # Compute the coefficients assocaited to that angle of attack cl_new, cd, cm = polar.get_coefs(aoa_deg_2pi) # print(cl, cl_new) if correct_lift: cl = cl_new # Recompute the forces based on the coefficients lift_force = cl*algebra.unit_vector(lift_force)*coef drag_force += cd*dir_urel*coef force = lift_force + drag_force new_struct_forces[inode, 0:3] = np.dot(cgb.T, force) return new_struct_forces
def xbeam_solv_disp2state(beam, tstep): numdof = beam.num_dof.value cbeam3_solv_disp2state(beam, tstep) tstep.dqdt[numdof:numdof+6] = tstep.for_vel tstep.dqddt[numdof:numdof+6] = tstep.for_acc tstep.dqdt[numdof+6:] = algebra.unit_vector(tstep.quat)
def xbeam_solv_state2disp(beam, tstep): numdof = beam.num_dof.value cbeam3_solv_state2disp(beam, tstep) tstep.for_vel = tstep.dqdt[numdof:numdof+6].astype(dtype=ct.c_double, order='F', copy=True) tstep.for_acc = tstep.dqddt[numdof:numdof+6].astype(dtype=ct.c_double, order='F', copy=True) tstep.quat = algebra.unit_vector(tstep.dqdt[numdof+6:]).astype(dtype=ct.c_double, order='F', copy=True)