def test_rotation_about_axis(self): """ Tests the rotations about the cartesian axes """ i = np.array([1, 0, 0]) j = np.array([0, 1, 0]) k = np.array([0, 0, 1]) angle = 90 * np.pi / 180 # Testing rotations about the x axis - rotation3d_x out = algebra.rotation3d_x(angle).dot(j) for ax in range(3): self.assertAlmostEqual(out[ax], k[ax]) out = algebra.rotation3d_x(angle).dot(k) for ax in range(3): self.assertAlmostEqual(out[ax], -j[ax]) # Testing rotations about the y axis - rotation3d_y out = algebra.rotation3d_y(angle).dot(i) for ax in range(3): self.assertAlmostEqual(out[ax], -k[ax]) out = algebra.rotation3d_y(angle).dot(k) for ax in range(3): self.assertAlmostEqual(out[ax], i[ax]) # Testing rotations about the z axis - rotation3d_z out = algebra.rotation3d_z(angle).dot(i) for ax in range(3): self.assertAlmostEqual(out[ax], j[ax]) out = algebra.rotation3d_z(angle).dot(j) for ax in range(3): self.assertAlmostEqual(out[ax], -i[ax])
def generate_strip(node_info, airfoil_db, aligned_grid, orientation_in=np.array([1, 0, 0]), calculate_zeta_dot=False): """ Returns a strip of panels in ``A`` frame of reference, it has to be then rotated to simulate angles of attack, etc """ strip_coordinates_a_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) strip_coordinates_b_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) zeta_dot_a_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) # airfoil coordinates # we are going to store everything in the x-z plane of the b # FoR, so that the transformation Cab rotates everything in place. if node_info['M_distribution'] == 'uniform': strip_coordinates_b_frame[1, :] = np.linspace(0.0, 1.0, node_info['M'] + 1) elif node_info['M_distribution'] == '1-cos': domain = np.linspace(0, 1.0, node_info['M'] + 1) strip_coordinates_b_frame[1, :] = 0.5 * (1.0 - np.cos(domain * np.pi)) elif node_info['M_distribution'].lower() == 'user_defined': # strip_coordinates_b_frame[1, :-1] = np.linspace(0.0, 1.0 - node_info['last_panel_length'], node_info['M']) # strip_coordinates_b_frame[1,-1] = 1. strip_coordinates_b_frame[ 1, :] = node_info['user_defined_m_distribution'] else: raise NotImplemented('M_distribution is ' + node_info['M_distribution'] + ' and it is not yet supported') strip_coordinates_b_frame[2, :] = airfoil_db[node_info['airfoil']]( strip_coordinates_b_frame[1, :]) # elastic axis correction for i_M in range(node_info['M'] + 1): strip_coordinates_b_frame[1, i_M] -= node_info['eaxis'] # chord_line_b_frame = strip_coordinates_b_frame[:, -1] - strip_coordinates_b_frame[:, 0] cs_velocity = np.zeros_like(strip_coordinates_b_frame) # control surface deflection if node_info['control_surface'] is not None: b_frame_hinge_coords = strip_coordinates_b_frame[:, node_info[ 'M'] - node_info['control_surface']['chord']] # support for different hinge location for fully articulated control surfaces if node_info['control_surface']['hinge_coords'] is not None: # make sure the hinge coordinates are only applied when M == cs_chord if not node_info['M'] - node_info['control_surface']['chord'] == 0: node_info['control_surface']['hinge_coords'] = None else: b_frame_hinge_coords = node_info['control_surface'][ 'hinge_coords'] for i_M in range( node_info['M'] - node_info['control_surface']['chord'], node_info['M'] + 1): relative_coords = strip_coordinates_b_frame[:, i_M] - b_frame_hinge_coords # rotate the control surface relative_coords = np.dot( algebra.rotation3d_x( -node_info['control_surface']['deflection']), relative_coords) # deflection velocity try: cs_velocity[:, i_M] += np.cross( np.array([ -node_info['control_surface']['deflection_dot'], 0.0, 0.0 ]), relative_coords) except KeyError: pass # restore coordinates relative_coords += b_frame_hinge_coords # substitute with new coordinates strip_coordinates_b_frame[:, i_M] = relative_coords # chord scaling strip_coordinates_b_frame *= node_info['chord'] # twist transformation (rotation around x_b axis) if np.abs(node_info['twist']) > 1e-6: Ctwist = algebra.rotation3d_x(node_info['twist']) else: Ctwist = np.eye(3) # Cab transformation Cab = algebra.crv2rotation(node_info['beam_psi']) rot_angle = algebra.angle_between_vectors_sign(orientation_in, Cab[:, 1], Cab[:, 2]) if np.sign(np.dot(orientation_in, Cab[:, 1])) >= 0: rot_angle += 0.0 else: rot_angle += -2 * np.pi Crot = algebra.rotation3d_z(-rot_angle) c_sweep = np.eye(3) if np.abs(node_info['sweep']) > 1e-6: c_sweep = algebra.rotation3d_z(node_info['sweep']) # transformation from beam to beam prime (with sweep and twist) for i_M in range(node_info['M'] + 1): strip_coordinates_b_frame[:, i_M] = np.dot( c_sweep, np.dot(Crot, np.dot(Ctwist, strip_coordinates_b_frame[:, i_M]))) strip_coordinates_a_frame[:, i_M] = np.dot( Cab, strip_coordinates_b_frame[:, i_M]) cs_velocity[:, i_M] = np.dot(Cab, cs_velocity[:, i_M]) # zeta_dot if calculate_zeta_dot: # velocity due to pos_dot for i_M in range(node_info['M'] + 1): zeta_dot_a_frame[:, i_M] += node_info['pos_dot'] # velocity due to psi_dot omega_a = algebra.crv_dot2omega(node_info['beam_psi'], node_info['psi_dot']) for i_M in range(node_info['M'] + 1): zeta_dot_a_frame[:, i_M] += (np.dot(algebra.skew(omega_a), strip_coordinates_a_frame[:, i_M])) # control surface deflection velocity contribution try: if node_info['control_surface'] is not None: node_info['control_surface']['deflection_dot'] for i_M in range(node_info['M'] + 1): zeta_dot_a_frame[:, i_M] += cs_velocity[:, i_M] except KeyError: pass else: zeta_dot_a_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) # add node coords for i_M in range(node_info['M'] + 1): strip_coordinates_a_frame[:, i_M] += node_info['beam_coord'] # add quarter-chord disp delta_c = (strip_coordinates_a_frame[:, -1] - strip_coordinates_a_frame[:, 0]) / node_info['M'] if node_info['M_distribution'] == 'uniform': for i_M in range(node_info['M'] + 1): strip_coordinates_a_frame[:, i_M] += 0.25 * delta_c else: warnings.warn( "No quarter chord disp of grid for non-uniform grid distributions implemented", UserWarning) # rotation from a to g for i_M in range(node_info['M'] + 1): strip_coordinates_a_frame[:, i_M] = np.dot( node_info['cga'], strip_coordinates_a_frame[:, i_M]) zeta_dot_a_frame[:, i_M] = np.dot(node_info['cga'], zeta_dot_a_frame[:, i_M]) return strip_coordinates_a_frame, zeta_dot_a_frame
def generate_strip(node_info, airfoil_db, aligned_grid, orientation_in=np.array([1, 0, 0])): """ Returns a strip in "a" frame of reference, it has to be then rotated to simulate angles of attack, etc :param node_info: :param airfoil_db: :param aligned_grid: :param orientation_in: :return: """ strip_coordinates_a_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) strip_coordinates_b_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) # airfoil coordinates # we are going to store everything in the x-z plane of the b # FoR, so that the transformation Cab rotates everything in place. if node_info['M_distribution'] == 'uniform': strip_coordinates_b_frame[1, :] = np.linspace(0.0, 1.0, node_info['M'] + 1) elif node_info['M_distribution'] == '1-cos': domain = np.linspace(0, 1.0, node_info['M'] + 1) strip_coordinates_b_frame[1, :] = 0.5 * (1.0 - np.cos(domain * np.pi)) else: raise NotImplemented('M_distribution is ' + node_info['M_distribution'] + ' and it is not yet supported') strip_coordinates_b_frame[2, :] = airfoil_db[node_info['airfoil']]( strip_coordinates_b_frame[1, :]) # elastic axis correction for i_M in range(node_info['M'] + 1): strip_coordinates_b_frame[1, i_M] -= node_info['eaxis'] # chord scaling strip_coordinates_b_frame *= node_info['chord'] # twist transformation (rotation around x_b axis) if np.abs(node_info['twist']) > 1e-6: Ctwist = algebra.rotation3d_x(node_info['twist']) else: Ctwist = np.eye(3) # Cab transformation Cab = algebra.crv2rot(node_info['beam_psi']) # sweep angle correction # angle between orientation_in and chord line chord_line_b_frame = strip_coordinates_b_frame[:, -1] - strip_coordinates_b_frame[:, 0] chord_line_a_frame = np.dot(Cab, chord_line_b_frame) sweep_angle = algebra.angle_between_vectors_sign(orientation_in, chord_line_a_frame, np.array([0, 0, 1])) # rotation matrix Csweep = algebra.rotation3d_z(-sweep_angle) # transformation from beam to aero for i_M in range(node_info['M'] + 1): strip_coordinates_a_frame[:, i_M] = np.dot( Cab, np.dot(Csweep, np.dot(Ctwist, strip_coordinates_b_frame[:, i_M]))) # add node coords for i_M in range(node_info['M'] + 1): strip_coordinates_a_frame[:, i_M] += node_info['beam_coord'] return strip_coordinates_a_frame
def generate_strip(node_info, airfoil_db, aligned_grid, orientation_in=np.array([1, 0, 0]), calculate_zeta_dot = False): """ Returns a strip in "a" frame of reference, it has to be then rotated to simulate angles of attack, etc :param node_info: :param airfoil_db: :param aligned_grid: :param orientation_in: :return: """ strip_coordinates_a_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) strip_coordinates_b_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) # airfoil coordinates # we are going to store everything in the x-z plane of the b # FoR, so that the transformation Cab rotates everything in place. if node_info['M_distribution'] == 'uniform': strip_coordinates_b_frame[1, :] = np.linspace(0.0, 1.0, node_info['M'] + 1) elif node_info['M_distribution'] == '1-cos': domain = np.linspace(0, 1.0, node_info['M'] + 1) strip_coordinates_b_frame[1, :] = 0.5*(1.0 - np.cos(domain*np.pi)) else: raise NotImplemented('M_distribution is ' + node_info['M_distribution'] + ' and it is not yet supported') strip_coordinates_b_frame[2, :] = airfoil_db[node_info['airfoil']]( strip_coordinates_b_frame[1, :]) # elastic axis correction for i_M in range(node_info['M'] + 1): strip_coordinates_b_frame[1, i_M] -= node_info['eaxis'] chord_line_b_frame = strip_coordinates_b_frame[:, -1] - strip_coordinates_b_frame[:, 0] # control surface deflection if node_info['control_surface'] is not None: b_frame_hinge_coords = strip_coordinates_b_frame[:, node_info['M'] - node_info['control_surface']['chord']] # support for different hinge location for fully articulated control surfaces if node_info['control_surface']['hinge_coords'] is not None: # make sure the hinge coordinates are only applied when M == cs_chord if not node_info['M'] - node_info['control_surface']['chord'] == 0: cout.cout_wrap('The hinge coordinates parameter is only supported when M == cs_chord') node_info['control_surface']['hinge_coords'] = None else: b_frame_hinge_coords = node_info['control_surface']['hinge_coords'] for i_M in range(node_info['M'] - node_info['control_surface']['chord'], node_info['M'] + 1): relative_coords = strip_coordinates_b_frame[:, i_M] - b_frame_hinge_coords # rotate the control surface relative_coords = np.dot(algebra.rotation3d_x(-node_info['control_surface']['deflection']), relative_coords) # restore coordinates relative_coords += b_frame_hinge_coords # substitute with new coordinates strip_coordinates_b_frame[:, i_M] = relative_coords # chord scaling strip_coordinates_b_frame *= node_info['chord'] # twist transformation (rotation around x_b axis) if np.abs(node_info['twist']) > 1e-6: Ctwist = algebra.rotation3d_x(node_info['twist']) else: Ctwist = np.eye(3) # Cab transformation Cab = algebra.crv2rot(node_info['beam_psi']) rot_angle = algebra.angle_between_vectors_sign(orientation_in, Cab[:, 1], Cab[:, 2]) Crot = algebra.rotation3d_z(-rot_angle) c_sweep = np.eye(3) if np.abs(node_info['sweep']) > 1e-6: c_sweep = algebra.rotation3d_z(node_info['sweep']) # transformation from beam to beam prime (with sweep and twist) for i_M in range(node_info['M'] + 1): strip_coordinates_b_frame[:, i_M] = np.dot(c_sweep, np.dot(Crot, np.dot(Ctwist, strip_coordinates_b_frame[:, i_M]))) strip_coordinates_a_frame[:, i_M] = np.dot(Cab, strip_coordinates_b_frame[:, i_M]) # zeta_dot if calculate_zeta_dot: zeta_dot_a_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) # velocity due to pos_dot for i_M in range(node_info['M'] + 1): zeta_dot_a_frame[:, i_M] += node_info['pos_dot'] # velocity due to psi_dot Omega_b = algebra.crv_dot2Omega(node_info['beam_psi'], node_info['psi_dot']) for i_M in range(node_info['M'] + 1): zeta_dot_a_frame[:, i_M] += ( np.dot(algebra.skew(Omega_b), strip_coordinates_a_frame[:, i_M])) else: zeta_dot_a_frame = np.zeros((3, node_info['M'] + 1), dtype=ct.c_double) # add node coords for i_M in range(node_info['M'] + 1): strip_coordinates_a_frame[:, i_M] += node_info['beam_coord'] # rotation from a to g for i_M in range(node_info['M'] + 1): strip_coordinates_a_frame[:, i_M] = np.dot(node_info['cga'], strip_coordinates_a_frame[:, i_M]) zeta_dot_a_frame[:, i_M] = np.dot(node_info['cga'], zeta_dot_a_frame[:, i_M]) return strip_coordinates_a_frame, zeta_dot_a_frame