Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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)