Repository: KratosMultiphysics/Kratos (C++)

Issue:
[GeoMechanicsApplication] Create MC shear and pressure capacity output.
To facilitate development of C-Phi reduction and to have generic output capabilities for geo-mechanical analysis, output that enables capacity checking on integration point stress level is desired. To compute this also Lode angle and its output are desired.

Definitions:
p = trace( sigma ) / 3
Lode angle theta = 1/3 * arcsin( -27/2 (sigma_1- p)(sigma_2 -p)(sigma_3 -p) / q^3)
q = von Mises equivalent stress.

shear capacity q / q_mc
q_mc = 3 ( p sin(Phi) + C cos(Phi) ) / ( sqrt(3) * cos(theta) - sin(theta) * sin(Phi) )
pressure capacity 3 sin(Phi ( q_mc - q ) / ( sqrt(3) * cos(theta) - sin(theta) * sin(Phi) )



Fix (patch):
diff --git a/applications/GeoMechanicsApplication/custom_utilities/README.md b/applications/GeoMechanicsApplication/custom_utilities/README.md
index 19f366e53763..f53ec2fb5315 100644
--- a/applications/GeoMechanicsApplication/custom_utilities/README.md
+++ b/applications/GeoMechanicsApplication/custom_utilities/README.md
@@ -5,8 +5,17 @@
 
 Utilities are developed to calculate matrices and vectors in transport equations
 
-![Image](https://github.com/KratosMultiphysics/Kratos/assets/56549273/296486b0-9e5e-408f-9839-aef8d8c7e720)
-
+$$ \begin{bmatrix} M & 0 \\
+                   0 & 0 \end{bmatrix} \begin{bmatrix} \ddot{u} \\
+                                                       \ddot{p} \end{bmatrix}  +
+   \begin{bmatrix} D & 0 \\
+                   Q^T & C \end{bmatrix} \begin{bmatrix} \dot{u} \\
+                                                          \dot{p} \end{bmatrix}  +
+   \begin{bmatrix} K & -Q \\
+                   0 & H \end{bmatrix} \begin{bmatrix} u \\
+                                                       p \end{bmatrix} =
+   \begin{bmatrix} f_u \\
+                   f_p \end{bmatrix} $$
 
 ### Permeability matrix (H)
 
@@ -28,9 +37,74 @@ File transport_equation_utilities.hpp includes
 -  CalculatePermeabilityMatrix function
 -  CalculateCompressibilityMatrix function
 
+## Stress strain utilities
+
+For convenience functions that compute invariants, equivalents and strain definitions.
+Given a stress tensor $\sigma$ or a strain tensor $\epsilon$. The eigenvalues of the stress tensor are $\sigma_1 \le \sigma_2 \le \sigma_3$
+
+$$\sigma = \begin{bmatrix} \sigma_{xx} & \sigma_{xy} & \sigma_{xz} \\
+                           \sigma_{xy} & \sigma_{yy} & \sigma_{yz} \\
+                           \sigma_{xz} & \sigma_{yz} & \sigma_{zz}  \end{bmatrix}$$
+
+$$\epsilon = \begin{bmatrix} \epsilon_{xx} & \epsilon_{xy} & \epsilon_{xz} \\
+                             \epsilon_{xy} & \epsilon_{yy} & \epsilon_{yz} \\
+                             \epsilon_{xz} & \epsilon_{yz} & \epsilon_{zz}  \end{bmatrix}$$
+
+### Trace
+
+The first tensor invariant:
+
+$$I_1 = trace(\sigma) = \Sigma_i \sigma_{i,i}$$
+
+### Mean stress
+
+$$p = \frac{1}{3} I_1 = \frac{1}{3} trace ( \sigma )$$
+
+### Von Mises stress
+
+With $J_2$ the second invariant of the tensor:
+
+$$\overline\sigma = \sqrt{3 J_2} = \sqrt{0.5((\sigma_{xx}-\sigma_{yy})^2 +
+                                 (\sigma_{yy}-\sigma_{zz})^2 +
+                                 (\sigma_{zz}-\sigma_{xx})^2 ) +
+                            3.0(\sigma_{xy}^2 + \sigma_{yz}^2 + \sigma_{xz}^2) }$$
+
+### Von Mises strain
+
+$$\overline{\epsilon} = \frac{2}{3} \sqrt{3 J_2}$$
+
+### Green Lagrange strain tensor
+
+With current configuration $x$ and reference configuration $X$, the deformation gradient $F = \frac{x}{X}$ and unit tensor $I$:
+
+$$\epsilon = 0.5 ( F^T \cdot F - I )$$
+
+### Hencky strain tensor
+
+$$\epsilon = 0.5 \ln ( F^T \cdot F )$$
+
+### Lode angle
+
+The negative sine definition for Lode angle is adapted here [Lode coordinates Wikipedia](https://en.wikipedia.org/wiki/Lode_coordinates):
+
+$$- \sin( 3 \bar{\theta}_s) = \frac{J_3}{2} (\frac{3}{J_2})^{\frac{3}{2}}$$
+
+which brings:
+
+$$\bar{\theta}_s = \frac{1}{3} \arcsin( - \frac{27}{2} \frac{(\sigma_1 - p)(\sigma_2 - p)(\sigma_3 - p)}{q^3})$$
+
+### Mohr Coulomb shear capacity
+
+Assessment of how the current stress utilizes the capacity as defined by the Mohr Coulomb yield surface.
 
+$$\frac{q}{q_{mc}}$$
 
+where
 
+$$q_{mc} = \frac{3}{\sqrt{3}\cos \bar{\theta}_s - \sin \bar{\theta}_s \sin \phi }(p \sin \phi + c \cos \phi)$$
 
+### Mohr Coulomb pressure capacity
 
+Assessment of how the current stress utilizes the capacity as defined by the Mohr Coulomb yield surface.
 
+$$(q_{mc} - q)\frac{3 \sin{\phi}}{\sqrt{3} \cos{\bar{\theta}_s} - \sin{\bar{\theta}_s} \sin{\phi}}$$
diff --git a/applications/GeoMechanicsApplication/custom_utilities/stress_strain_utilities.cpp b/applications/GeoMechanicsApplication/custom_utilities/stress_strain_utilities.cpp
index e0a328355a0c..1af789f9b8ea 100644
--- a/applications/GeoMechanicsApplication/custom_utilities/stress_strain_utilities.cpp
+++ b/applications/GeoMechanicsApplication/custom_utilities/stress_strain_utilities.cpp
@@ -13,14 +13,15 @@
 #include "stress_strain_utilities.h"
 #include "custom_utilities/math_utilities.hpp"
 #include "geo_mechanics_application_constants.h"
+#include <cmath>
 
 namespace Kratos
 {
 
-double StressStrainUtilities::CalculateVonMisesStress(const Vector& StressVector)
+double StressStrainUtilities::CalculateVonMisesStress(const Vector& rStressVector)
 {
-    Matrix LocalStressTensor =
-        MathUtils<double>::StressVectorToTensor(StressVector); // reduced dimension stress tensor
+    const Matrix LocalStressTensor =
+        MathUtils<double>::StressVectorToTensor(rStressVector); // reduced dimension stress tensor
 
     Matrix StressTensor(3, 3); // 3D stress tensor
     noalias(StressTensor) = ZeroMatrix(3, 3);
@@ -30,7 +31,7 @@ double StressStrainUtilities::CalculateVonMisesStress(const Vector& StressVector
         }
     }
 
-    double SigmaEquivalent =
+    const double SigmaEquivalent =
         0.5 * ((StressTensor(0, 0) - StressTensor(1, 1)) * (StressTensor(0, 0) - StressTensor(1, 1)) +
                (StressTensor(1, 1) - StressTensor(2, 2)) * (StressTensor(1, 1) - StressTensor(2, 2)) +
                (StressTensor(2, 2) - StressTensor(0, 0)) * (StressTensor(2, 2) - StressTensor(0, 0)) +
@@ -40,9 +41,9 @@ double StressStrainUtilities::CalculateVonMisesStress(const Vector& StressVector
     return std::sqrt(std::max(SigmaEquivalent, 0.));
 }
 
-double StressStrainUtilities::CalculateTrace(const Vector& StressVector)
+double StressStrainUtilities::CalculateTrace(const Vector& rStressVector)
 {
-    Matrix StressTensor = MathUtils<double>::StressVectorToTensor(StressVector); // reduced dimension stress tensor
+    const Matrix StressTensor = MathUtils<double>::StressVectorToTensor(rStressVector); // reduced dimension stress tensor
 
     double trace = 0.0;
     for (std::size_t i = 0; i < StressTensor.size1(); ++i) {
@@ -52,26 +53,75 @@ double StressStrainUtilities::CalculateTrace(const Vector& StressVector)
     return trace;
 }
 
-double StressStrainUtilities::CalculateMeanStress(const Vector& StressVector)
+double StressStrainUtilities::CalculateMeanStress(const Vector& rStressVector)
 {
-    return CalculateTrace(StressVector) / (StressVector.size() == 3 ? 2.0 : 3.0);
+    return CalculateTrace(rStressVector) / (rStressVector.size() == 3 ? 2.0 : 3.0);
 }
 
-double StressStrainUtilities::CalculateVonMisesStrain(const Vector& StrainVector)
+double StressStrainUtilities::CalculateLodeAngle(const Vector& rStressVector)
 {
-    return (2.0 / 3.0) * CalculateVonMisesStress(StrainVector);
+    KRATOS_ERROR_IF(rStressVector.size() < 4);
+
+    const double p                   = CalculateMeanStress(rStressVector);
+    const double q                   = CalculateVonMisesStress(rStressVector);
+    const Matrix local_stress_tensor = MathUtils<double>::StressVectorToTensor(rStressVector);
+    Matrix       sigma_princi;
+    Matrix       eigen_vectors;
+    MathUtils<double>::GaussSeidelEigenSystem(local_stress_tensor, eigen_vectors, sigma_princi, 1.0e-16, 20);
+    const double numerator = (sigma_princi(0, 0) - p) * (sigma_princi(1, 1) - p) * (sigma_princi(2, 2) - p);
+    if (std::abs(numerator) < 1.0E-12) return 0.;
+    return std::asin((-27. / 2.) * numerator / (q * q * q)) / 3.0;
 }
 
-Vector StressStrainUtilities::CalculateHenckyStrain(const Matrix& DeformationGradient, size_t VoigtSize)
+double StressStrainUtilities::CalculateMohrCoulombShearCapacity(const Vector& rStressVector, double C, double Phi)
+{
+    KRATOS_ERROR_IF(rStressVector.size() < 4);
+
+    const double q_mc = CalculateQMohrCoulomb(rStressVector, C, Phi);
+    const double q    = CalculateVonMisesStress(rStressVector);
+
+    return q / q_mc;
+}
+
+double StressStrainUtilities::CalculateQMohrCoulomb(const Vector& rStressVector, double C, double Phi)
+{
+    const double denominator = CalculateDenominator(rStressVector, Phi);
+    const double p           = -CalculateMeanStress(rStressVector);
+    return 3. * (p * std::sin(Phi) + C * std::cos(Phi)) / denominator;
+}
+
+double StressStrainUtilities::CalculateDenominator(const Vector& rStressVector, double Phi)
+{
+    const double lode_angle = CalculateLodeAngle(rStressVector);
+    return std::sqrt(3.) * std::cos(lode_angle) - std::sin(lode_angle) * std::sin(Phi);
+}
+
+double StressStrainUtilities::CalculateMohrCoulombPressureCapacity(const Vector& rStressVector, double C, double Phi)
+{
+    KRATOS_ERROR_IF(rStressVector.size() < 4);
+
+    const double denominator = CalculateDenominator(rStressVector, Phi);
+    const double q_mc        = CalculateQMohrCoulomb(rStressVector, C, Phi);
+    const double q           = CalculateVonMisesStress(rStressVector);
+
+    return 3. * std::sin(Phi) * (q_mc - q) / denominator;
+}
+
+double StressStrainUtilities::CalculateVonMisesStrain(const Vector& rStrainVector)
+{
+    return (2.0 / 3.0) * CalculateVonMisesStress(rStrainVector);
+}
+
+Vector StressStrainUtilities::CalculateHenckyStrain(const Matrix& rDeformationGradient, size_t VoigtSize)
 {
     // right Cauchy Green deformation tensor C
-    Matrix C = prod(trans(DeformationGradient), DeformationGradient);
+    Matrix C = prod(trans(rDeformationGradient), rDeformationGradient);
     // Eigenvalues of C matrix, so principal right Cauchy Green deformation tensor C
     Matrix EigenValuesMatrix;
     Matrix EigenVectorsMatrix;
     MathUtils<double>::GaussSeidelEigenSystem(C, EigenVectorsMatrix, EigenValuesMatrix, 1.0e-16, 20);
     // Compute natural strain == Logarithmic strain == Hencky strain from principal strains
-    for (std::size_t i = 0; i < DeformationGradient.size1(); ++i) {
+    for (std::size_t i = 0; i < rDeformationGradient.size1(); ++i) {
         EigenValuesMatrix(i, i) = 0.5 * std::log(EigenValuesMatrix(i, i));
     }
 
@@ -80,7 +130,7 @@ Vector StressStrainUtilities::CalculateHenckyStrain(const Matrix& DeformationGra
     MathUtils<double>::BDBtProductOperation(ETensor, EigenValuesMatrix, EigenVectorsMatrix);
 
     // From tensor to vector
-    if (DeformationGradient.size1() == 2 && VoigtSize == 4) {
+    if (rDeformationGradient.size1() == 2 && VoigtSize == 4) {
         // Plane strain
         Vector StrainVector2D;
         StrainVector2D = MathUtils<double>::StrainTensorToVector(ETensor, 3);
diff --git a/applications/GeoMechanicsApplication/custom_utilities/stress_strain_utilities.h b/applications/GeoMechanicsApplication/custom_utilities/stress_strain_utilities.h
index a424394e9805..1e3e1c5d2aa6 100644
--- a/applications/GeoMechanicsApplication/custom_utilities/stress_strain_utilities.h
+++ b/applications/GeoMechanicsApplication/custom_utilities/stress_strain_utilities.h
@@ -23,12 +23,19 @@ namespace Kratos
 class KRATOS_API(GEO_MECHANICS_APPLICATION) StressStrainUtilities
 {
 public:
-    static double CalculateVonMisesStress(const Vector& StressVector);
-    static double CalculateTrace(const Vector& StressVector);
-    static double CalculateMeanStress(const Vector& StressVector);
-    static double CalculateVonMisesStrain(const Vector& StrainVector);
-    static Vector CalculateHenckyStrain(const Matrix& DeformationGradient, size_t VoigtSize);
+    static double CalculateVonMisesStress(const Vector& rStressVector);
+    static double CalculateTrace(const Vector& rStressVector);
+    static double CalculateMeanStress(const Vector& rStressVector);
+    static double CalculateLodeAngle(const Vector& rStressVector);
+    static double CalculateMohrCoulombShearCapacity(const Vector& rStressVector, double C, double Phi);
+    static double CalculateMohrCoulombPressureCapacity(const Vector& rStressVector, double C, double Phi);
+    static double CalculateVonMisesStrain(const Vector& rStrainVector);
+    static Vector CalculateHenckyStrain(const Matrix& rDeformationGradient, size_t VoigtSize);
     static Matrix CalculateGreenLagrangeStrainTensor(const Matrix& rDeformationGradient);
+
+private:
+    static double CalculateQMohrCoulomb(const Vector& rStressVector, double C, double Phi);
+    static double CalculateDenominator(const Vector& rStressVector, double Phi);
 };
 
 } // namespace Kratos
\ No newline at end of file