Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
18d02d5
set hardening with the recent structure
mnabideltares Nov 6, 2025
193f999
Fixes, and clang format
mnabideltares Nov 6, 2025
a337918
restructured and added unit tests
mnabideltares Nov 11, 2025
7b2fa35
Merge remote-tracking branch 'origin/master' into geo/13826-use-new-s…
mnabideltares Nov 12, 2025
bad8482
Modifications based on review
mnabideltares Nov 16, 2025
31b63a1
Bug is fixed, unit tests are fixed
mnabideltares Nov 17, 2025
9b064e0
Added keywords to material parameters
mnabideltares Nov 17, 2025
4e72305
Made two member functions `const`
avdg81 Nov 19, 2025
35f0ad1
Removed unused `#include`
avdg81 Nov 19, 2025
78e86fc
Made `CalculateCornerPoint` a member of class `CoulombWithTensionCutO…
avdg81 Nov 19, 2025
c15c117
Made `IsStressAtTensionApexReturnZone` a member of class `CoulombWith…
avdg81 Nov 19, 2025
d01e531
Made `IsStressAtTensionCutoffReturnZone` a member of class `CoulombWi…
avdg81 Nov 19, 2025
163654c
Made `IsStressAtCornerReturnZone` a member of class `CoulombWithTensi…
avdg81 Nov 19, 2025
a5bde4b
Made `ReturnStressAtTensionApexReturnZone` a member of class `Coulomb…
avdg81 Nov 19, 2025
3f502fd
Made `ReturnStressAtTensionCutoffReturnZone` a member of class `Coulo…
avdg81 Nov 19, 2025
e5da12e
Made `ReturnStressAtRegularFailureZone` a member of class `CoulombWit…
avdg81 Nov 19, 2025
cbae902
When calculating the equivalent plastic strain, calculate the plastic…
avdg81 Nov 19, 2025
064546b
Got rid of a local variable
avdg81 Nov 19, 2025
58d7c68
Rewrote the calculation of the equivalent plastic strain increment
avdg81 Nov 19, 2025
5345b79
Make a truly linear function when you ask for it
avdg81 Nov 19, 2025
4f973a4
Moved the plastic tolerance and maximum number of iterations from the…
avdg81 Nov 19, 2025
1b2c0fc
Renamed two Geo variables and changed the type of one of them
avdg81 Nov 19, 2025
5439346
Added check for material properties in Coulomb yield surface
mnabideltares Nov 26, 2025
f0ba844
Fixes
mnabideltares Nov 26, 2025
277c5a0
resetting of kappa is applied
mnabideltares Nov 27, 2025
051834a
Some minor suggestions
avdg81 Nov 28, 2025
5c28601
Adopt a different mechanism to restore kappa when needed
avdg81 Nov 28, 2025
9d0845c
Added unittests
mnabideltares Dec 2, 2025
c7a7088
Fixed unittests
mnabideltares Dec 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,83 +14,25 @@
//

#include "custom_constitutive/coulomb_with_tension_cut_off_impl.h"
#include "custom_processes/set_automated_initial_variable_process.h"
#include "custom_utilities/constitutive_law_utilities.h"
#include "custom_utilities/ublas_utilities.h"
#include "geo_mechanics_application_variables.h"
#include "includes/properties.h"
#include "includes/serializer.h"

namespace
{

using namespace Kratos;

Vector CalculateCornerPoint(double FrictionAngleInRadians, double Cohesion, double TensileStrength, double Apex)
{
// Check whether the tension cut-off lies beyond the apex
auto result = Vector{ZeroVector(2)};
result[0] = Apex;
if (TensileStrength > result[0]) return result;

result[0] = (TensileStrength - Cohesion * std::cos(FrictionAngleInRadians)) /
(1.0 - std::sin(FrictionAngleInRadians));
result[1] = (Cohesion * std::cos(FrictionAngleInRadians) - TensileStrength * std::sin(FrictionAngleInRadians)) /
(1.0 - std::sin(FrictionAngleInRadians));
return result;
}

bool IsStressAtTensionApexReturnZone(const Vector& rTrialSigmaTau, double TensileStrength, double Apex)
{
return TensileStrength < Apex && rTrialSigmaTau[0] - rTrialSigmaTau[1] - TensileStrength > 0.0;
}

bool IsStressAtTensionCutoffReturnZone(const Vector& rTrialSigmaTau, double TensileStrength, double Apex, const Vector& rCornerPoint)
{
return TensileStrength < Apex &&
rCornerPoint[1] - rTrialSigmaTau[1] - rCornerPoint[0] + rTrialSigmaTau[0] > 0.0;
}

bool IsStressAtCornerReturnZone(const Vector& rTrialSigmaTau,
const Vector& rDerivativeOfFlowFunction,
const Vector& rCornerPoint)
{
return (rTrialSigmaTau[0] - rCornerPoint[0]) * rDerivativeOfFlowFunction[1] -
(rTrialSigmaTau[1] - rCornerPoint[1]) * rDerivativeOfFlowFunction[0] >=
0.0;
}

Vector ReturnStressAtTensionApexReturnZone(double TensileStrength)
{
auto result = Vector{ZeroVector{2}};
result[0] = TensileStrength;
return result;
}

Vector ReturnStressAtTensionCutoffReturnZone(const Vector& rSigmaTau,
const Vector& rDerivativeOfFlowFunction,
double TensileStrength)
{
const auto lambda = (TensileStrength - rSigmaTau[0] - rSigmaTau[1]) /
(rDerivativeOfFlowFunction[0] + rDerivativeOfFlowFunction[1]);
return rSigmaTau + lambda * rDerivativeOfFlowFunction;
}

Vector ReturnStressAtRegularFailureZone(const Vector& rSigmaTau,
CoulombYieldSurface& rCoulombYieldSurface,
const Vector& rDerivativeOfFlowFunction)
{
const auto lambda = rCoulombYieldSurface.CalculatePlasticMultiplier(rSigmaTau, rDerivativeOfFlowFunction);
return rSigmaTau + lambda * rDerivativeOfFlowFunction;
}

} // namespace

namespace Kratos
{

CoulombWithTensionCutOffImpl::CoulombWithTensionCutOffImpl(const Properties& rMaterialProperties)
: mCoulombYieldSurface{rMaterialProperties}, mTensionCutOff{rMaterialProperties[GEO_TENSILE_STRENGTH]}
{
if (rMaterialProperties.Has(GEO_ABS_YIELD_FUNCTION_TOLERANCE)) {
mAbsoluteYieldFunctionValueTolerance = rMaterialProperties[GEO_ABS_YIELD_FUNCTION_TOLERANCE];
}

if (rMaterialProperties.Has(GEO_MAX_PLASTIC_ITERATIONS)) {
mMaxNumberOfPlasticIterations = rMaterialProperties[GEO_MAX_PLASTIC_ITERATIONS];
}
}

bool CoulombWithTensionCutOffImpl::IsAdmissibleSigmaTau(const Vector& rTrialSigmaTau) const
Expand All @@ -104,51 +46,106 @@ bool CoulombWithTensionCutOffImpl::IsAdmissibleSigmaTau(const Vector& rTrialSigm
}

Vector CoulombWithTensionCutOffImpl::DoReturnMapping(const Vector& rTrialSigmaTau,
CoulombYieldSurface::CoulombAveragingType AveragingType)
CoulombYieldSurface::CoulombAveragingType AveragingType,
double& kappa_start)
{
Vector result = ZeroVector(2);
const auto kappa_start = mCoulombYieldSurface.GetKappa();
Vector result = ZeroVector(2);

for (unsigned int counter = 0; counter < mCoulombYieldSurface.GetMaxIterations(); ++counter) {
const auto apex = mCoulombYieldSurface.CalculateApex();
if (kappa_start < 0.0) {
kappa_start = mCoulombYieldSurface.GetKappa();
} else {
mCoulombYieldSurface.SetKappa(kappa_start);
}

if (IsStressAtTensionApexReturnZone(rTrialSigmaTau, mTensionCutOff.GetTensileStrength(), apex)) {
return ReturnStressAtTensionApexReturnZone(mTensionCutOff.GetTensileStrength());
for (auto counter = std::size_t{0}; counter < mMaxNumberOfPlasticIterations; ++counter) {
if (IsStressAtTensionApexReturnZone(rTrialSigmaTau)) {
return ReturnStressAtTensionApexReturnZone();
}

const auto corner_point = CalculateCornerPoint(mCoulombYieldSurface.GetFrictionAngleInRadians(),
mCoulombYieldSurface.GetCohesion(),
mTensionCutOff.GetTensileStrength(), apex);

if (IsStressAtTensionCutoffReturnZone(rTrialSigmaTau, mTensionCutOff.GetTensileStrength(),
apex, corner_point)) {
return ReturnStressAtTensionCutoffReturnZone(
rTrialSigmaTau, mTensionCutOff.DerivativeOfFlowFunction(rTrialSigmaTau),
mTensionCutOff.GetTensileStrength());
if (IsStressAtTensionCutoffReturnZone(rTrialSigmaTau)) {
return ReturnStressAtTensionCutoffReturnZone(rTrialSigmaTau);
}

if (IsStressAtCornerReturnZone(
rTrialSigmaTau, mCoulombYieldSurface.DerivativeOfFlowFunction(rTrialSigmaTau, AveragingType),
corner_point)) {
result = corner_point;
if (IsStressAtCornerReturnZone(rTrialSigmaTau, AveragingType)) {
result = CalculateCornerPoint();
} else { // Regular failure region
result = ReturnStressAtRegularFailureZone(
rTrialSigmaTau, mCoulombYieldSurface,
mCoulombYieldSurface.DerivativeOfFlowFunction(rTrialSigmaTau, AveragingType));
result = ReturnStressAtRegularFailureZone(rTrialSigmaTau, AveragingType);
}

const auto lambda = mCoulombYieldSurface.CalculatePlasticMultiplier(
rTrialSigmaTau, mCoulombYieldSurface.DerivativeOfFlowFunction(rTrialSigmaTau, AveragingType));
const double kappa = kappa_start + mCoulombYieldSurface.CalculateEquivalentPlasticStrain(
rTrialSigmaTau, AveragingType, lambda);
const auto kappa = kappa_start + mCoulombYieldSurface.CalculateEquivalentPlasticStrainIncrement(
rTrialSigmaTau, AveragingType);
mCoulombYieldSurface.SetKappa(kappa);

double error = std::abs(mCoulombYieldSurface.YieldFunctionValue(result));
if (error < mCoulombYieldSurface.GetConvergenceTolerance()) break;
if (std::abs(mCoulombYieldSurface.YieldFunctionValue(result)) < mAbsoluteYieldFunctionValueTolerance) {
break;
}
}
return result;
Copy link
Contributor

@avdg81 avdg81 Nov 12, 2025

Choose a reason for hiding this comment

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

With the proposed for loop, you would only get here when no convergence has been achieved after carrying out the maximum number of iterations. I'm not sure what would be the best thing to do when that happens, but at least we can now easily distinguish between the converged and non-converged case.

}

Vector CoulombWithTensionCutOffImpl::CalculateCornerPoint() const
{
// 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.

}

bool CoulombWithTensionCutOffImpl::IsStressAtTensionApexReturnZone(const Vector& rTrialSigmaTau) const
{
const auto tensile_strength = mTensionCutOff.GetTensileStrength();
return tensile_strength < mCoulombYieldSurface.CalculateApex() &&
rTrialSigmaTau[0] - rTrialSigmaTau[1] - tensile_strength > 0.0;
}

bool CoulombWithTensionCutOffImpl::IsStressAtTensionCutoffReturnZone(const Vector& rTrialSigmaTau) const
{
const auto corner_point = CalculateCornerPoint();
return mTensionCutOff.GetTensileStrength() < mCoulombYieldSurface.CalculateApex() &&
corner_point[1] - rTrialSigmaTau[1] - corner_point[0] + rTrialSigmaTau[0] > 0.0;
}

bool CoulombWithTensionCutOffImpl::IsStressAtCornerReturnZone(const Vector& rTrialSigmaTau,
CoulombYieldSurface::CoulombAveragingType AveragingType) const
{
const auto corner_point = CalculateCornerPoint();
const auto derivative_of_flow_function =
mCoulombYieldSurface.DerivativeOfFlowFunction(rTrialSigmaTau, AveragingType);
return (rTrialSigmaTau[0] - corner_point[0]) * derivative_of_flow_function[1] -
(rTrialSigmaTau[1] - corner_point[1]) * derivative_of_flow_function[0] >=
0.0;
}

Vector CoulombWithTensionCutOffImpl::ReturnStressAtTensionApexReturnZone() const
{
return UblasUtilities::CreateVector({mTensionCutOff.GetTensileStrength(), 0.0});
}

Vector CoulombWithTensionCutOffImpl::ReturnStressAtTensionCutoffReturnZone(const Vector& rSigmaTau) const
{
const auto derivative_of_flow_function = mTensionCutOff.DerivativeOfFlowFunction(rSigmaTau);
const auto lambda_tc = (mTensionCutOff.GetTensileStrength() - rSigmaTau[0] - rSigmaTau[1]) /
(derivative_of_flow_function[0] + derivative_of_flow_function[1]);
return rSigmaTau + lambda_tc * derivative_of_flow_function;
}

Vector CoulombWithTensionCutOffImpl::ReturnStressAtRegularFailureZone(const Vector& rSigmaTau,
CoulombYieldSurface::CoulombAveragingType AveragingType) const
{
const auto derivative_of_flow_function =
mCoulombYieldSurface.DerivativeOfFlowFunction(rSigmaTau, AveragingType);
const auto lambda = mCoulombYieldSurface.CalculatePlasticMultiplier(rSigmaTau, derivative_of_flow_function);
return rSigmaTau + lambda * derivative_of_flow_function;
}

void CoulombWithTensionCutOffImpl::save(Serializer& rSerializer) const
{
rSerializer.save("CoulombYieldSurface", mCoulombYieldSurface);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,24 @@ class CoulombWithTensionCutOffImpl

[[nodiscard]] bool IsAdmissibleSigmaTau(const Vector& rTrialSigmaTau) const;
[[nodiscard]] Vector DoReturnMapping(const Vector& rTrialSigmaTau,
CoulombYieldSurface::CoulombAveragingType AveragingType);
CoulombYieldSurface::CoulombAveragingType AveragingType,
double& kappa_start);

private:
CoulombYieldSurface mCoulombYieldSurface;
TensionCutoff mTensionCutOff;
double mAbsoluteYieldFunctionValueTolerance{1.0e-8};
std::size_t mMaxNumberOfPlasticIterations{100};

[[nodiscard]] Vector CalculateCornerPoint() const;
[[nodiscard]] bool IsStressAtTensionApexReturnZone(const Vector& rTrialSigmaTau) const;
[[nodiscard]] bool IsStressAtTensionCutoffReturnZone(const Vector& rTrialSigmaTau) const;
[[nodiscard]] bool IsStressAtCornerReturnZone(const Vector& rTrialSigmaTau,
CoulombYieldSurface::CoulombAveragingType AveragingType) const;
[[nodiscard]] Vector ReturnStressAtTensionApexReturnZone() const;
[[nodiscard]] Vector ReturnStressAtTensionCutoffReturnZone(const Vector& rSigmaTau) const;
[[nodiscard]] Vector ReturnStressAtRegularFailureZone(const Vector& rSigmaTau,
CoulombYieldSurface::CoulombAveragingType AveragingType) const;

friend class Serializer;
void save(Serializer& rSerializer) const;
Expand Down
Loading
Loading