def calculate_internal_force_array_old(self, test=False):
        """Computes the 2D internal nodal force array for the element for the current configuration using Gauss
        quadrature. Runs for each deformed configuration in the analysis.

        OLD: uses the inverse jacobian and the first Piola-Kirchhoff stress, and is in 2D.

        :param bool test: whether to perform numerical differentiation check on the result
        """
        # Initialize force array to be computed using Gauss quadrature
        force_array = numpy.zeros((self.degrees_of_freedom, self.node_quantity))
        # Sum over quadrature points
        for quadrature_point in self.quadrature_points:
            # Initialize integrand to be computed for this quadrature point
            integrand = numpy.zeros((self.degrees_of_freedom, self.node_quantity))
            for dof_1 in range(self.degrees_of_freedom):
                for node_index in range(self.node_quantity):
                    # Sum over repeated indices
                    for dof_2 in range(self.degrees_of_freedom):
                        for coordinate_index in range(self.dimension):
                            integrand[dof_1][node_index] += (
                                quadrature_point.first_piola_kirchhoff_stress[dof_1][dof_2]
                                * self.shape_function_derivatives(
                                    node_index=node_index, position=quadrature_point.position,
                                    coordinate_index=coordinate_index)
                                * self.jacobian_matrix_inverse[coordinate_index][dof_2])
            # Weight the integrand
            integrand *= quadrature_point.weight
            # Add the integrand to the internal_force_array
            force_array += integrand
        # Scale the force array for isoparametric triangle and multiply by the thickness (assumed to be constant)
        force_array *= .5 * self.thickness
        if test:
            tests.numerical_differentiation_force_array(element=self, force_array=force_array)
        return force_array
    def calculate_internal_force_array(self, test=False):
        """Computes the 3D internal nodal force array for the element for the current configuration using Gauss
        quadrature. Runs for each deformed configuration in the analysis.

        Uses the residual force, Kirchhoff stress, and curvilinear coordinate system.

        :param bool test: whether to perform numerical differentiation check on the result
        """
        # Initialize force array
        dimensions = (self.degrees_of_freedom, self.node_quantity)
        internal_force_array = numpy.zeros(dimensions)
        # Sum over quadrature points
        for quadrature_point in self.quadrature_points:
            # Initialize integrand to be computed for this quadrature point
            integrand = numpy.zeros((self.degrees_of_freedom, self.node_quantity))
            for dof_1 in range(self.degrees_of_freedom):
                for node_index in range(self.node_quantity):
                    # Sum over repeated indices
                    for dof_2 in range(self.degrees_of_freedom):
                        for coordinate_index in range(self.dimension):
                            integrand[dof_1][node_index] += (
                                quadrature_point.kirchhoff_stress[coordinate_index][dof_2]
                                * quadrature_point.current_configuration.basis[dof_2][dof_1]
                                * self.shape_function_derivatives(node_index=node_index,
                                                                  position=quadrature_point.position,
                                                                  coordinate_index=coordinate_index))
            # Weight the integrand
            integrand *= quadrature_point.weight
            # Add the integrand to the internal_force_array
            internal_force_array += integrand
        # Scale the force array for isoparametric triangle and multiply by the thickness and differential area
        internal_force_array *= .5 * self.thickness * self.reference_configuration.differential_area
        if test:
            tests.numerical_differentiation_force_array(element=self, force_array=internal_force_array)
        return internal_force_array