def first_piola_kirchhoff_stress(cls, material, deformation_gradient, dimension=3, test=False):
        """Compute the first Piola-Kirchhoff stress for the material from the deformation gradient under
        the specified assumptions.

        :param material: material to which the deformation gradient applies
        :param numpy.ndarray deformation_gradient: 3x3 matrix describing the deformation of the body
        :param int dimension: desired dimension of the returned matrix
        :param bool test: whether to perform the verification test for the stress result
        """
        result = (
            (material.first_lame_parameter * math.log(numpy.linalg.det(deformation_gradient)) - material.shear_modulus)
            * operations.inverse_transpose(deformation_gradient)
            + material.shear_modulus * deformation_gradient)
        # Verify the correctness of this result by comparing to numerical differentiation
        if test:
            tests.numerical_differentiation_first_piola_kirchhoff_stress(constitutive_model=cls,
                                                                         material=material,
                                                                         deformation_gradient=deformation_gradient,
                                                                         first_piola_kirchhoff_stress=result)
        # Return a 2x2 matrix if requested for plane stress:
        if dimension == 2:
            return result[0:2, 0:2]
        # Otherwise return the full 3x3 matrix
        else:
            return result
def error_testing():
    # NOTE: this test will result in an error for the default tolerance value, because this function is intended
    # to violate the tolerance for the purposes of showing the behavior of the error as a function of h.
    deformation_gradient = operations.generate_random_deformation_gradient()  # uncomment for "bad" F: - numpy.eye(3)
    material = materials.Custom(name='custom material', first_lame_parameter=5, shear_modulus=3)
    constitutive_model = constitutive_models.Neohookean()
    (strain_energy_density,
     first_piola_kirchhoff_stress,
     tangent_moduli) = constitutive_model.calculate_all(material=material,
                                                        deformation_gradient=deformation_gradient)
    h_values = list(numpy.logspace(-2, -10, 100))
    p_errors = []
    c_errors = []
    for h_value in h_values:
        # NOTE these functions don't normally return these values
        p_error = tests.numerical_differentiation_first_piola_kirchhoff_stress(constitutive_model=constitutive_model,
                                                                               material=material,
                                                                               deformation_gradient=deformation_gradient,
                                                                               first_piola_kirchhoff_stress=first_piola_kirchhoff_stress,
                                                                               h=h_value)
        c_error = tests.numerical_differentiation_tangent_moduli(constitutive_model=constitutive_model,
                                                                 material=material,
                                                                 deformation_gradient=deformation_gradient,
                                                                 tangent_moduli=tangent_moduli,
                                                                 h=h_value)
        p_errors.append(p_error)
        c_errors.append(c_error)
    plt.figure()
    plt.plot(h_values, p_errors, 'b', label='P error')
    plt.plot(h_values, c_errors, 'r', label='C error')
    plt.xscale('log')
    plt.yscale('log')
    plt.ylim(10e-10, 10e-3)
    plt.title('3-point formula errors for "good" deformation gradient')
    plt.xlabel('h')
    plt.ylabel('error')
    plt.legend(loc='best')
    plt.show()