Esempio n. 1
0
def eval(Zeta00, Zeta01, Zeta02, Zeta03, Uc):
    """
    Returns a 4 x 3 array, containing the derivative of Wnc*Uc w.r.t the panel
    vertices coordinates.
    """

    R02 = Zeta02 - Zeta00
    R13 = Zeta03 - Zeta01

    crR02R13 = algebra.cross3(R02, R13)
    norm_crR02R13 = np.linalg.norm(crR02R13)
    cub_crR02R13 = norm_crR02R13**3

    Acr = algebra.cross3(crR02R13, R13)
    Bcr = algebra.cross3(crR02R13, R02)
    Cdot = np.dot(crR02R13, Uc)

    uc_x, uc_y, uc_z = Uc
    r02_x, r02_y, r02_z = R02
    r13_x, r13_y, r13_z = R13
    crR13Uc_x, crR13Uc_y, crR13Uc_z = algebra.cross3(R13, Uc)
    crR02Uc_x, crR02Uc_y, crR02Uc_z = algebra.cross3(R02, Uc)
    crR02R13_x, crR02R13_y, crR02R13_z = crR02R13
    Acr_x, Acr_y, Acr_z = Acr
    Bcr_x, Bcr_y, Bcr_z = Bcr

    # dUnorm_dR.shape=(2,3)
    dUnorm_dR = np.array(
        [[
            Acr_x * Cdot / cub_crR02R13 + crR13Uc_x / norm_crR02R13,
            Acr_y * Cdot / cub_crR02R13 + crR13Uc_y / norm_crR02R13,
            Acr_z * Cdot / cub_crR02R13 + crR13Uc_z / norm_crR02R13
        ],
         [
             -Bcr_x * Cdot / cub_crR02R13 - crR02Uc_x / norm_crR02R13,
             -Bcr_y * Cdot / cub_crR02R13 - crR02Uc_y / norm_crR02R13,
             -Bcr_z * Cdot / cub_crR02R13 - crR02Uc_z / norm_crR02R13
         ]])

    # Allocate
    dUnorm_dZeta = np.zeros((4, 3))
    for vv in range(4):  # loop through panel vertices
        for cc_zeta in range(3):  # loop panel vertices component
            for rr in range(2):  # loop segments R02, R13
                for cc_rvec in range(3):  # loop segment component
                    dUnorm_dZeta[vv, cc_zeta] += \
                        dUnorm_dR[rr, cc_rvec] * dR_dZeta[vv, cc_zeta, rr, cc_rvec]

    return dUnorm_dZeta
Esempio n. 2
0
    def extract_resultants(self, tstep=None):
        if tstep is None:
            tstep = self.data.structure.timestep_info[self.data.ts]
        applied_forces = self.data.structure.nodal_b_for_2_a_for(tstep.steady_applied_forces,
                                                                 tstep)

        applied_forces_copy = applied_forces.copy()
        gravity_forces_copy = tstep.gravity_forces.copy()
        for i_node in range(self.data.structure.num_node):
            applied_forces_copy[i_node, 3:6] += algebra.cross3(tstep.pos[i_node, :],
                                                               applied_forces_copy[i_node, 0:3])
            gravity_forces_copy[i_node, 3:6] += algebra.cross3(tstep.pos[i_node, :],
                                                               gravity_forces_copy[i_node, 0:3])

        totals = np.sum(applied_forces_copy + gravity_forces_copy, axis=0)
        return totals[0:3], totals[3:6]
Esempio n. 3
0
def efficiency_local_aero2struct_forces(local_aero_forces, chi_g, cbg, force_efficiency, moment_efficiency, i_elem,
                                        i_local_node):
    r"""
    Maps the local aerodynamic forces at a given vertex to its corresponding structural node, introducing user-defined
    efficiency and constant value factors.

    .. math::
        \mathbf{f}_{struct}^B &= \varepsilon^f_0 C^{BG}\mathbf{f}_{i,aero}^G + \varepsilon^f_1\\
        \mathbf{m}_{struct}^B &= \varepsilon^m_0 (C^{BG}(\mathbf{m}_{i,aero}^G +
        \tilde{\boldsymbol{\zeta}}^G\mathbf{f}_{i, aero}^G)) + \varepsilon^m_1

    Args:
        local_aero_forces (np.ndarray): aerodynamic forces and moments at a grid vertex
        chi_g (np.ndarray): vector between grid vertex and structural node in inertial frame
        cbg (np.ndarray): transformation matrix between inertial and body frames of reference
        force_efficiency (np.ndarray): force efficiency matrix for all structural elements. Its size is ``n_elem x n_node_elem x 2 x 3``
        moment_efficiency (np.ndarray): moment efficiency matrix for all structural elements. Its size is ``n_elem x n_node_elem x 2 x 3``
        i_elem (int): element index
        i_local_node (int): local node index within element

    Returns:
         np.ndarray: corresponding aerodynamic force at the structural node from the force and moment at a grid vertex
    """
    local_struct_forces = np.zeros(6)
    local_struct_forces[0:3] += np.dot(cbg, local_aero_forces[0:3]) * force_efficiency[i_elem, i_local_node, 0] # element wise multiplication
    local_struct_forces[0:3] += force_efficiency[i_elem, i_local_node, 1]
    local_struct_forces[3:6] += np.dot(cbg, local_aero_forces[3:6]) * moment_efficiency[i_elem, i_local_node, 0]
    local_struct_forces[3:6] += np.dot(cbg, algebra.cross3(chi_g, local_aero_forces[0:3])) * moment_efficiency[i_elem, i_local_node, 0]
    local_struct_forces[3:6] += moment_efficiency[i_elem, i_local_node, 1]

    return local_struct_forces
Esempio n. 4
0
def biot_panel_fast(zetaC, ZetaPanel, gamma=1.0):
    """
    Induced velocity over point ZetaC of a panel of vertices coordinates
    ZetaPanel and circulaiton gamma, where:
        ZetaPanel.shape=(4,3)=[vertex local number, (x,y,z) component]
    """

    Cfact = cfact_biot * gamma
    q = np.zeros((3, ))

    R_list = zetaC - ZetaPanel
    Runit_list = [R_list[ii] / algebra.norm3d(R_list[ii]) for ii in svec]

    for aa, bb in LoopPanel:

        RAB = ZetaPanel[bb, :] - ZetaPanel[aa, :]  # segment vector
        Vcr = algebra.cross3(R_list[aa], R_list[bb])
        vcr2 = np.dot(Vcr, Vcr)
        if vcr2 < (VORTEX_RADIUS_SQ * algebra.normsq3d(RAB)):
            continue

        q += ((Cfact / vcr2) *
              np.dot(RAB, Runit_list[aa] - Runit_list[bb])) * Vcr

    return q
Esempio n. 5
0
def local_aero2struct_forces(local_aero_forces, chi_g, cbg, force_efficiency=None, moment_efficiency=None, i_elem=None,
                             i_local_node=None):
    r"""
    Maps the local aerodynamic forces at a given vertex to its corresponding structural node.

    .. math::
        \mathbf{f}_{struct}^B &= C^{BG}\mathbf{f}_{i,aero}^G\\
        \mathbf{m}_{struct}^B &= C^{BG}(\mathbf{m}_{i,aero}^G +
        \tilde{\boldsymbol{\zeta}}^G\mathbf{f}_{i, aero}^G)

    Args:
        local_aero_forces (np.ndarray): aerodynamic forces and moments at a grid vertex
        chi_g (np.ndarray): vector between grid vertex and structural node in inertial frame
        cbg (np.ndarray): transformation matrix between inertial and body frames of reference
        force_efficiency (np.ndarray): Unused. See :func:`~sharpy.aero.utils.mapping.efficiency_local_aero2struct_forces`.
        moment_efficiency (np.ndarray): Unused. See :func:`~sharpy.aero.utils.mapping.efficiency_local_aero2struct_forces`.
        i_elem (int):

    Returns:
         np.ndarray: corresponding aerodynamic force at the structural node from the force and moment at a grid vertex

    """
    local_struct_forces = np.zeros(6)
    local_struct_forces[0:3] += np.dot(cbg, local_aero_forces[0:3])
    local_struct_forces[3:6] += np.dot(cbg, local_aero_forces[3:6])
    local_struct_forces[3:6] += np.dot(cbg, algebra.cross3(chi_g, local_aero_forces[0:3]))

    return local_struct_forces
Esempio n. 6
0
def eval_seg_comp_loop(DerP, DerA, DerB, ZetaP, ZetaA, ZetaB, gamma_seg,
                       vortex_radius):
    """
    Derivative of induced velocity Q w.r.t. collocation and segment coordinates
    in format:
        [ (x,y,z) of Q, (x,y,z) of Zeta ]
    Warning: function optimised for performance. Variables are scaled during the
    execution.
    """

    vortex_radius_sq = vortex_radius * vortex_radius
    Cfact = cfact_biot * gamma_seg

    RA = ZetaP - ZetaA
    RB = ZetaP - ZetaB
    RAB = ZetaB - ZetaA
    Vcr = algebra.cross3(RA, RB)
    vcr2 = np.dot(Vcr, Vcr)

    # numerical radious
    if vcr2 < (vortex_radius_sq * algebra.normsq3d(RAB)):
        return

    ### other constants
    ra1, rb1 = algebra.norm3d(RA), algebra.norm3d(RB)
    rainv = 1. / ra1
    rbinv = 1. / rb1
    Tv = RA * rainv - RB * rbinv
    dotprod = np.dot(RAB, Tv)

    ### --------------------------------------------- cross-product derivatives
    # lower triangular part only
    vcr2inv = 1. / vcr2
    vcr4inv = vcr2inv * vcr2inv
    diag_fact = Cfact * vcr2inv * dotprod
    off_fact = -2. * Cfact * vcr4inv * dotprod
    Dvcross = [[diag_fact + off_fact * Vcr[0]**2],
               [off_fact * Vcr[0] * Vcr[1], diag_fact + off_fact * Vcr[1]**2],
               [
                   off_fact * Vcr[0] * Vcr[2], off_fact * Vcr[1] * Vcr[2],
                   diag_fact + off_fact * Vcr[2]**2
               ]]

    ### ------------------------------------------ difference terms derivatives
    Vsc = Vcr * vcr2inv * Cfact
    Ddiff = np.array([RAB * Vsc[0], RAB * Vsc[1], RAB * Vsc[2]])
    dQ_dRAB = np.array([Tv * Vsc[0], Tv * Vsc[1], Tv * Vsc[2]])

    ### ---------------------------------------------- Final assembly (crucial)
    # ps: calling Dvcross_by_skew3d does not slow down execution.

    dQ_dRA = Dvcross_by_skew3d(Dvcross, -RB) \
             + np.dot(Ddiff, der_runit(RA, rainv, -rainv ** 3))
    dQ_dRB = Dvcross_by_skew3d(Dvcross, RA) \
             - np.dot(Ddiff, der_runit(RB, rbinv, -rbinv ** 3))

    DerP += dQ_dRA + dQ_dRB  # w.r.t. P
    DerA -= dQ_dRAB + dQ_dRA  # w.r.t. A
    DerB += dQ_dRAB - dQ_dRB  # w.r.t. B
Esempio n. 7
0
def joukovski_qs_segment(zetaA, zetaB, v_mid, gamma=1.0, fact=0.5):
    """
    Joukovski force over vetices A and B produced by the segment A->B.
    The factor fact allows to compute directly the contribution over the
    vertices A and B (e.g. 0.5) or include DENSITY.
    """

    rab = zetaB - zetaA
    fs = algebra.cross3(v_mid, rab)
    gfact = fact * gamma

    return gfact * fs
Esempio n. 8
0
def panel_normal(ZetaPanel):
    """
    return normal of panel with vertex coordinates ZetaPanel, where:
        ZetaPanel.shape=(4,3)
    """

    # build cross-vectors
    r02 = ZetaPanel[2, :] - ZetaPanel[0, :]
    r13 = ZetaPanel[3, :] - ZetaPanel[1, :]

    nvec = algebra.cross3(r02, r13)
    nvec = nvec / algebra.norm3d(nvec)

    return nvec
Esempio n. 9
0
def biot_segment(zetaP, zetaA, zetaB, gamma=1.0):
    """
    Induced velocity of segment A_>B of circulation gamma over point P.
    """

    # differences
    ra = zetaP - zetaA
    rb = zetaP - zetaB
    rab = zetaB - zetaA
    ra_norm, rb_norm = algebra.norm3d(ra), algebra.norm3d(rb)
    vcross = algebra.cross3(ra, rb)
    vcross_sq = np.dot(vcross, vcross)

    # numerical radius
    if vcross_sq < (VORTEX_RADIUS_SQ * algebra.normsq3d(rab)):
        return np.zeros((3, ))

    q = ((cfact_biot * gamma / vcross_sq) * \
         (np.dot(rab, ra) / ra_norm - np.dot(rab, rb) / rb_norm)) * vcross

    return q
Esempio n. 10
0
def aero2struct_force_mapping(aero_forces,
                              struct2aero_mapping,
                              zeta,
                              pos_def,
                              psi_def,
                              master,
                              conn,
                              cag=np.eye(3),
                              aero_dict=None):
    r"""
    Maps the aerodynamic forces at the lattice to the structural nodes

    The aerodynamic forces from the UVLM are always in the inertial ``G`` frame of reference and have to be transformed
    to the body or local ``B`` frame of reference in which the structural forces are defined.

    Since the structural nodes and aerodynamic panels are coincident in a spanwise direction, the aerodynamic forces
    that correspond to a structural node are the summation of the ``M+1`` forces defined at the lattice at that
    spanwise location.

    .. math::
        \mathbf{f}_{struct}^B &= \sum\limits_{i=0}^{m+1}C^{BG}\mathbf{f}_{i,aero}^G \\
        \mathbf{m}_{struct}^B &= \sum\limits_{i=0}^{m+1}C^{BG}(\mathbf{m}_{i,aero}^G +
        \tilde{\boldsymbol{\zeta}}^G\mathbf{f}_{i, aero}^G)

    where :math:`\tilde{\boldsymbol{\zeta}}^G` is the skew-symmetric matrix of the vector between the lattice
    grid vertex and the structural node.

    Args:
        aero_forces (list): Aerodynamic forces from the UVLM in inertial frame of reference
        struct2aero_mapping (dict): Structural to aerodynamic node mapping
        zeta (list): Aerodynamic grid coordinates
        pos_def (np.ndarray): Vector of structural node displacements
        psi_def (np.ndarray): Vector of structural node rotations (CRVs)
        master: Unused
        conn (np.ndarray): Connectivities matrix
        cag (np.ndarray): Transformation matrix between inertial and body-attached reference ``A``
        aero_dict (dict): Dictionary containing the grid's information.

    Returns:
        np.ndarray: structural forces in an ``n_node x 6`` vector
    """

    n_node, _ = pos_def.shape
    n_elem, _, _ = psi_def.shape
    struct_forces = np.zeros((n_node, 6))

    nodes = []

    for i_elem in range(n_elem):
        for i_local_node in range(3):

            i_global_node = conn[i_elem, i_local_node]
            if i_global_node in nodes:
                continue

            nodes.append(i_global_node)
            for mapping in struct2aero_mapping[i_global_node]:
                i_surf = mapping['i_surf']
                i_n = mapping['i_n']
                _, n_m, _ = aero_forces[i_surf].shape

                crv = psi_def[i_elem, i_local_node, :]
                cab = algebra.crv2rotation(crv)
                cbg = np.dot(cab.T, cag)

                for i_m in range(n_m):
                    chi_g = zeta[i_surf][:, i_m, i_n] - np.dot(cag.T, pos_def[i_global_node, :])
                    struct_forces[i_global_node, 0:3] += np.dot(cbg, aero_forces[i_surf][0:3, i_m, i_n])
                    struct_forces[i_global_node, 3:6] += np.dot(cbg, aero_forces[i_surf][3:6, i_m, i_n])
                    struct_forces[i_global_node, 3:6] += np.dot(cbg, algebra.cross3(chi_g, aero_forces[i_surf][0:3, i_m, i_n]))

    return struct_forces
Esempio n. 11
0
def eval_seg_exp_loop(DerP, DerA, DerB, ZetaP, ZetaA, ZetaB, gamma_seg,
                      vortex_radius):
    """
    Derivative of induced velocity Q w.r.t. collocation (DerC) and segment
    coordinates in format.

    To optimise performance, the function requires the derivative terms to be
    pre-allocated and passed as input.

    Each Der* term returns derivatives in the format

        [ (x,y,z) of Zeta,  (x,y,z) of Q]

    Warning: to optimise performance, variables are scaled during the execution.
    """

    RA = ZetaP - ZetaA
    RB = ZetaP - ZetaB
    RAB = ZetaB - ZetaA
    Vcr = algebra.cross3(RA, RB)
    vcr2 = np.dot(Vcr, Vcr)

    # numerical radious
    vortex_radious_here = vortex_radius * algebra.norm3d(RAB)
    if vcr2 < vortex_radious_here**2:
        return

    # scaling
    ra1, rb1 = algebra.norm3d(RA), algebra.norm3d(RB)
    ra2, rb2 = ra1**2, rb1**2
    rainv = 1. / ra1
    rbinv = 1. / rb1
    ra_dir, rb_dir = RA * rainv, RB * rbinv
    ra3inv, rb3inv = rainv**3, rbinv**3
    Vcr = Vcr / vcr2

    diff_vec = ra_dir - rb_dir
    vdot_prod = np.dot(diff_vec, RAB)
    T2 = vdot_prod / vcr2

    # Extract components
    ra_x, ra_y, ra_z = RA
    rb_x, rb_y, rb_z = RB
    rab_x, rab_y, rab_z = RAB
    vcr_x, vcr_y, vcr_z = Vcr
    ra2_x, ra2_y, ra2_z = RA**2
    rb2_x, rb2_y, rb2_z = RB**2
    ra_vcr_x, ra_vcr_y, ra_vcr_z = 2. * algebra.cross3(RA, Vcr)
    rb_vcr_x, rb_vcr_y, rb_vcr_z = 2. * algebra.cross3(RB, Vcr)
    vcr_sca_x, vcr_sca_y, vcr_sca_z = Vcr * ra3inv
    vcr_scb_x, vcr_scb_y, vcr_scb_z = Vcr * rb3inv

    # # ### derivatives indices:
    # # # the 1st is the component of the vaiable w.r.t derivative are taken.
    # # # the 2nd is the component of the output
    dQ_dRA = np.array([
        [
            -vdot_prod * rb_vcr_x * vcr_x + vcr_sca_x *
            (rab_x *
             (ra2 - ra2_x) - ra_x * ra_y * rab_y - ra_x * ra_z * rab_z),
            -T2 * rb_z - vdot_prod * rb_vcr_x * vcr_y + vcr_sca_y *
            (rab_x *
             (ra2 - ra2_x) - ra_x * ra_y * rab_y - ra_x * ra_z * rab_z),
            T2 * rb_y - vdot_prod * rb_vcr_x * vcr_z + vcr_sca_z *
            (rab_x * (ra2 - ra2_x) - ra_x * ra_y * rab_y - ra_x * ra_z * rab_z)
        ],
        [
            T2 * rb_z - vdot_prod * rb_vcr_y * vcr_x + vcr_sca_x *
            (rab_y *
             (ra2 - ra2_y) - ra_x * ra_y * rab_x - ra_y * ra_z * rab_z),
            -vdot_prod * rb_vcr_y * vcr_y + vcr_sca_y *
            (rab_y *
             (ra2 - ra2_y) - ra_x * ra_y * rab_x - ra_y * ra_z * rab_z),
            -T2 * rb_x - vdot_prod * rb_vcr_y * vcr_z + vcr_sca_z *
            (rab_y * (ra2 - ra2_y) - ra_x * ra_y * rab_x - ra_y * ra_z * rab_z)
        ],
        [
            -T2 * rb_y - vdot_prod * rb_vcr_z * vcr_x + vcr_sca_x *
            (rab_z *
             (ra2 - ra2_z) - ra_x * ra_z * rab_x - ra_y * ra_z * rab_y),
            T2 * rb_x - vdot_prod * rb_vcr_z * vcr_y + vcr_sca_y *
            (rab_z *
             (ra2 - ra2_z) - ra_x * ra_z * rab_x - ra_y * ra_z * rab_y),
            -vdot_prod * rb_vcr_z * vcr_z + vcr_sca_z *
            (rab_z * (ra2 - ra2_z) - ra_x * ra_z * rab_x - ra_y * ra_z * rab_y)
        ]
    ])

    dQ_dRB = np.array([[
        vdot_prod * ra_vcr_x * vcr_x + vcr_scb_x *
        (rab_x * (-rb2 + rb2_x) + rab_y * rb_x * rb_y + rab_z * rb_x * rb_z),
        T2 * ra_z + vdot_prod * ra_vcr_x * vcr_y + vcr_scb_y *
        (rab_x * (-rb2 + rb2_x) + rab_y * rb_x * rb_y + rab_z * rb_x * rb_z),
        -T2 * ra_y + vdot_prod * ra_vcr_x * vcr_z + vcr_scb_z *
        (rab_x * (-rb2 + rb2_x) + rab_y * rb_x * rb_y + rab_z * rb_x * rb_z)
    ],
                       [
                           -T2 * ra_z + vdot_prod * ra_vcr_y * vcr_x +
                           vcr_scb_x * (rab_x * rb_x * rb_y + rab_y *
                                        (-rb2 + rb2_y) + rab_z * rb_y * rb_z),
                           vdot_prod * ra_vcr_y * vcr_y + vcr_scb_y *
                           (rab_x * rb_x * rb_y + rab_y *
                            (-rb2 + rb2_y) + rab_z * rb_y * rb_z),
                           T2 * ra_x + vdot_prod * ra_vcr_y * vcr_z +
                           vcr_scb_z * (rab_x * rb_x * rb_y + rab_y *
                                        (-rb2 + rb2_y) + rab_z * rb_y * rb_z)
                       ],
                       [
                           T2 * ra_y + vdot_prod * ra_vcr_z * vcr_x +
                           vcr_scb_x *
                           (rab_x * rb_x * rb_z + rab_y * rb_y * rb_z + rab_z *
                            (-rb2 + rb2_z)), -T2 * ra_x +
                           vdot_prod * ra_vcr_z * vcr_y + vcr_scb_y *
                           (rab_x * rb_x * rb_z + rab_y * rb_y * rb_z + rab_z *
                            (-rb2 + rb2_z)),
                           vdot_prod * ra_vcr_z * vcr_z + vcr_scb_z *
                           (rab_x * rb_x * rb_z + rab_y * rb_y * rb_z + rab_z *
                            (-rb2 + rb2_z))
                       ]])

    dQ_dRAB = np.array(
        [[vcr_x * diff_vec[0], vcr_y * diff_vec[0], vcr_z * diff_vec[0]],
         [vcr_x * diff_vec[1], vcr_y * diff_vec[1], vcr_z * diff_vec[1]],
         [vcr_x * diff_vec[2], vcr_y * diff_vec[2], vcr_z * diff_vec[2]]])

    DerP += (cfact_biot * gamma_seg) * (dQ_dRA + dQ_dRB).T  # w.r.t. P
    DerA += (cfact_biot * gamma_seg) * (-dQ_dRAB - dQ_dRA).T  # w.r.t. A
    DerB += (cfact_biot * gamma_seg) * (dQ_dRAB - dQ_dRB).T  # w.r.t. B
Esempio n. 12
0
def eval_panel_fast_coll(zetaP, ZetaPanel, vortex_radius, gamma_pan=1.0):
    """
    Computes derivatives of induced velocity w.r.t. coordinates of target point,
    zetaP, coordinates. Returns two elements:
        - DerP: derivative of induced velocity w.r.t. ZetaP, with:
            DerP.shape=(3,3) : DerC[ Uind_{x,y,z}, ZetaC_{x,y,z} ]

    The function is based on eval_panel_fast, but does not perform operations
    required to compute the derivatives w.r.t. the panel coordinates.
    """

    vortex_radius_sq = vortex_radius * vortex_radius
    DerP = np.zeros((3, 3))

    ### ---------------------------------------------- Compute common variables
    # these are constants or variables depending only on vertices and P coords
    Cfact = cfact_biot * gamma_pan

    # distance vertex ii-th from P
    R_list = zetaP - ZetaPanel
    r1_list = [algebra.norm3d(R_list[ii]) for ii in svec]
    r1inv_list = [1. / r1_list[ii] for ii in svec]
    Runit_list = [R_list[ii] * r1inv_list[ii] for ii in svec]
    Der_runit_list = [
        der_runit(R_list[ii], r1inv_list[ii], -r1inv_list[ii]**3)
        for ii in svec
    ]

    ### ------------------------------------------------- Loop through segments
    for aa, bb in LoopPanel:

        RAB = ZetaPanel[bb, :] - ZetaPanel[aa, :]  # segment vector
        Vcr = algebra.cross3(R_list[aa], R_list[bb])
        vcr2 = np.dot(Vcr, Vcr)

        if vcr2 < (vortex_radius_sq * algebra.normsq3d(RAB)):
            continue

        Tv = Runit_list[aa] - Runit_list[bb]
        dotprod = np.dot(RAB, Tv)

        ### ----------------------------------------- cross-product derivatives
        # lower triangular part only
        vcr2inv = 1. / vcr2
        vcr4inv = vcr2inv * vcr2inv
        diag_fact = Cfact * vcr2inv * dotprod
        off_fact = -2. * Cfact * vcr4inv * dotprod
        Dvcross = [[diag_fact + off_fact * Vcr[0]**2],
                   [
                       off_fact * Vcr[0] * Vcr[1],
                       diag_fact + off_fact * Vcr[1]**2
                   ],
                   [
                       off_fact * Vcr[0] * Vcr[2], off_fact * Vcr[1] * Vcr[2],
                       diag_fact + off_fact * Vcr[2]**2
                   ]]

        ### ---------------------------------------- difference term derivative
        Vsc = Vcr * vcr2inv * Cfact
        Ddiff = np.array([RAB * Vsc[0], RAB * Vsc[1], RAB * Vsc[2]])

        ### ------------------------------------------ Final assembly (crucial)

        # dQ_dRA=Dvcross_by_skew3d(Dvcross,-R_list[bb])\
        # 									 +np.dot(Ddiff, Der_runit_list[aa] )
        # dQ_dRB=Dvcross_by_skew3d(Dvcross, R_list[aa])\
        # 									 -np.dot(Ddiff, Der_runit_list[bb] )

        DerP += Dvcross_by_skew3d(Dvcross, RAB) + \
                +np.dot(Ddiff, Der_runit_list[aa] - Der_runit_list[bb])

    return DerP
Esempio n. 13
0
def eval_panel_fast(zetaP, ZetaPanel, vortex_radius, gamma_pan=1.0):
    """
    Computes derivatives of induced velocity w.r.t. coordinates of target point,
    zetaP, and panel coordinates. Returns two elements:
        - DerP: derivative of induced velocity w.r.t. ZetaP, with:
            DerP.shape=(3,3) : DerC[ Uind_{x,y,z}, ZetaC_{x,y,z} ]
        - DerVertices: derivative of induced velocity wrt panel vertices, with:
            DerVertices.shape=(4,3,3) :
            DerVertices[ vertex number {0,1,2,3},  Uind_{x,y,z}, ZetaC_{x,y,z} ]

    The function is based on eval_panel_comp, but minimises operationsby
    recycling variables.
    """

    vortex_radius_sq = vortex_radius * vortex_radius
    DerP = np.zeros((3, 3))
    DerVertices = np.zeros((4, 3, 3))

    ### ---------------------------------------------- Compute common variables
    # these are constants or variables depending only on vertices and P coords
    Cfact = cfact_biot * gamma_pan

    # distance vertex ii-th from P
    R_list = zetaP - ZetaPanel
    r1_list = [algebra.norm3d(R_list[ii]) for ii in svec]
    r1inv_list = [1. / r1_list[ii] for ii in svec]
    Runit_list = [R_list[ii] * r1inv_list[ii] for ii in svec]
    Der_runit_list = [
        der_runit(R_list[ii], r1inv_list[ii], -r1inv_list[ii]**3)
        for ii in svec
    ]

    ### ------------------------------------------------- Loop through segments
    for aa, bb in LoopPanel:

        RAB = ZetaPanel[bb, :] - ZetaPanel[aa, :]  # segment vector
        Vcr = algebra.cross3(R_list[aa], R_list[bb])
        vcr2 = np.dot(Vcr, Vcr)

        if vcr2 < (vortex_radius_sq * algebra.normsq3d(RAB)):
            continue

        Tv = Runit_list[aa] - Runit_list[bb]
        dotprod = np.dot(RAB, Tv)

        ### ----------------------------------------- cross-product derivatives
        # lower triangular part only
        vcr2inv = 1. / vcr2
        vcr4inv = vcr2inv * vcr2inv
        diag_fact = Cfact * vcr2inv * dotprod
        off_fact = -2. * Cfact * vcr4inv * dotprod
        Dvcross = [[diag_fact + off_fact * Vcr[0]**2],
                   [
                       off_fact * Vcr[0] * Vcr[1],
                       diag_fact + off_fact * Vcr[1]**2
                   ],
                   [
                       off_fact * Vcr[0] * Vcr[2], off_fact * Vcr[1] * Vcr[2],
                       diag_fact + off_fact * Vcr[2]**2
                   ]]

        ### ---------------------------------------- difference term derivative
        Vsc = Vcr * vcr2inv * Cfact
        Ddiff = np.array([RAB * Vsc[0], RAB * Vsc[1], RAB * Vsc[2]])

        ### ---------------------------------------------------- RAB derivative
        dQ_dRAB = np.array([Tv * Vsc[0], Tv * Vsc[1], Tv * Vsc[2]])

        ### ------------------------------------------ Final assembly (crucial)

        dQ_dRA = Dvcross_by_skew3d(Dvcross, -R_list[bb]) \
                 + np.dot(Ddiff, Der_runit_list[aa])
        dQ_dRB = Dvcross_by_skew3d(Dvcross, R_list[aa]) \
                 - np.dot(Ddiff, Der_runit_list[bb])

        DerP += dQ_dRA + dQ_dRB  # w.r.t. P
        DerVertices[aa, :, :] -= dQ_dRAB + dQ_dRA  # w.r.t. A
        DerVertices[bb, :, :] += dQ_dRAB - dQ_dRB  # w.r.t. B

    # ### collocation point only
    # DerP +=Dvcross_by_skew3d(Dvcross,RA-RB)+np.dot(Ddiff,
    # 		  der_runit(RA,rainv,minus_rainv3)-der_runit(RB,rbinv,minus_rbinv3))

    return DerP, DerVertices