Ejemplo n.º 1
0
    def compute(self, inputs, outputs):
        A = inputs['A']
        nodes = inputs['nodes']
        mrho = inputs['mrho']
        wwr = self.surface['wing_weight_ratio']
        Aspars = inputs['Aspars']  #VMGM

        # Calculate the volume and weight of the structure
        element_spar_volumes = norm(nodes[1:, :] - nodes[:-1, :], axis=1) * Aspars
        element_skin_volumes = norm(nodes[1:, :] - nodes[:-1, :], axis=1) * (A - Aspars)

        # nodes[1:, :] - nodes[:-1, :] this is the delta array of the difference between the points
        element_mass = element_spar_volumes * mrho[0] * wwr + element_skin_volumes * mrho[1] * wwr
        weight = np.sum(element_mass)
        weight_spars = np.sum(element_spar_volumes * mrho[0] * wwr)  #VMGM

        # If the tube is symmetric, double the computed weight
        if self.surface['symmetry']:
            weight *= 2.
            weight_spars *= 2.  #VMGM

        if weight < 0 :
            print("negative weight")
            
        outputs['structural_mass'] = weight
        outputs['element_mass'] = element_mass
        outputs['spars_mass'] = weight_spars  #VMGM
Ejemplo n.º 2
0
    def compute(self, inputs, outputs):

        struct_weights = inputs['element_weights']
        nodes = inputs['nodes']

        element_lengths = np.ones(self.ny - 1, dtype=complex)
        for i in range(self.ny - 1):
            element_lengths[i] = norm(nodes[i + 1] - nodes[i])

        # And we also need the deltas between consecutive nodes
        deltas = nodes[1:, :] - nodes[:-1, :]

        # Assume weight coincides with the elastic axis
        z_forces_for_each = struct_weights / 2.
        z_moments_for_each = struct_weights * element_lengths / 12. \
                            * (deltas[:, 0]**2 + deltas[:, 1]**2)**0.5 / element_lengths

        loads = np.zeros((self.ny, 6), dtype=complex)

        # Loads in z-direction
        loads[:-1, 2] += -z_forces_for_each
        loads[1:, 2] += -z_forces_for_each

        # Bending moments for consistency
        loads[:-1, 3] += -z_moments_for_each * deltas[:, 1] / element_lengths
        loads[1:, 3] += z_moments_for_each * deltas[:, 1] / element_lengths

        loads[:-1, 4] += -z_moments_for_each * deltas[:, 0] / element_lengths
        loads[1:, 4] += z_moments_for_each * deltas[:, 0] / element_lengths

        outputs['struct_weight_loads'] = loads
Ejemplo n.º 3
0
    def compute(self, inputs, outputs):
        A = inputs['A']
        nodes = inputs['nodes']
        mrho = self.surface['mrho']
        wwr = self.surface['wing_weight_ratio']

        # Calculate the volume and weight of the structure
        element_volumes = norm(nodes[1:, :] - nodes[:-1, :], axis=1) * A

        # nodes[1:, :] - nodes[:-1, :] this is the delta array of the different between the points
        element_mass = element_volumes * mrho * wwr
        weight = np.sum(element_mass)

        # If the tube is symmetric, double the computed weight
        if self.surface['symmetry']:
            weight *= 2.

        outputs['structural_mass'] = weight
        outputs['element_mass'] = element_mass
    def compute(self, inputs, outputs):
        dtype = float
        if self.under_complex_step:
            dtype = complex

        self.T = np.zeros((3, 3), dtype=dtype)
        self.x_gl = np.array([1, 0, 0], dtype=dtype)
        radius = inputs['radius']
        disp = inputs['disp']
        nodes = inputs['nodes']
        T = self.T
        E = self.E
        G = self.G
        x_gl = self.x_gl

        num_elems = self.ny - 1
        for ielem in range(num_elems):

            P0 = nodes[ielem, :]
            P1 = nodes[ielem + 1, :]
            L = norm(P1 - P0)

            x_loc = unit(P1 - P0)
            y_loc = unit(np.cross(x_loc, x_gl))
            z_loc = unit(np.cross(x_loc, y_loc))

            T[0, :] = x_loc
            T[1, :] = y_loc
            T[2, :] = z_loc

            u0x, u0y, u0z = T.dot(disp[ielem, :3])
            r0x, r0y, r0z = T.dot(disp[ielem, 3:])
            u1x, u1y, u1z = T.dot(disp[ielem + 1, :3])
            r1x, r1y, r1z = T.dot(disp[ielem + 1, 3:])

            tmp = np.sqrt((r1y - r0y)**2 + (r1z - r0z)**2)
            sxx0 = E * (u1x - u0x) / L + E * radius[ielem] / L * tmp
            sxx1 = E * (u0x - u1x) / L + E * radius[ielem] / L * tmp
            sxt = G * radius[ielem] * (r1x - r0x) / L

            outputs['vonmises'][ielem, 0] = np.sqrt(sxx0**2 + 3 * sxt**2)
            outputs['vonmises'][ielem, 1] = np.sqrt(sxx1**2 + 3 * sxt**2)
Ejemplo n.º 5
0
    def compute(self, inputs, outputs):
        radius = inputs['radius']
        disp = inputs['disp']
        nodes = inputs['nodes']
        T = self.T
        E = self.E
        G = self.G
        x_gl = self.x_gl

        if fortran_flag:
            vm = OAS_API.oas_api.calc_vonmises(nodes, radius, disp, E, G, x_gl)
            outputs['vonmises'] = vm

        else:

            num_elems = self.ny - 1
            for ielem in range(self.ny - 1):

                P0 = nodes[ielem, :]
                P1 = nodes[ielem + 1, :]
                L = norm(P1 - P0)

                x_loc = unit(P1 - P0)
                y_loc = unit(np.cross(x_loc, x_gl))
                z_loc = unit(np.cross(x_loc, y_loc))

                T[0, :] = x_loc
                T[1, :] = y_loc
                T[2, :] = z_loc

                u0x, u0y, u0z = T.dot(disp[ielem, :3])
                r0x, r0y, r0z = T.dot(disp[ielem, 3:])
                u1x, u1y, u1z = T.dot(disp[ielem + 1, :3])
                r1x, r1y, r1z = T.dot(disp[ielem + 1, 3:])

                tmp = np.sqrt((r1y - r0y)**2 + (r1z - r0z)**2)
                sxx0 = E * (u1x - u0x) / L + E * radius[ielem] / L * tmp
                sxx1 = E * (u0x - u1x) / L + E * radius[ielem] / L * tmp
                sxt = G * radius[ielem] * (r1x - r0x) / L

                outputs['vonmises'][ielem, 0] = np.sqrt(sxx0**2 + 3 * sxt**2)
                outputs['vonmises'][ielem, 1] = np.sqrt(sxx1**2 + 3 * sxt**2)
    def compute(self, inputs, outputs):
        dtype = float
        if self.under_complex_step:
            dtype = complex

        self.T = np.zeros((3, 3),dtype=dtype)
        self.x_gl = np.array([1, 0, 0],dtype=dtype)
        radius = inputs['radius']
        disp = inputs['disp']
        nodes = inputs['nodes']
        T = self.T
        E = self.E
        G = self.G
        x_gl = self.x_gl

        num_elems = self.ny - 1
        for ielem in range(num_elems):

            P0 = nodes[ielem, :]
            P1 = nodes[ielem+1, :]
            L = norm(P1 - P0)

            x_loc = unit(P1 - P0)
            y_loc = unit(np.cross(x_loc, x_gl))
            z_loc = unit(np.cross(x_loc, y_loc))

            T[0, :] = x_loc
            T[1, :] = y_loc
            T[2, :] = z_loc

            u0x, u0y, u0z = T.dot(disp[ielem, :3])
            r0x, r0y, r0z = T.dot(disp[ielem, 3:])
            u1x, u1y, u1z = T.dot(disp[ielem+1, :3])
            r1x, r1y, r1z = T.dot(disp[ielem+1, 3:])

            tmp = np.sqrt((r1y - r0y)**2 + (r1z - r0z)**2)
            sxx0 = E * (u1x - u0x) / L + E * radius[ielem] / L * tmp
            sxx1 = E * (u0x - u1x) / L + E * radius[ielem] / L * tmp
            sxt = G * radius[ielem] * (r1x - r0x) / L

            outputs['vonmises'][ielem, 0] = np.sqrt(sxx0**2 + 3 * sxt**2)
            outputs['vonmises'][ielem, 1] = np.sqrt(sxx1**2 + 3 * sxt**2)
Ejemplo n.º 7
0
    def compute(self, inputs, outputs):
        A = inputs['A']
        nodes = inputs['nodes']
        mrho = self.surface['mrho']
        wwr = self.surface['wing_weight_ratio']
        lf = inputs['load_factor']

        # Calculate the volume and weight of the structure
        element_volumes = norm(nodes[1:, :] - nodes[:-1, :], axis=1) * A

        # nodes[1:, :] - nodes[:-1, :] this is the delta array of the different between the points
        element_weights = element_volumes * mrho * 9.81 * wwr * lf
        weight = np.sum(element_weights)

        # If the tube is symmetric, double the computed weight and set the
        # y-location of the cg to 0, at the symmetry plane
        if self.surface['symmetry']:
            weight *= 2.

        #outputs['structural_weight'] = weight
        outputs['structural_weight'] = weight
        outputs['element_weights'] = element_weights
Ejemplo n.º 8
0
    def compute(self, inputs, outputs):
        A = inputs['A']
        nodes = inputs['nodes']
        mrho = self.surface['mrho']
        wwr = self.surface['wing_weight_ratio']
        lf = inputs['load_factor']

        # Calculate the volume and weight of the structure
        element_volumes = norm(nodes[1:, :] - nodes[:-1, :], axis=1) * A

        # nodes[1:, :] - nodes[:-1, :] this is the delta array of the different between the points
        element_weights = element_volumes * mrho * 9.81 * wwr * lf
        weight = np.sum(element_weights)

        # If the tube is symmetric, double the computed weight and set the
        # y-location of the cg to 0, at the symmetry plane
        if self.surface['symmetry']:
            weight *= 2.

        #outputs['structural_weight'] = weight
        outputs['structural_weight'] = weight
        outputs['element_weights'] = element_weights
    def compute(self, inputs, outputs):

        struct_weights = inputs['element_mass'] * inputs[
            'load_factor'] * grav_constant
        nodes = inputs['nodes']

        element_lengths = norm(nodes[1:, :] - nodes[:-1, :], axis=1)

        # And we also need the deltas between consecutive nodes
        deltas = nodes[1:, :] - nodes[:-1, :]
        # save these slices cause I use them a lot
        del0 = deltas[:, 0]
        del1 = deltas[:, 1]

        # Assume weight coincides with the elastic axis
        z_forces_for_each = struct_weights / 2.
        z_moments_for_each = struct_weights / 12. \
                            * (del0**2 + del1**2)**0.5

        loads = outputs['struct_weight_loads']
        loads *= 0  # need to zero it out, since we're accumulating onto it
        # Why doesn't this trigger when running ../../tests/test_aerostruct_wingbox_+weight_analysis.py???
        if self.under_complex_step:
            loads = np.zeros((self.ny, 6), dtype=complex)

        # Loads in z-direction
        loads[:-1, 2] += -z_forces_for_each
        loads[1:, 2] += -z_forces_for_each

        # Bending moments for consistency
        bm3 = z_moments_for_each * del1 / element_lengths
        loads[:-1, 3] += -bm3
        loads[1:, 3] += bm3

        bm4 = z_moments_for_each * del0 / element_lengths
        loads[:-1, 4] += -bm4
        loads[1:, 4] += bm4
        outputs['struct_weight_loads'] = loads
    def compute(self, inputs, outputs):

        struct_weights = inputs['element_mass'] * inputs['load_factor'] * grav_constant
        nodes = inputs['nodes']

        element_lengths = norm(nodes[1:, :] - nodes[:-1, :], axis=1)

        # And we also need the deltas between consecutive nodes
        deltas = nodes[1:, :] - nodes[:-1, :]
        # save these slices cause I use them alot
        del0 = deltas[: , 0]
        del1 = deltas[: , 1]

        # Assume weight coincides with the elastic axis
        z_forces_for_each = struct_weights / 2.
        z_moments_for_each = struct_weights / 12. \
                            * (del0**2 + del1**2)**0.5

        loads = outputs['struct_weight_loads']
        loads *= 0 # need to zero it out, since we're accumulating onto it
        # Why doesn't this trigger when running ../../tests/test_aerostruct_wingbox_+weight_analysis.py???
        if self.under_complex_step:
            loads = np.zeros((self.ny, 6), dtype=complex)

        # Loads in z-direction
        loads[:-1, 2] += -z_forces_for_each
        loads[1:, 2] += -z_forces_for_each

        # Bending moments for consistency
        bm3 = z_moments_for_each * del1 / element_lengths
        loads[:-1, 3] += -bm3
        loads[1:, 3] += bm3

        bm4 = z_moments_for_each * del0 / element_lengths
        loads[:-1, 4] += -bm4
        loads[1:, 4] += bm4
        outputs['struct_weight_loads'] = loads
Ejemplo n.º 11
0
    def compute(self, inputs, outputs):
        mesh = inputs['mesh']
        vectors = mesh[-1, :, :] - mesh[0, :, :]
        streamwise_chords = np.sqrt(np.sum(vectors**2, axis=1))
        streamwise_chords = 0.5 * streamwise_chords[:
                                                    -1] + 0.5 * streamwise_chords[
                                                        1:]

        # Chord lengths for the panel strips at the panel midpoint
        outputs['streamwise_chords'] = streamwise_chords.copy()

        fem_twists = np.zeros(streamwise_chords.shape)
        fem_chords = streamwise_chords.copy()

        surface = self.surface

        # Gets the shear center by looking at the four corners.
        # Assumes same spar thickness for front and rear spar.
        w = (surface['data_x_upper'][0] *(surface['data_y_upper'][0]-surface['data_y_lower'][0]) + \
        surface['data_x_upper'][-1]*(surface['data_y_upper'][-1]-surface['data_y_lower'][-1])) / \
        ( (surface['data_y_upper'][0]-surface['data_y_lower'][0]) + (surface['data_y_upper'][-1]-surface['data_y_lower'][-1]))

        # TODO: perhaps replace this or link with existing nodes computation
        nodes = (1 - w) * mesh[0, :, :] + w * mesh[-1, :, :]

        mesh_vectors = mesh[-1, :, :] - mesh[0, :, :]

        # Loop over spanwise elements
        for ielem in range(mesh.shape[1] - 1):

            # Obtain the element nodes
            P0 = nodes[ielem, :]
            P1 = nodes[ielem + 1, :]

            elem_vec = (P1 - P0)  # vector along element
            temp_vec = elem_vec.copy()
            temp_vec[0] = 0.  # vector along element without x component

            # This is used to get chord length normal to FEM element.
            # To be clear, this 3D angle sweep measure.
            # This is the projection to the wing orthogonal to the FEM direction.
            cos_theta_fe_sweep = norm(temp_vec) / norm(elem_vec)
            fem_chords[ielem] = fem_chords[ielem] * cos_theta_fe_sweep

        outputs['fem_chords'] = fem_chords

        # Loop over spanwise elements
        for ielem in range(mesh.shape[1] - 1):

            # The following is used to approximate the twist angle for the section normal to the FEM element
            mesh_vec_0 = mesh_vectors[ielem]
            temp_mesh_vectors_0 = mesh_vec_0.copy()
            temp_mesh_vectors_0[2] = 0.

            cos_twist_0 = norm(temp_mesh_vectors_0) / norm(mesh_vec_0)

            if cos_twist_0 > 1.:
                theta_0 = 0.  # to prevent nan in case value for arccos is greater than 1 due to machine precision
            else:
                theta_0 = np.arccos(cos_twist_0)

            mesh_vec_1 = mesh_vectors[ielem + 1]
            temp_mesh_vectors_1 = mesh_vec_1.copy()
            temp_mesh_vectors_1[2] = 0.

            cos_twist_1 = norm(temp_mesh_vectors_1) / norm(mesh_vec_1)

            if cos_twist_1 > 1.:
                theta_1 = 0.  # to prevent nan in case value for arccos is greater than 1 due to machine precision
            else:
                theta_1 = np.arccos(cos_twist_1)

            fem_twists[ielem] = (
                theta_0 +
                theta_1) / 2 * streamwise_chords[ielem] / fem_chords[ielem]
        outputs['fem_twists'] = fem_twists
Ejemplo n.º 12
0
    def compute(self, inputs, outputs):
        disp = inputs['disp']
        nodes = inputs['nodes']
        A_enc = inputs['A_enc']
        Qy = inputs['Qz']
        J = inputs['J']
        htop = inputs['htop']
        hbottom = inputs['hbottom']
        hfront = inputs['hfront']
        hrear = inputs['hrear']
        spar_thickness = inputs['spar_thickness']
        vonmises = outputs['vonmises']

        # Only use complex type for these arrays if we're using cs to check derivs
        dtype = type(disp[0, 0])
        T = np.zeros((3, 3), dtype=dtype)
        x_gl = np.array([1, 0, 0], dtype=dtype)

        E = self.E
        G = self.G

        num_elems = self.ny - 1
        for ielem in range(num_elems):

            P0 = nodes[ielem, :]
            P1 = nodes[ielem + 1, :]
            L = norm(P1 - P0)

            x_loc = unit(P1 - P0)
            y_loc = unit(np.cross(x_loc, x_gl))
            z_loc = unit(np.cross(x_loc, y_loc))

            T[0, :] = x_loc
            T[1, :] = y_loc
            T[2, :] = z_loc

            u0x, u0y, u0z = T.dot(disp[ielem, :3])
            r0x, r0y, r0z = T.dot(disp[ielem, 3:])
            u1x, u1y, u1z = T.dot(disp[ielem + 1, :3])
            r1x, r1y, r1z = T.dot(disp[ielem + 1, 3:])

            # this is stress = modulus * strain; positive is tensile
            axial_stress = E * (u1x - u0x) / L

            # this is Torque / (2 * thickness_min * Area_enclosed)
            torsion_stress = G * J[ielem] / L * (
                r1x - r0x) / 2 / spar_thickness[ielem] / A_enc[ielem]

            # this is moment * h / I
            top_bending_stress = E / (L**2) * (
                6 * u0y + 2 * r0z * L - 6 * u1y + 4 * r1z * L) * htop[ielem]

            # this is moment * h / I
            bottom_bending_stress = -E / (L**2) * (
                6 * u0y + 2 * r0z * L - 6 * u1y + 4 * r1z * L) * hbottom[ielem]

            # this is moment * h / I
            front_bending_stress = -E / (L**2) * (
                -6 * u0z + 2 * r0y * L + 6 * u1z + 4 * r1y * L) * hfront[ielem]

            # this is moment * h / I
            rear_bending_stress = E / (L**2) * (
                -6 * u0z + 2 * r0y * L + 6 * u1z + 4 * r1y * L) * hrear[ielem]

            # shear due to bending (VQ/It) note: the I used to get V cancels the other I
            vertical_shear = E / (L**3) * (-12 * u0y - 6 * r0z * L + 12 * u1y -
                                           6 * r1z * L) * Qy[ielem] / (
                                               2 * spar_thickness[ielem])

            # print("==========",ielem,"================")
            # print("vertical_shear", vertical_shear)
            # print("top",top_bending_stress)
            # print("bottom",bottom_bending_stress)
            # print("front",front_bending_stress)
            # print("rear",rear_bending_stress)
            # print("axial", axial_stress)
            # print("torsion", torsion_stress)

            # The 4 stress combinations:
            vonmises[ielem, 0] = np.sqrt(
                (top_bending_stress + rear_bending_stress + axial_stress)**2 +
                3 * torsion_stress**2) / self.tssf
            vonmises[ielem,
                     1] = np.sqrt((bottom_bending_stress +
                                   front_bending_stress + axial_stress)**2 +
                                  3 * torsion_stress**2)
            vonmises[ielem,
                     2] = np.sqrt((front_bending_stress + axial_stress)**2 +
                                  3 * (torsion_stress - vertical_shear)**2)
            vonmises[ielem, 3] = np.sqrt(
                (rear_bending_stress + axial_stress)**2 + 3 *
                (torsion_stress + vertical_shear)**2) / self.tssf
    def compute_partials(self, inputs, J):

        struct_weights = inputs['element_mass'] * inputs['load_factor'] * grav_constant
        nodes = inputs['nodes']

        element_lengths = norm(nodes[1:, :] - nodes[:-1, :], axis=1)

        # And we also need the deltas between consecutive nodes
        deltas = nodes[1:, :] - nodes[:-1, :]
        # save these slices cause I use them alot
        del0 = deltas[: , 0]
        del1 = deltas[: , 1]

        # Assume weight coincides with the elastic axis
        z_moments_for_each = struct_weights / 12. \
                            * (del0**2 + del1**2)**0.5

        dzf__dew = .5*inputs['load_factor'][0]
        dzf__dlf = inputs['element_mass']/2.

        dzm__dew = diags((del0**2 + del1**2)**0.5/12*inputs['load_factor'])
        dzm__dlf =  (del0**2 + del1**2)**.5/12. * inputs['element_mass']

        dbm3__dzm = diags(del1/element_lengths)
        dbm4__dzm = diags(del0/element_lengths)

        # need to convert to lil to re-order the data to match the original row/col indexing from setup
        dswl__dlf = (-self.dswl__dbm3*dbm3__dzm*coo_matrix(dzm__dlf).T +\
                    -self.dswl__dbm4*dbm4__dzm*coo_matrix(dzm__dlf).T +\
                    self.dswl__dzf*coo_matrix(dzf__dlf).T).tolil()

        J['struct_weight_loads', 'load_factor'] = dswl__dlf[self.dswl__dlf_row, self.dswl__dlf_col].toarray().flatten() * grav_constant

        dswl__dew = (-self.dswl__dbm4 * dbm4__dzm * dzm__dew +\
                    -self.dswl__dbm3 * dbm3__dzm * dzm__dew +\
                    self.dswl__dzf  * dzf__dew).tolil()

        data = dswl__dew[self.dswl__dew_pattern.row, self.dswl__dew_pattern.col].toarray().flatten()
        J['struct_weight_loads', 'element_mass'] = data * grav_constant


        # dstruct_weight_loads__dnodes (this one is super complicated)

        # del__dnodes
        # note: del__dnodes matrix already created in setup, just need to set data
        raw_data = diags(1/element_lengths)*(nodes[1:, :] - nodes[:-1, :])
        data = np.hstack((-raw_data,raw_data)).flatten()
        self.del__dnodes.data = data

        dzm_dnodes = diags(struct_weights/12*(del0**2 + del1**2)**-.5)*\
                     (diags(del0)*self.ddel0__dnodes  + diags(del1)*self.ddel1__dnodes)

        dbm3_dnodes = diags(del1/element_lengths)*dzm_dnodes \
                    + diags(z_moments_for_each/element_lengths)*self.ddel1__dnodes \
                    - diags(z_moments_for_each*del1/element_lengths**2)*self.del__dnodes


        dbm4_dnodes = diags(del0/element_lengths)*dzm_dnodes \
                    + diags(z_moments_for_each/element_lengths)*self.ddel0__dnodes \
                    - diags(z_moments_for_each*del0/element_lengths**2)*self.del__dnodes


        # this is kind of dumb, but I need lil cause I have to re-index to preserve order
        #     the coo column ordering doesn't seem to be deterministic
        #     so I use the original row/col from the pattern as index arrays to
        #     pull the data out in the correct order
        dswl__dnodes= (self.dswl__dbm3*dbm3_dnodes+self.dswl__dbm4*dbm4_dnodes).tolil()
        data = dswl__dnodes[self.dswl__dnodes_pattern.row, self.dswl__dnodes_pattern.col].toarray().flatten()
        J['struct_weight_loads', 'nodes'] = -data
Ejemplo n.º 14
0
    def compute(self, inputs, outputs):
        disp = inputs['disp']
        nodes = inputs['nodes']
        A_enc = inputs['A_enc']
        Qy = inputs['Qz']
        Iz = inputs['Iz']
        J = inputs['J']
        htop = inputs['htop']
        hbottom = inputs['hbottom']
        hfront = inputs['hfront']
        hrear = inputs['hrear']
        spar_thickness = inputs['spar_thickness']
        skin_thickness = inputs['skin_thickness']
        vonmises = outputs['vonmises']

        # Only use complex type for these arrays if we're using cs to check derivs
        dtype = type(disp[0, 0])
        T = np.zeros((3, 3), dtype=dtype)
        x_gl = np.array([1, 0, 0], dtype=dtype)

        E = self.E
        G = self.G

        num_elems = self.ny - 1
        for ielem in range(num_elems):

            P0 = nodes[ielem, :]
            P1 = nodes[ielem+1, :]
            L = norm(P1 - P0)

            x_loc = unit(P1 - P0)
            y_loc = unit(np.cross(x_loc, x_gl))
            z_loc = unit(np.cross(x_loc, y_loc))

            T[0, :] = x_loc
            T[1, :] = y_loc
            T[2, :] = z_loc

            u0x, u0y, u0z = T.dot(disp[ielem, :3])
            r0x, r0y, r0z = T.dot(disp[ielem, 3:])
            u1x, u1y, u1z = T.dot(disp[ielem+1, :3])
            r1x, r1y, r1z = T.dot(disp[ielem+1, 3:])

            axial_stress = E * (u1x - u0x) / L      # this is stress = modulus * strain; positive is tensile
            torsion_stress = G * J[ielem] / L * (r1x - r0x) / 2 / spar_thickness[ielem] / A_enc[ielem]   # this is Torque / (2 * thickness_min * Area_enclosed)
            top_bending_stress = E / (L**2) * (6 * u0y + 2 * r0z * L - 6 * u1y + 4 * r1z * L ) * htop[ielem] # this is moment * htop / I
            bottom_bending_stress = - E / (L**2) * (6 * u0y + 2 * r0z * L - 6 * u1y + 4 * r1z * L ) * hbottom[ielem] # this is moment * htop / I
            front_bending_stress = - E / (L**2) * (-6 * u0z + 2 * r0y * L + 6 * u1z + 4 * r1y * L ) * hfront[ielem] # this is moment * htop / I
            rear_bending_stress = E / (L**2) * (-6 * u0z + 2 * r0y * L + 6 * u1z + 4 * r1y * L ) * hrear[ielem] # this is moment * htop / I

            vertical_shear =  E / (L**3) *(-12 * u0y - 6 * r0z * L + 12 * u1y - 6 * r1z * L ) * Qy[ielem] / (2 * spar_thickness[ielem]) # shear due to bending (VQ/It) note: the I used to get V cancels the other I

            # print("==========",ielem,"================")
            # print("vertical_shear", vertical_shear)
            # print("top",top_bending_stress)
            # print("bottom",bottom_bending_stress)
            # print("front",front_bending_stress)
            # print("rear",rear_bending_stress)
            # print("axial", axial_stress)
            # print("torsion", torsion_stress)

            vonmises[ielem, 0] = np.sqrt((top_bending_stress + rear_bending_stress + axial_stress)**2 + 3*torsion_stress**2) / self.tssf
            vonmises[ielem, 1] = np.sqrt((bottom_bending_stress + front_bending_stress + axial_stress)**2 + 3*torsion_stress**2)
            vonmises[ielem, 2] = np.sqrt((front_bending_stress + axial_stress)**2 + 3*(torsion_stress-vertical_shear)**2)
            vonmises[ielem, 3] = np.sqrt((rear_bending_stress + axial_stress)**2 + 3*(torsion_stress+vertical_shear)**2) / self.tssf
Ejemplo n.º 15
0
    def compute_partials(self, inputs, partials):

        radius = inputs['radius']
        disp = inputs['disp']
        nodes = inputs['nodes']
        T = self.T
        E = self.E
        G = self.G
        x_gl = self.x_gl

        num_elems = self.ny - 1
        for ielem in range(num_elems):

            # Compute the coordinate delta between the two element end points
            P0 = nodes[ielem, :]
            P1 = nodes[ielem+1, :]
            dP = P1 - P0

            # Compute the derivative of element length
            L = norm(dP)
            dLddP = norm_d(dP)

            # unit function converts a vector to a unit vector
            # calculate the transormation to the local element frame.
            # We use x_gl to provide a reference axis to reference
            x_loc = unit(dP)
            dxdP = unit_d(dP)

            y_loc = unit(np.cross(x_loc, x_gl))
            dtmpdx, _ = cross_d(x_loc, x_gl)
            dydtmp = unit_d(np.cross(x_loc, x_gl))
            dydP = dydtmp.dot(dtmpdx).dot(dxdP)

            z_loc = unit(np.cross(x_loc, y_loc))
            dtmpdx, dtmpdy = cross_d(x_loc,y_loc)
            dzdtmp = unit_d(np.cross(x_loc, y_loc))
            dzdP = dzdtmp.dot(dtmpdx).dot(dxdP) + dzdtmp.dot(dtmpdy).dot(dydP)

            T[0, :] = x_loc
            T[1, :] = y_loc
            T[2, :] = z_loc

            u0x = x_loc.dot(disp[ielem, :3])
            r0x, r0y, r0z = T.dot(disp[ielem, 3:])
            u1x = x_loc.dot(disp[ielem+1, :3])
            r1x, r1y, r1z = T.dot(disp[ielem+1, 3:])

            # #$$$$$$$$$$$$$$$$$$$$$$$$$$

            # The derivatives of the above code wrt displacement all boil down to sections of the T matrix
            dxddisp = T[0,:]
            dyddisp = T[1,:]
            dzddisp = T[2,:]

            #The derivatives of the above code wrt T all boil down to sections of the #displacement vector
            du0dloc = disp[ielem, :3]
            dr0dloc = disp[ielem, 3:]
            du1dloc = disp[ielem+1, :3]
            dr1dloc = disp[ielem+1, 3:]

            #$$$$$$$$$$$$$$$$$$$$$$$$$$
            # Original code
            # $$$$$$$$$$$$
            tmp = np.sqrt((r1y - r0y)**2 + (r1z - r0z)**2) + 1e-50 #added eps to avoid 0 disp singularity
            sxx0 = E * (u1x - u0x) / L + E * radius[ielem] / L * tmp
            sxx1 = E * (u0x - u1x) / L + E * radius[ielem] / L * tmp
            sxt = G * radius[ielem] * (r1x - r0x) / L
            # $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

            dtmpdr0y = 1/tmp * (r1y - r0y)*-1
            dtmpdr1y = 1/tmp * (r1y - r0y)
            dtmpdr0z = 1/tmp * (r1z - r0z)*-1
            dtmpdr1z = 1/tmp * (r1z - r0z)

            # Combine all of the derivtives for tmp
            dtmpdDisp = np.zeros(12)
            dr0xdDisp = np.zeros(12)
            dr1xdDisp = np.zeros(12)

            #r0 term
            dr0xdDisp[3:6] = dxddisp
            dr1xdDisp[9:12] = dxddisp

            dtmpdDisp[3:6] = dtmpdr0y*dyddisp
            dtmpdDisp[3:6] += dtmpdr0z*dzddisp
            dtmpdDisp[9:12] = dtmpdr1y*dyddisp
            dtmpdDisp[9:12] += dtmpdr1z*dzddisp

            # x_loc, y_loc and z_loc terms
            # (dttmpx_loc is zeros, so don't compute with it)
            dtmpdy_loc = dtmpdr0y*dr0dloc + dtmpdr1y*dr1dloc
            dtmpdz_loc = dtmpdr0z*dr0dloc + dtmpdr1z*dr1dloc

            dtmpdP = dtmpdy_loc.dot(dydP) + dtmpdz_loc.dot(dzdP)

            dsxx0dtmp = E * radius[ielem] / L
            dsxx0du0x = -E / L
            dsxx0du1x = E / L
            dsxx0dL =  -E * (u1x - u0x) / (L*L) - E * radius[ielem] / (L*L) * tmp

            dsxx1dtmp = E * radius[ielem] / L
            dsxx1du0x = E / L
            dsxx1du1x = -E / L
            dsxx1dL = -E * (u0x - u1x) / (L*L) - E * radius[ielem] / (L*L) * tmp

            dsxx0dP = dsxx0dtmp * dtmpdP + \
                      dsxx0du0x*du0dloc.dot(dxdP) + dsxx0du1x*du1dloc.dot(dxdP)+\
                      dsxx0dL*dLddP

            dsxx1dP = dsxx1dtmp * dtmpdP + \
                      dsxx1du0x*du0dloc.dot(dxdP)+dsxx1du1x*du1dloc.dot(dxdP)+\
                      dsxx1dL*dLddP

            # Combine sxx0 and sxx1 terms

            # Start with the tmp term
            dsxx0dDisp = dsxx0dtmp * dtmpdDisp
            dsxx1dDisp = dsxx1dtmp * dtmpdDisp

            # Now add the direct u dep
            dsxx0dDisp[0:3] = dsxx0du0x * dxddisp
            dsxx0dDisp[6:9] = dsxx0du1x * dxddisp

            dsxx1dDisp[0:3] = dsxx1du0x * dxddisp
            dsxx1dDisp[6:9] = dsxx1du1x * dxddisp

            # Combine sxt term
            dsxtdr0x = -G * radius[ielem] / L
            dsxtdr1x = G * radius[ielem] / L
            dsxtdL =  - G * radius[ielem] * (r1x - r0x) / (L*L)

            dsxtdP = dsxtdr0x*(dr0dloc.dot(dxdP)) + dsxtdr1x*(dr1dloc.dot(dxdP)) + \
                     dsxtdL*dLddP
            #disp
            dsxtdDisp = dsxtdr0x * dr0xdDisp + dsxtdr1x * dr1xdDisp

            #radius derivatives
            dsxxdrad = E / L * tmp
            dsxtdrad = G * (r1x - r0x)/L

            fact = 1.0 / (np.sqrt(sxx0**2 + 3 * sxt**2))
            dVm0dsxx0 = sxx0 * fact
            dVm0dsxt = 3 * sxt * fact

            fact = 1.0 / (np.sqrt(sxx1**2 + 3 * sxt**2))
            dVm1dsxx1 = sxx1 * fact
            dVm1dsxt = 3 * sxt * fact

            ii = 2 * ielem
            partials['vonmises', 'radius'][ii] = dVm0dsxx0*dsxxdrad + dVm0dsxt*dsxtdrad
            partials['vonmises', 'radius'][ii+1] = dVm1dsxx1*dsxxdrad + dVm1dsxt*dsxtdrad

            ii = 24 * ielem
            partials['vonmises', 'disp'][ii:ii+12] = dVm0dsxx0*dsxx0dDisp + dVm0dsxt*dsxtdDisp
            partials['vonmises', 'disp'][ii+12:ii+24] = dVm1dsxx1*dsxx1dDisp + dVm1dsxt*dsxtdDisp

            # Compute terms for the nodes
            ii = 12 * ielem

            dVm0_dnode = dVm0dsxx0*dsxx0dP + dVm0dsxt*dsxtdP
            partials['vonmises', 'nodes'][ii:ii+3] = -dVm0_dnode
            partials['vonmises', 'nodes'][ii+3:ii+6] = dVm0_dnode

            dVM1_dnode = dVm1dsxx1*dsxx1dP + dVm1dsxt*dsxtdP
            partials['vonmises', 'nodes'][ii+6:ii+9] = -dVM1_dnode
            partials['vonmises', 'nodes'][ii+9:ii+12] = dVM1_dnode
    def compute_partials(self, inputs, partials):

        radius = inputs['radius']
        disp = inputs['disp']
        nodes = inputs['nodes']
        T = self.T
        E = self.E
        G = self.G
        x_gl = self.x_gl

        num_elems = self.ny - 1
        for ielem in range(num_elems):

            # Compute the coordinate delta between the two element end points
            P0 = nodes[ielem, :]
            P1 = nodes[ielem + 1, :]
            dP = P1 - P0

            # Compute the derivative of element length
            L = norm(dP)
            dLddP = norm_d(dP)

            # unit function converts a vector to a unit vector
            # calculate the transormation to the local element frame.
            # We use x_gl to provide a reference axis to reference
            x_loc = unit(dP)
            dxdP = unit_d(dP)

            y_loc = unit(np.cross(x_loc, x_gl))
            dtmpdx, _ = cross_d(x_loc, x_gl)
            dydtmp = unit_d(np.cross(x_loc, x_gl))
            dydP = dydtmp.dot(dtmpdx).dot(dxdP)

            z_loc = unit(np.cross(x_loc, y_loc))
            dtmpdx, dtmpdy = cross_d(x_loc, y_loc)
            dzdtmp = unit_d(np.cross(x_loc, y_loc))
            dzdP = dzdtmp.dot(dtmpdx).dot(dxdP) + dzdtmp.dot(dtmpdy).dot(dydP)

            T[0, :] = x_loc
            T[1, :] = y_loc
            T[2, :] = z_loc

            u0x = x_loc.dot(disp[ielem, :3])
            r0x, r0y, r0z = T.dot(disp[ielem, 3:])
            u1x = x_loc.dot(disp[ielem + 1, :3])
            r1x, r1y, r1z = T.dot(disp[ielem + 1, 3:])

            # #$$$$$$$$$$$$$$$$$$$$$$$$$$

            # The derivatives of the above code wrt displacement all boil down to sections of the T matrix
            dxddisp = T[0, :]
            dyddisp = T[1, :]
            dzddisp = T[2, :]

            #The derivatives of the above code wrt T all boil down to sections of the #displacement vector
            du0dloc = disp[ielem, :3]
            dr0dloc = disp[ielem, 3:]
            du1dloc = disp[ielem + 1, :3]
            dr1dloc = disp[ielem + 1, 3:]

            #$$$$$$$$$$$$$$$$$$$$$$$$$$
            # Original code
            # $$$$$$$$$$$$
            tmp = np.sqrt(
                (r1y - r0y)**2 +
                (r1z - r0z)**2) + 1e-50  #added eps to avoid 0 disp singularity
            sxx0 = E * (u1x - u0x) / L + E * radius[ielem] / L * tmp
            sxx1 = E * (u0x - u1x) / L + E * radius[ielem] / L * tmp
            sxt = G * radius[ielem] * (r1x - r0x) / L
            # $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

            dtmpdr0y = 1 / tmp * (r1y - r0y) * -1
            dtmpdr1y = 1 / tmp * (r1y - r0y)
            dtmpdr0z = 1 / tmp * (r1z - r0z) * -1
            dtmpdr1z = 1 / tmp * (r1z - r0z)

            # Combine all of the derivtives for tmp
            dtmpdDisp = np.zeros(12)
            dr0xdDisp = np.zeros(12)
            dr1xdDisp = np.zeros(12)

            #r0 term
            dr0xdDisp[3:6] = dxddisp
            dr1xdDisp[9:12] = dxddisp

            dtmpdDisp[3:6] = dtmpdr0y * dyddisp
            dtmpdDisp[3:6] += dtmpdr0z * dzddisp
            dtmpdDisp[9:12] = dtmpdr1y * dyddisp
            dtmpdDisp[9:12] += dtmpdr1z * dzddisp

            # x_loc, y_loc and z_loc terms
            # (dttmpx_loc is zeros, so don't compute with it)
            dtmpdy_loc = dtmpdr0y * dr0dloc + dtmpdr1y * dr1dloc
            dtmpdz_loc = dtmpdr0z * dr0dloc + dtmpdr1z * dr1dloc

            dtmpdP = dtmpdy_loc.dot(dydP) + dtmpdz_loc.dot(dzdP)

            dsxx0dtmp = E * radius[ielem] / L
            dsxx0du0x = -E / L
            dsxx0du1x = E / L
            dsxx0dL = -E * (u1x -
                            u0x) / (L * L) - E * radius[ielem] / (L * L) * tmp

            dsxx1dtmp = E * radius[ielem] / L
            dsxx1du0x = E / L
            dsxx1du1x = -E / L
            dsxx1dL = -E * (u0x -
                            u1x) / (L * L) - E * radius[ielem] / (L * L) * tmp

            dsxx0dP = dsxx0dtmp * dtmpdP + \
                      dsxx0du0x*du0dloc.dot(dxdP) + dsxx0du1x*du1dloc.dot(dxdP)+\
                      dsxx0dL*dLddP

            dsxx1dP = dsxx1dtmp * dtmpdP + \
                      dsxx1du0x*du0dloc.dot(dxdP)+dsxx1du1x*du1dloc.dot(dxdP)+\
                      dsxx1dL*dLddP

            # Combine sxx0 and sxx1 terms

            # Start with the tmp term
            dsxx0dDisp = dsxx0dtmp * dtmpdDisp
            dsxx1dDisp = dsxx1dtmp * dtmpdDisp

            # Now add the direct u dep
            dsxx0dDisp[0:3] = dsxx0du0x * dxddisp
            dsxx0dDisp[6:9] = dsxx0du1x * dxddisp

            dsxx1dDisp[0:3] = dsxx1du0x * dxddisp
            dsxx1dDisp[6:9] = dsxx1du1x * dxddisp

            # Combine sxt term
            dsxtdr0x = -G * radius[ielem] / L
            dsxtdr1x = G * radius[ielem] / L
            dsxtdL = -G * radius[ielem] * (r1x - r0x) / (L * L)

            dsxtdP = dsxtdr0x*(dr0dloc.dot(dxdP)) + dsxtdr1x*(dr1dloc.dot(dxdP)) + \
                     dsxtdL*dLddP
            #disp
            dsxtdDisp = dsxtdr0x * dr0xdDisp + dsxtdr1x * dr1xdDisp

            #radius derivatives
            dsxxdrad = E / L * tmp
            dsxtdrad = G * (r1x - r0x) / L

            fact = 1.0 / (np.sqrt(sxx0**2 + 3 * sxt**2))
            dVm0dsxx0 = sxx0 * fact
            dVm0dsxt = 3 * sxt * fact

            fact = 1.0 / (np.sqrt(sxx1**2 + 3 * sxt**2))
            dVm1dsxx1 = sxx1 * fact
            dVm1dsxt = 3 * sxt * fact

            ii = 2 * ielem
            partials['vonmises',
                     'radius'][ii] = dVm0dsxx0 * dsxxdrad + dVm0dsxt * dsxtdrad
            partials['vonmises',
                     'radius'][ii +
                               1] = dVm1dsxx1 * dsxxdrad + dVm1dsxt * dsxtdrad

            ii = 24 * ielem
            partials[
                'vonmises',
                'disp'][ii:ii +
                        12] = dVm0dsxx0 * dsxx0dDisp + dVm0dsxt * dsxtdDisp
            partials[
                'vonmises',
                'disp'][ii + 12:ii +
                        24] = dVm1dsxx1 * dsxx1dDisp + dVm1dsxt * dsxtdDisp

            # Compute terms for the nodes
            ii = 12 * ielem

            dVm0_dnode = dVm0dsxx0 * dsxx0dP + dVm0dsxt * dsxtdP
            partials['vonmises', 'nodes'][ii:ii + 3] = -dVm0_dnode
            partials['vonmises', 'nodes'][ii + 3:ii + 6] = dVm0_dnode

            dVM1_dnode = dVm1dsxx1 * dsxx1dP + dVm1dsxt * dsxtdP
            partials['vonmises', 'nodes'][ii + 6:ii + 9] = -dVM1_dnode
            partials['vonmises', 'nodes'][ii + 9:ii + 12] = dVM1_dnode
Ejemplo n.º 17
0
    def compute_partials(self, inputs, partials):

        radius = inputs['radius']
        disp = inputs['disp']
        nodes = inputs['nodes']
        T = self.T
        E = self.E
        G = self.G
        x_gl = self.x_gl

        num_elems = self.ny - 1
        for ielem in range(num_elems):

            # Compute the coordinate delta between the two element end points
            P0 = nodes[ielem, :]
            P1 = nodes[ielem+1, :]
            dP = P1 - P0

            # and its derivatives
            ddPdP0 = -1.0*np.eye(3)
            ddPdP1 = 1.0*np.eye(3)

            # Compute the element length and its derivative
            L = norm(dP)
            dLddP = norm_d(dP)

            # unit function converts a vector to a unit vector
            # calculate the transormation to the local element frame.
            # We use x_gl to provide a reference axis to reference
            x_loc = unit(dP)
            dxdP = unit_d(dP)

            y_loc = unit(np.cross(x_loc, x_gl))
            dtmpdx,dummy = cross_d(x_loc,x_gl)
            dydtmp = unit_d(np.cross(x_loc, x_gl))
            dydP = dydtmp.dot(dtmpdx).dot(dxdP)

            z_loc = unit(np.cross(x_loc, y_loc))
            dtmpdx,dtmpdy = cross_d(x_loc,y_loc)
            dzdtmp = unit_d(np.cross(x_loc, y_loc))
            dzdP = dzdtmp.dot(dtmpdx).dot(dxdP)+dzdtmp.dot(dtmpdy).dot(dydP)

            T[0, :] = x_loc
            T[1, :] = y_loc
            T[2, :] = z_loc

            #$$$$$$$$$$$$$$$$$$$$$$$$$$
            # Original code
            # $$$$$$$$$$$$
            u0x, u0y, u0z = T.dot(disp[ielem, :3])
            r0x, r0y, r0z = T.dot(disp[ielem, 3:])
            u1x, u1y, u1z = T.dot(disp[ielem+1, :3])
            r1x, r1y, r1z = T.dot(disp[ielem+1, 3:])

            # #$$$$$$$$$$$$$$$$$$$$$$$$$$

            # The derivatives of the above code wrt displacement all boil down to sections of the T matrix
            dxddisp = T[0,:]
            dyddisp = T[1,:]
            dzddisp = T[2,:]

            #The derivatives of the above code wrt T all boil down to sections of the #displacement vector
            # du0dT = disp[ielem, :3]
            # dr0dT = disp[ielem, 3:]
            # du1dT = disp[ielem+1, :3]
            # dr1dT = disp[ielem+1, 3:]

            du0dloc = disp[ielem, :3]
            dr0dloc = disp[ielem, 3:]
            du1dloc = disp[ielem+1, :3]
            dr1dloc = disp[ielem+1, 3:]

            #$$$$$$$$$$$$$$$$$$$$$$$$$$
            # Original code
            # $$$$$$$$$$$$
            tmp = np.sqrt((r1y - r0y)**2 + (r1z - r0z)**2) + 1e-50 #added eps to avoid 0 disp singularity
            sxx0 = E * (u1x - u0x) / L + E * radius[ielem] / L * tmp
            sxx1 = E * (u0x - u1x) / L + E * radius[ielem] / L * tmp
            sxt = G * radius[ielem] * (r1x - r0x) / L
            # $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

            dtmpdr0y = 1/tmp * (r1y - r0y)*-1
            dtmpdr1y = 1/tmp * (r1y - r0y)
            dtmpdr0z = 1/tmp * (r1z - r0z)*-1
            dtmpdr1z = 1/tmp * (r1z - r0z)

            # Combine all of the derivtives for tmp
            dtmpdDisp = np.zeros(2 * 6)
            dr0xdDisp = np.zeros(2*6)
            dr0ydDisp = np.zeros(2*6)
            dr0zdDisp = np.zeros(2*6)
            dr1xdDisp = np.zeros(2*6)
            dr1ydDisp = np.zeros(2*6)
            dr1zdDisp = np.zeros(2*6)

            idx1 = 3
            idx2 = 6 + 3
            #r0 term
            dr0xdDisp[idx1:idx1+3] = dxddisp
            dr0ydDisp[idx1:idx1+3] = dyddisp
            dr0zdDisp[idx1:idx1+3] = dzddisp
            dr1xdDisp[idx2:idx2+3] = dxddisp
            dr1ydDisp[idx2:idx2+3] = dyddisp
            dr1zdDisp[idx2:idx2+3] = dzddisp
            dtmpdDisp = (dtmpdr0y*dr0ydDisp+dtmpdr1y*dr1ydDisp+\
                        dtmpdr0z*dr0zdDisp+dtmpdr1z*dr1zdDisp)

            # x_loc, y_loc and z_loc terms
            dtmpdx_loc = np.array([0,0,0])
            dtmpdy_loc = dtmpdr0y*dr0dloc + dtmpdr1y*dr1dloc
            dtmpdz_loc = dtmpdr0z*dr0dloc + dtmpdr1z*dr1dloc

            dtmpdP = dtmpdx_loc.dot(dxdP) + dtmpdy_loc.dot(dydP) + dtmpdz_loc.dot(dzdP)

            dsxx0dtmp = E * radius[ielem] / L
            dsxx0du0x = -E / L
            dsxx0du1x = E / L
            dsxx0dL =  -E * (u1x - u0x) / (L*L) - E * radius[ielem] / (L*L) * tmp

            dsxx1dtmp = E * radius[ielem] / L
            dsxx1du0x = E / L
            dsxx1du1x = -E / L
            dsxx1dL = -E * (u0x - u1x) / (L*L) - E * radius[ielem] / (L*L) * tmp

            dsxx0dP = dsxx0dtmp * dtmpdP + \
                      dsxx0du0x*du0dloc.dot(dxdP) + dsxx0du1x*du1dloc.dot(dxdP)+\
                      dsxx0dL*dLddP

            dsxx1dP = dsxx1dtmp * dtmpdP + \
                      dsxx1du0x*du0dloc.dot(dxdP)+dsxx1du1x*du1dloc.dot(dxdP)+\
                      dsxx1dL*dLddP

            # Combine sxx0 and sxx1 terms
            idx1 = 0
            idx2 = 6
            # Start with the tmp term
            dsxx0dDisp = dsxx0dtmp * dtmpdDisp
            dsxx1dDisp = dsxx1dtmp * dtmpdDisp

            # Now add the direct u dep
            dsxx0dDisp[idx1:idx1+3] = dsxx0du0x * dxddisp
            dsxx0dDisp[idx2:idx2+3] = dsxx0du1x * dxddisp

            dsxx1dDisp[idx1:idx1+3] = dsxx1du0x * dxddisp
            dsxx1dDisp[idx2:idx2+3] = dsxx1du1x * dxddisp

            # Combine sxt term
            dsxtdDisp = np.zeros(2 * 6)
            idx1 = 3
            idx2 = 6 + 3

            dsxtdr0x = -G * radius[ielem] / L
            dsxtdr1x = G * radius[ielem] / L
            dsxtdL =  - G * radius[ielem] * (r1x - r0x) / (L*L)

            dsxtdP = dsxtdr0x*(dr0dloc.dot(dxdP))+ dsxtdr1x*(dr1dloc.dot(dxdP))+\
                     dsxtdL*dLddP
            #disp
            dsxtdDisp= dsxtdr0x * dr0xdDisp + dsxtdr1x * dr1xdDisp

            #radius derivatives
            dsxx0drad = E / L * tmp
            dsxx1drad = E / L * tmp
            dsxtdrad = G * (r1x - r0x)/L

            dVm0dsxx0 = (sxx0)/(np.sqrt(sxx0**2 + 3 * sxt**2))
            dVm0dsxt = (3*sxt)/(np.sqrt(sxx0**2 + 3 * sxt**2))
            dVm1dsxx1 = (sxx1)/(np.sqrt(sxx1**2 + 3 * sxt**2))
            dVm1dsxt = (3*sxt)/(np.sqrt(sxx1**2 + 3 * sxt**2))

            idx = ielem*2
            partials['vonmises','radius'][idx,ielem] = dVm0dsxx0*dsxx0drad+dVm0dsxt*dsxtdrad
            partials['vonmises','radius'][idx+1,ielem] = dVm1dsxx1*dsxx1drad+dVm1dsxt*dsxtdrad

            idx2 = ielem*6
            partials['vonmises','disp'][idx,idx2:idx2+12] = (dVm0dsxx0*dsxx0dDisp+dVm0dsxt*dsxtdDisp )
            partials['vonmises','disp'][idx+1,idx2:idx2+12] = (dVm1dsxx1*dsxx1dDisp+dVm1dsxt*dsxtdDisp )

            # Compute terms for the nodes
            idx3 = ielem*3

            partials['vonmises','nodes'][idx,idx3:idx3+3] = dVm0dsxx0*dsxx0dP.dot(ddPdP0)+dVm0dsxt*dsxtdP.dot(ddPdP0)
            partials['vonmises','nodes'][idx,idx3+3:idx3+6] = dVm0dsxx0*dsxx0dP.dot(ddPdP1)+dVm0dsxt*dsxtdP.dot(ddPdP1)

            partials['vonmises','nodes'][idx+1,idx3:idx3+3] = dVm1dsxx1*dsxx1dP.dot(ddPdP0)+dVm1dsxt*dsxtdP.dot(ddPdP0)
            partials['vonmises','nodes'][idx+1,idx3+3:idx3+6] = dVm1dsxx1*dsxx1dP.dot(ddPdP1)+dVm1dsxt*dsxtdP.dot(ddPdP1)
    def compute(self, inputs, outputs):
        mesh = inputs['mesh']
        vectors = mesh[-1, :, :] - mesh[0, :, :]
        streamwise_chords = np.sqrt(np.sum(vectors**2, axis=1))
        streamwise_chords = 0.5 * streamwise_chords[:-1] + 0.5 * streamwise_chords[1:]

        # Chord lengths for the panel strips at the panel midpoint
        outputs['streamwise_chords'] = streamwise_chords.copy()

        fem_twists = np.zeros(streamwise_chords.shape, dtype=type(mesh[0, 0, 0]))
        fem_chords = streamwise_chords.copy()

        surface = self.surface

        # Gets the shear center by looking at the four corners.
        # Assumes same spar thickness for front and rear spar.
        w = (surface['data_x_upper'][0] *(surface['data_y_upper'][0]-surface['data_y_lower'][0]) + \
        surface['data_x_upper'][-1]*(surface['data_y_upper'][-1]-surface['data_y_lower'][-1])) / \
        ( (surface['data_y_upper'][0]-surface['data_y_lower'][0]) + (surface['data_y_upper'][-1]-surface['data_y_lower'][-1]))

        # TODO: perhaps replace this or link with existing nodes computation
        nodes = (1-w) * mesh[0, :, :] + w * mesh[-1, :, :]

        mesh_vectors = mesh[-1, :, :] - mesh[0, :, :]

        # Loop over spanwise elements
        for ielem in range(mesh.shape[1] - 1):

            # Obtain the element nodes
            P0 = nodes[ielem, :]
            P1 = nodes[ielem+1, :]

            elem_vec = (P1 - P0) # vector along element
            temp_vec = elem_vec.copy()
            temp_vec[0] = 0. # vector along element without x component

            # This is used to get chord length normal to FEM element.
            # To be clear, this 3D angle sweep measure.
            # This is the projection to the wing orthogonal to the FEM direction.
            cos_theta_fe_sweep = norm(temp_vec) / norm(elem_vec)
            fem_chords[ielem] = fem_chords[ielem] * cos_theta_fe_sweep

        outputs['fem_chords'] = fem_chords

        # Loop over spanwise elements
        for ielem in range(mesh.shape[1] - 1):

            # The following is used to approximate the twist angle for the section normal to the FEM element
            mesh_vec_0 = mesh_vectors[ielem]
            temp_mesh_vectors_0 = mesh_vec_0.copy()
            temp_mesh_vectors_0[2] = 0.

            cos_twist_0 = norm(temp_mesh_vectors_0) / norm(mesh_vec_0)

            if cos_twist_0 > 1.:
                theta_0 = 0. # to prevent nan in case value for arccos is greater than 1 due to machine precision
            else:
                theta_0 = np.arccos(cos_twist_0)

            mesh_vec_1 = mesh_vectors[ielem + 1]
            temp_mesh_vectors_1 = mesh_vec_1.copy()
            temp_mesh_vectors_1[2] = 0.

            cos_twist_1 = norm(temp_mesh_vectors_1) / norm(mesh_vec_1)

            if cos_twist_1 > 1.:
                theta_1 = 0. # to prevent nan in case value for arccos is greater than 1 due to machine precision
            else:
                theta_1 = np.arccos(cos_twist_1)

            fem_twists[ielem] = (theta_0 + theta_1) / 2 * streamwise_chords[ielem] / fem_chords[ielem]
        outputs['fem_twists'] = fem_twists
    def compute_partials(self, inputs, J):

        struct_weights = inputs['element_mass'] * inputs[
            'load_factor'] * grav_constant
        nodes = inputs['nodes']

        element_lengths = norm(nodes[1:, :] - nodes[:-1, :], axis=1)

        # And we also need the deltas between consecutive nodes
        deltas = nodes[1:, :] - nodes[:-1, :]
        # save these slices cause I use them alot
        del0 = deltas[:, 0]
        del1 = deltas[:, 1]

        # Assume weight coincides with the elastic axis
        z_moments_for_each = struct_weights / 12. \
                            * (del0**2 + del1**2)**0.5

        dzf__dew = .5 * inputs['load_factor'][0]
        dzf__dlf = inputs['element_mass'] / 2.

        dzm__dew = diags((del0**2 + del1**2)**0.5 / 12 * inputs['load_factor'])
        dzm__dlf = (del0**2 + del1**2)**.5 / 12. * inputs['element_mass']

        dbm3__dzm = diags(del1 / element_lengths)
        dbm4__dzm = diags(del0 / element_lengths)

        # need to convert to lil to re-order the data to match the original row/col indexing from setup
        dswl__dlf = (-self.dswl__dbm3*dbm3__dzm*coo_matrix(dzm__dlf).T +\
                    -self.dswl__dbm4*dbm4__dzm*coo_matrix(dzm__dlf).T +\
                    self.dswl__dzf*coo_matrix(dzf__dlf).T).tolil()

        J['struct_weight_loads', 'load_factor'] = dswl__dlf[
            self.dswl__dlf_row,
            self.dswl__dlf_col].toarray().flatten() * grav_constant

        dswl__dew = (-self.dswl__dbm4 * dbm4__dzm * dzm__dew +\
                    -self.dswl__dbm3 * dbm3__dzm * dzm__dew +\
                    self.dswl__dzf  * dzf__dew).tolil()

        data = dswl__dew[self.dswl__dew_pattern.row,
                         self.dswl__dew_pattern.col].toarray().flatten()
        J['struct_weight_loads', 'element_mass'] = data * grav_constant

        # dstruct_weight_loads__dnodes (this one is super complicated)

        # del__dnodes
        # note: del__dnodes matrix already created in setup, just need to set data
        raw_data = diags(1 / element_lengths) * (nodes[1:, :] - nodes[:-1, :])
        data = np.hstack((-raw_data, raw_data)).flatten()
        self.del__dnodes.data = data

        dzm_dnodes = diags(struct_weights/12*(del0**2 + del1**2)**-.5)*\
                     (diags(del0)*self.ddel0__dnodes  + diags(del1)*self.ddel1__dnodes)

        dbm3_dnodes = diags(del1/element_lengths)*dzm_dnodes \
                    + diags(z_moments_for_each/element_lengths)*self.ddel1__dnodes \
                    - diags(z_moments_for_each*del1/element_lengths**2)*self.del__dnodes


        dbm4_dnodes = diags(del0/element_lengths)*dzm_dnodes \
                    + diags(z_moments_for_each/element_lengths)*self.ddel0__dnodes \
                    - diags(z_moments_for_each*del0/element_lengths**2)*self.del__dnodes

        # this is kind of dumb, but I need lil cause I have to re-index to preserve order
        #     the coo column ordering doesn't seem to be deterministic
        #     so I use the original row/col from the pattern as index arrays to
        #     pull the data out in the correct order
        dswl__dnodes = (self.dswl__dbm3 * dbm3_dnodes +
                        self.dswl__dbm4 * dbm4_dnodes).tolil()
        data = dswl__dnodes[self.dswl__dnodes_pattern.row,
                            self.dswl__dnodes_pattern.col].toarray().flatten()
        J['struct_weight_loads', 'nodes'] = -data
Ejemplo n.º 20
0
    def compute_partials(self, inputs, partials):

        radius = inputs['radius']
        disp = inputs['disp']
        nodes = inputs['nodes']
        T = self.T
        E = self.E
        G = self.G
        x_gl = self.x_gl

        num_elems = self.ny - 1
        for ielem in range(num_elems):

            # Compute the coordinate delta between the two element end points
            P0 = nodes[ielem, :]
            P1 = nodes[ielem + 1, :]
            dP = P1 - P0

            # and its derivatives
            ddPdP0 = -1.0 * np.eye(3)
            ddPdP1 = 1.0 * np.eye(3)

            # Compute the element length and its derivative
            L = norm(dP)
            dLddP = norm_d(dP)

            # unit function converts a vector to a unit vector
            # calculate the transormation to the local element frame.
            # We use x_gl to provide a reference axis to reference
            x_loc = unit(dP)
            dxdP = unit_d(dP)

            y_loc = unit(np.cross(x_loc, x_gl))
            dtmpdx, dummy = cross_d(x_loc, x_gl)
            dydtmp = unit_d(np.cross(x_loc, x_gl))
            dydP = dydtmp.dot(dtmpdx).dot(dxdP)

            z_loc = unit(np.cross(x_loc, y_loc))
            dtmpdx, dtmpdy = cross_d(x_loc, y_loc)
            dzdtmp = unit_d(np.cross(x_loc, y_loc))
            dzdP = dzdtmp.dot(dtmpdx).dot(dxdP) + dzdtmp.dot(dtmpdy).dot(dydP)

            T[0, :] = x_loc
            T[1, :] = y_loc
            T[2, :] = z_loc

            #$$$$$$$$$$$$$$$$$$$$$$$$$$
            # Original code
            # $$$$$$$$$$$$
            u0x, u0y, u0z = T.dot(disp[ielem, :3])
            r0x, r0y, r0z = T.dot(disp[ielem, 3:])
            u1x, u1y, u1z = T.dot(disp[ielem + 1, :3])
            r1x, r1y, r1z = T.dot(disp[ielem + 1, 3:])

            # #$$$$$$$$$$$$$$$$$$$$$$$$$$

            # The derivatives of the above code wrt displacement all boil down to sections of the T matrix
            dxddisp = T[0, :]
            dyddisp = T[1, :]
            dzddisp = T[2, :]

            #The derivatives of the above code wrt T all boil down to sections of the #displacement vector
            # du0dT = disp[ielem, :3]
            # dr0dT = disp[ielem, 3:]
            # du1dT = disp[ielem+1, :3]
            # dr1dT = disp[ielem+1, 3:]

            du0dloc = disp[ielem, :3]
            dr0dloc = disp[ielem, 3:]
            du1dloc = disp[ielem + 1, :3]
            dr1dloc = disp[ielem + 1, 3:]

            #$$$$$$$$$$$$$$$$$$$$$$$$$$
            # Original code
            # $$$$$$$$$$$$
            tmp = np.sqrt((r1y - r0y)**2 + (r1z - r0z)**2)
            sxx0 = E * (u1x - u0x) / L + E * radius[ielem] / L * tmp
            sxx1 = E * (u0x - u1x) / L + E * radius[ielem] / L * tmp
            sxt = G * radius[ielem] * (r1x - r0x) / L
            # $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

            dtmpdr0y = 1 / tmp * (r1y - r0y) * -1
            dtmpdr1y = 1 / tmp * (r1y - r0y)
            dtmpdr0z = 1 / tmp * (r1z - r0z) * -1
            dtmpdr1z = 1 / tmp * (r1z - r0z)

            # Combine all of the derivtives for tmp
            dtmpdDisp = np.zeros(2 * 6)
            dr0xdDisp = np.zeros(2 * 6)
            dr0ydDisp = np.zeros(2 * 6)
            dr0zdDisp = np.zeros(2 * 6)
            dr1xdDisp = np.zeros(2 * 6)
            dr1ydDisp = np.zeros(2 * 6)
            dr1zdDisp = np.zeros(2 * 6)

            idx1 = 3
            idx2 = 6 + 3
            #r0 term
            dr0xdDisp[idx1:idx1 + 3] = dxddisp
            dr0ydDisp[idx1:idx1 + 3] = dyddisp
            dr0zdDisp[idx1:idx1 + 3] = dzddisp
            dr1xdDisp[idx2:idx2 + 3] = dxddisp
            dr1ydDisp[idx2:idx2 + 3] = dyddisp
            dr1zdDisp[idx2:idx2 + 3] = dzddisp
            dtmpdDisp = (dtmpdr0y*dr0ydDisp+dtmpdr1y*dr1ydDisp+\
                        dtmpdr0z*dr0zdDisp+dtmpdr1z*dr1zdDisp)

            # x_loc, y_loc and z_loc terms
            dtmpdx_loc = np.array([0, 0, 0])
            dtmpdy_loc = dtmpdr0y * dr0dloc + dtmpdr1y * dr1dloc
            dtmpdz_loc = dtmpdr0z * dr0dloc + dtmpdr1z * dr1dloc

            dtmpdP = dtmpdx_loc.dot(dxdP) + dtmpdy_loc.dot(
                dydP) + dtmpdz_loc.dot(dzdP)

            dsxx0dtmp = E * radius[ielem] / L
            dsxx0du0x = -E / L
            dsxx0du1x = E / L
            dsxx0dL = -E * (u1x -
                            u0x) / (L * L) - E * radius[ielem] / (L * L) * tmp

            dsxx1dtmp = E * radius[ielem] / L
            dsxx1du0x = E / L
            dsxx1du1x = -E / L
            dsxx1dL = -E * (u0x -
                            u1x) / (L * L) - E * radius[ielem] / (L * L) * tmp

            dsxx0dP = dsxx0dtmp * dtmpdP + \
                      dsxx0du0x*du0dloc.dot(dxdP) + dsxx0du1x*du1dloc.dot(dxdP)+\
                      dsxx0dL*dLddP

            dsxx1dP = dsxx1dtmp * dtmpdP + \
                      dsxx1du0x*du0dloc.dot(dxdP)+dsxx1du1x*du1dloc.dot(dxdP)+\
                      dsxx1dL*dLddP

            # Combine sxx0 and sxx1 terms
            idx1 = 0
            idx2 = 6
            # Start with the tmp term
            dsxx0dDisp = dsxx0dtmp * dtmpdDisp
            dsxx1dDisp = dsxx1dtmp * dtmpdDisp

            # Now add the direct u dep
            dsxx0dDisp[idx1:idx1 + 3] = dsxx0du0x * dxddisp
            dsxx0dDisp[idx2:idx2 + 3] = dsxx0du1x * dxddisp

            dsxx1dDisp[idx1:idx1 + 3] = dsxx1du0x * dxddisp
            dsxx1dDisp[idx2:idx2 + 3] = dsxx1du1x * dxddisp

            # Combine sxt term
            dsxtdDisp = np.zeros(2 * 6)
            idx1 = 3
            idx2 = 6 + 3

            dsxtdr0x = -G * radius[ielem] / L
            dsxtdr1x = G * radius[ielem] / L
            dsxtdL = -G * radius[ielem] * (r1x - r0x) / (L * L)

            dsxtdP = dsxtdr0x*(dr0dloc.dot(dxdP))+ dsxtdr1x*(dr1dloc.dot(dxdP))+\
                     dsxtdL*dLddP
            #disp
            dsxtdDisp = dsxtdr0x * dr0xdDisp + dsxtdr1x * dr1xdDisp

            #radius derivatives
            dsxx0drad = E / L * tmp
            dsxx1drad = E / L * tmp
            dsxtdrad = G * (r1x - r0x) / L

            dVm0dsxx0 = (sxx0) / (np.sqrt(sxx0**2 + 3 * sxt**2))
            dVm0dsxt = (3 * sxt) / (np.sqrt(sxx0**2 + 3 * sxt**2))
            dVm1dsxx1 = (sxx1) / (np.sqrt(sxx1**2 + 3 * sxt**2))
            dVm1dsxt = (3 * sxt) / (np.sqrt(sxx1**2 + 3 * sxt**2))

            idx = ielem * 2
            partials['vonmises', 'radius'][
                idx, ielem] = dVm0dsxx0 * dsxx0drad + dVm0dsxt * dsxtdrad
            partials['vonmises', 'radius'][
                idx + 1, ielem] = dVm1dsxx1 * dsxx1drad + dVm1dsxt * dsxtdrad

            idx2 = ielem * 6
            partials['vonmises','disp'][idx,idx2:idx2+12] = partials['vonmises','disp'][idx,idx2:idx2+12]+\
                                                            (dVm0dsxx0*dsxx0dDisp+dVm0dsxt*dsxtdDisp )
            partials['vonmises','disp'][idx+1,idx2:idx2+12] = partials['vonmises','disp'][idx+1,idx2:idx2+12]+\
                                                              (dVm1dsxx1*dsxx1dDisp+dVm1dsxt*dsxtdDisp )

            # Compute terms for the nodes
            idx3 = ielem * 3

            partials['vonmises', 'nodes'][idx, idx3:idx3 + 3] = partials[
                'vonmises',
                'nodes'][idx, idx3:idx3 + 3] + dVm0dsxx0 * dsxx0dP.dot(
                    ddPdP0) + dVm0dsxt * dsxtdP.dot(ddPdP0)
            partials['vonmises', 'nodes'][idx, idx3 + 3:idx3 + 6] = partials[
                'vonmises',
                'nodes'][idx, idx3 + 3:idx3 + 6] + dVm0dsxx0 * dsxx0dP.dot(
                    ddPdP1) + dVm0dsxt * dsxtdP.dot(ddPdP1)

            partials['vonmises', 'nodes'][idx + 1, idx3:idx3 + 3] = partials[
                'vonmises',
                'nodes'][idx + 1, idx3:idx3 + 3] + dVm1dsxx1 * dsxx1dP.dot(
                    ddPdP0) + dVm1dsxt * dsxtdP.dot(ddPdP0)
            partials['vonmises',
                     'nodes'][idx + 1, idx3 + 3:idx3 +
                              6] = partials['vonmises', 'nodes'][
                                  idx + 1,
                                  idx3 + 3:idx3 + 6] + dVm1dsxx1 * dsxx1dP.dot(
                                      ddPdP1) + dVm1dsxt * dsxtdP.dot(ddPdP1)