Skip to content

Conversation

@mnabideltares
Copy link
Contributor

📝 Description
The code is copied and modified to match the recently implemented structure for Mohr-Coulomb

🆕 Changelog

  • Added hardening
  • Registered the coefficients, as arrays to account for different hardening types
  • Added unit tests

Copy link
Contributor

@avdg81 avdg81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Mohamed,

Thank you very much for preparing this clear and well-scoped PR. It was very much what I had expected. I have a few suggestions about the iteration loop as well as moving some functions around, but all of them are relatively minor things. Well done!

@mnabideltares mnabideltares requested a review from avdg81 November 17, 2025 22:05
avdg81 and others added 16 commits November 19, 2025 15:11
… multiplier rather than passing it through a function parameter
It now uses proper mathematical structures rather than working things out at the component level.
… Coulomb yield surface to the Mohr-Coulomb implementation class
mnabideltares and others added 4 commits November 26, 2025 15:30
- Simplified some code by using a utility function that creates vectors.
- Made member `CheckMaterialProperties` `const`.
- Extracted a private member function that checks hardening coefficients (to reduce duplication).
Copy link
Contributor

@avdg81 avdg81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Mohamed,
Thanks a lot for processing the review suggestions. After reviewing your latest changes, I have a few more (minor) suggestions. I believe we're almost there. Thanks for all the work that you have done.

Comment on lines 88 to 99
// Check whether the tension cut-off lies beyond the apex
auto result = Vector{ZeroVector(2)};
result[0] = mCoulombYieldSurface.CalculateApex();
const auto tensile_strength = mTensionCutOff.GetTensileStrength();
if (tensile_strength > result[0]) return result;

const auto cohesion = mCoulombYieldSurface.GetCohesion();
const auto sin_phi = std::sin(mCoulombYieldSurface.GetFrictionAngleInRadians());
const auto cos_phi = std::cos(mCoulombYieldSurface.GetFrictionAngleInRadians());
result[0] = (tensile_strength - cohesion * cos_phi) / (1.0 - sin_phi);
result[1] = (cohesion * cos_phi - tensile_strength * sin_phi) / (1.0 - sin_phi);
return result;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By using UblasUtilities::CreateVector we can make the code slightly more elegant:

Suggested change
// Check whether the tension cut-off lies beyond the apex
auto result = Vector{ZeroVector(2)};
result[0] = mCoulombYieldSurface.CalculateApex();
const auto tensile_strength = mTensionCutOff.GetTensileStrength();
if (tensile_strength > result[0]) return result;
const auto cohesion = mCoulombYieldSurface.GetCohesion();
const auto sin_phi = std::sin(mCoulombYieldSurface.GetFrictionAngleInRadians());
const auto cos_phi = std::cos(mCoulombYieldSurface.GetFrictionAngleInRadians());
result[0] = (tensile_strength - cohesion * cos_phi) / (1.0 - sin_phi);
result[1] = (cohesion * cos_phi - tensile_strength * sin_phi) / (1.0 - sin_phi);
return result;
const auto tensile_strength = mTensionCutOff.GetTensileStrength();
if (const auto apex = mCoulombYieldSurface.CalculateApex(); tensile_strength > apex)
return UblasUtilities::CreateVector({apex, 0.0});
const auto cohesion = mCoulombYieldSurface.GetCohesion();
const auto sin_phi = std::sin(mCoulombYieldSurface.GetFrictionAngleInRadians());
const auto cos_phi = std::cos(mCoulombYieldSurface.GetFrictionAngleInRadians());
return UblasUtilities::CreateVector({(tensile_strength - cohesion * cos_phi) / (1.0 - sin_phi),
(cohesion * cos_phi - tensile_strength * sin_phi) / (1.0 - sin_phi)});

Note that the early exit now looks consistent with the result returned at line 129.

CoulombYieldSurface::KappaDependentFunction MakeLinearFunction(double Value, double Coefficient)
{
return [Value, Coefficient](double kappa) { return std::max(Value + Coefficient * kappa, 0.0); };
return [Value, Coefficient](double kappa) { return Value + Coefficient * kappa; };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpicking (to follow the Kratos Style Guide):

Suggested change
return [Value, Coefficient](double kappa) { return Value + Coefficient * kappa; };
return [Value, Coefficient](double Kappa) { return Value + Coefficient * Kappa; };

mMaterialProperties[GEO_COULOMB_HARDENING_CONVERGENCE_TOLERANCE] = 1.0e-8;
}

this->CheckMaterialProperties();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No this pointer required here, so please leave it out.

Suggested change
this->CheckMaterialProperties();
CheckMaterialProperties();

Comment on lines 193 to 194
const auto mean =
std::accumulate(principal_stress_vector.begin(), principal_stress_vector.end(), 0.0) / 3.0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could consider to replace the hard-coded size by asking for the vector's size:

Suggested change
const auto mean =
std::accumulate(principal_stress_vector.begin(), principal_stress_vector.end(), 0.0) / 3.0;
const auto mean = std::accumulate(principal_stress_vector.begin(), principal_stress_vector.end(), 0.0) /
static_cast<double>(principal_stress_vector.size());

What do you think? And perhaps @WPK4FEM may also want to weigh in?

Comment on lines 210 to 232
check_properties.CheckAvailability(GEO_COHESION_FUNCTION_COEFFICIENTS);
const auto& cohesion_vec = mMaterialProperties[GEO_COHESION_FUNCTION_COEFFICIENTS];
for (unsigned int i = 0; i < cohesion_vec.size(); ++i) {
KRATOS_ERROR_IF(cohesion_vec[i] < 0.0)
<< "Entry " << i << " in " << GEO_COHESION_FUNCTION_COEFFICIENTS.Name()
<< " out of range. Value: " << cohesion_vec[i];
}

check_properties.CheckAvailability(GEO_FRICTION_ANGLE_FUNCTION_COEFFICIENTS);
const auto& friction_vec = mMaterialProperties[GEO_FRICTION_ANGLE_FUNCTION_COEFFICIENTS];
for (unsigned int i = 0; i < friction_vec.size(); ++i) {
KRATOS_ERROR_IF(friction_vec[i] < 0.0)
<< "Entry " << i << " in " << GEO_FRICTION_ANGLE_FUNCTION_COEFFICIENTS.Name()
<< " out of range. Value: " << friction_vec[i];
}

check_properties.CheckAvailability(GEO_DILATANCY_ANGLE_FUNCTION_COEFFICIENTS);
const auto& dilatancy_vec = mMaterialProperties[GEO_DILATANCY_ANGLE_FUNCTION_COEFFICIENTS];
for (unsigned int i = 0; i < dilatancy_vec.size(); ++i) {
KRATOS_ERROR_IF(dilatancy_vec[i] < 0.0)
<< "Entry " << i << " in " << GEO_DILATANCY_ANGLE_FUNCTION_COEFFICIENTS.Name()
<< " out of range. Value: " << dilatancy_vec[i];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checks of the three coefficient vectors look very similar. Perhaps we can extract it to a private member? I was thinking of something like:

void CoulombYieldSurface::CheckHardeningCoefficients(const Variable<Vector>& rCoefficientsVariable,
                                                     const CheckProperties&  rChecker) const
{
    rChecker.CheckAvailability(rCoefficientsVariable);

    const auto& coefficients = mMaterialProperties[rCoefficientsVariable];
    for (auto i = std::size_t{0}; i < coefficients.size(); ++i) {
        KRATOS_ERROR_IF(coefficients[i] < 0.0) << "Entry " << i << " in " << rCoefficientsVariable.Name()
                                               << " out of range. Value: " << coefficients[i];
    }
}

Once that member function is available, this piece of code can be reduced to three function calls:

Suggested change
check_properties.CheckAvailability(GEO_COHESION_FUNCTION_COEFFICIENTS);
const auto& cohesion_vec = mMaterialProperties[GEO_COHESION_FUNCTION_COEFFICIENTS];
for (unsigned int i = 0; i < cohesion_vec.size(); ++i) {
KRATOS_ERROR_IF(cohesion_vec[i] < 0.0)
<< "Entry " << i << " in " << GEO_COHESION_FUNCTION_COEFFICIENTS.Name()
<< " out of range. Value: " << cohesion_vec[i];
}
check_properties.CheckAvailability(GEO_FRICTION_ANGLE_FUNCTION_COEFFICIENTS);
const auto& friction_vec = mMaterialProperties[GEO_FRICTION_ANGLE_FUNCTION_COEFFICIENTS];
for (unsigned int i = 0; i < friction_vec.size(); ++i) {
KRATOS_ERROR_IF(friction_vec[i] < 0.0)
<< "Entry " << i << " in " << GEO_FRICTION_ANGLE_FUNCTION_COEFFICIENTS.Name()
<< " out of range. Value: " << friction_vec[i];
}
check_properties.CheckAvailability(GEO_DILATANCY_ANGLE_FUNCTION_COEFFICIENTS);
const auto& dilatancy_vec = mMaterialProperties[GEO_DILATANCY_ANGLE_FUNCTION_COEFFICIENTS];
for (unsigned int i = 0; i < dilatancy_vec.size(); ++i) {
KRATOS_ERROR_IF(dilatancy_vec[i] < 0.0)
<< "Entry " << i << " in " << GEO_DILATANCY_ANGLE_FUNCTION_COEFFICIENTS.Name()
<< " out of range. Value: " << dilatancy_vec[i];
}
CheckHardeningCoefficients(GEO_COHESION_FUNCTION_COEFFICIENTS, check_properties);
CheckHardeningCoefficients(GEO_FRICTION_ANGLE_FUNCTION_COEFFICIENTS, check_properties);
CheckHardeningCoefficients(GEO_DILATANCY_ANGLE_FUNCTION_COEFFICIENTS, check_properties);

CalculatePlasticMultiplier(rSigmaTau, DerivativeOfFlowFunction(rSigmaTau, AveragingType));
}

void CoulombYieldSurface::CheckMaterialProperties()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When member functions can be made const, please do so:

Suggested change
void CoulombYieldSurface::CheckMaterialProperties()
void CoulombYieldSurface::CheckMaterialProperties() const


private:
void InitializeKappaDependentFunctions();
void CheckMaterialProperties();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a result of my previous suggestion:

Suggested change
void CheckMaterialProperties();
void CheckMaterialProperties() const;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important note: this suggestion is unrelated to your PR, but I just happened to see it: member function InterfaceCoulombWithTensionCutOff::WorkingSpaceDimension apparently assumes a line interface element (see the comment at line 63). This needs to be fixed (including a (unit?) test) in a separate PR.

Comment on lines 141 to 143
double kappa_start = -1.0;
mapped_sigma_tau = mCoulombWithTensionCutOffImpl.DoReturnMapping(
trial_sigma_tau, CoulombYieldSurface::CoulombAveragingType::NO_AVERAGING, kappa_start);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to propose a slightly different approach where we can tell mCoulombWithTensionCutOffImpl to save $\kappa$ as well as when to restore it. In this way, we don't need to pass around $\kappa$. You can find the implementation of this suggestion on a separate branch geo/experimental-changes-for-coulomb-hardening.

Comment on lines -72 to -77
KRATOS_EXPECT_EXCEPTION_IS_THROWN(
[[maybe_unused]] const auto unused = law.Check(properties, element_geometry, process_info),
"GEO_COHESION does not exist in the property with Id 3.")
properties.SetValue(GEO_COHESION, -1.0);
KRATOS_EXPECT_EXCEPTION_IS_THROWN(
[[maybe_unused]] const auto unused = law.Check(properties, element_geometry, process_info), "GEO_COHESION in the property with Id 3 has an invalid value: -1 is out of the range [0, -).")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please try to reproduce these tests for the Coulomb yield surface?

@mnabideltares mnabideltares requested a review from avdg81 December 3, 2025 13:41
Copy link
Contributor

@avdg81 avdg81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Mohamed,
Thanks for taking care of the review suggestions. I feel this PR is ready to go.

@mnabideltares mnabideltares merged commit b1ed779 into master Dec 4, 2025
9 of 10 checks passed
@mnabideltares mnabideltares deleted the geo/13826-use-new-structure-for-hardening branch December 4, 2025 08:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[GeoMechanicsApplication] Implement the formulations in the code (Extend Mohr-Coulomb)

3 participants