[GeoMechanicsApplicaiton] Make tension cut off truly optional for Mohr Coulomb model#14467
[GeoMechanicsApplicaiton] Make tension cut off truly optional for Mohr Coulomb model#14467rfaasse wants to merge 22 commits into
Conversation
…on cutoff object based on that
…ion-cut-off-truly-optional
… values) in all crow materials
| // Keeping the old names for backward compatibility | ||
| KRATOS_REGISTER_CONSTITUTIVE_LAW("GeoMohrCoulombWithTensionCutOff2D", mMohrCoulomb2D) | ||
| KRATOS_REGISTER_CONSTITUTIVE_LAW("GeoMohrCoulombWithTensionCutOff3D", mMohrCoulomb3D) | ||
| KRATOS_REGISTER_CONSTITUTIVE_LAW("GeoInterfaceCoulombWithTensionCutOff", mInterfaceCoulomb) |
There was a problem hiding this comment.
I was doubting whether we should keep the old ones. Curious what others think
WPK4FEM
left a comment
There was a problem hiding this comment.
HI Richard,
This is what I managed to read and comment on today, I will have another look.
Wijtze Pieter
| const auto coulomb_yield_function_value = mCoulombYieldSurface.YieldFunctionValue(rTrialStressState); | ||
| const auto tension_yield_function_value = mTensionCutOff.YieldFunctionValue(rTrialStressState); | ||
| constexpr auto tolerance = 1.0e-10; | ||
| constexpr auto tolerance = 1.0e-10; |
There was a problem hiding this comment.
Please relate there to the available relative or absolute numerical tolerances.
There was a problem hiding this comment.
These are in test_utilities.h (so in the test folder) and therefore cannot be reached here. Also, the defaults are 1e-6 for relative and 1e-12 for absolute. What do you think we should use here? I wasn't that involved in the development of Coulomb, so I'm not sure why this exact tolerance was chosen at the time
| auto tension_admissable = true; | ||
| if (mTensionCutOff) { | ||
| const auto tension_yield_function_value = mTensionCutOff->YieldFunctionValue(rTrialStressState); | ||
| const auto tension_tolerance = tolerance * (1.0 + std::abs(tension_yield_function_value)); | ||
| tension_admissable = tension_yield_function_value < tension_tolerance; | ||
| } | ||
|
|
||
| const auto admissible_state = coulomb_yield_function_value < coulomb_tolerance && tension_admissable; |
There was a problem hiding this comment.
The MC part is always there so lets start from that
| auto tension_admissable = true; | |
| if (mTensionCutOff) { | |
| const auto tension_yield_function_value = mTensionCutOff->YieldFunctionValue(rTrialStressState); | |
| const auto tension_tolerance = tolerance * (1.0 + std::abs(tension_yield_function_value)); | |
| tension_admissable = tension_yield_function_value < tension_tolerance; | |
| } | |
| const auto admissible_state = coulomb_yield_function_value < coulomb_tolerance && tension_admissable; | |
| auto admissible_state = coulomb_yield_function_value < coulomb_tolerance; | |
| if (admissible_state && mTensionCutOff) { | |
| const auto tension_yield_function_value = mTensionCutOff->YieldFunctionValue(rTrialStressState); | |
| const auto tension_tolerance = tolerance * (1.0 + std::abs(tension_yield_function_value)); | |
| admissible_state = tension_yield_function_value < tension_tolerance; | |
| } |
There was a problem hiding this comment.
Nice suggestion, done!
| if (mTensionCutOff && IsStressAtTensionApexReturnZone(trial_traction)) { | ||
| mPlasticityStatus = PlasticityStatus::TENSION_APEX; | ||
| return ReturnStressAtTensionApexReturnZone(rTrialStressState); | ||
| } | ||
|
|
||
| if (IsStressAtTensionCutoffReturnZone(trial_traction)) { | ||
| if (mTensionCutOff && IsStressAtTensionCutoffReturnZone(trial_traction)) { | ||
| mPlasticityStatus = PlasticityStatus::TENSION_CUT_OFF; | ||
| return ReturnStressAtTensionCutoffReturnZone(rTrialStressState, | ||
| rElasticConstitutiveTensor, AveragingType); | ||
| } |
There was a problem hiding this comment.
Both previous conditions need the TensionCutOff to be active so lets check that in an encompassing condition.
| if (mTensionCutOff) { | |
| if (IsStressAtTensionApexReturnZone(trial_traction)) { | |
| mPlasticityStatus = PlasticityStatus::TENSION_APEX; | |
| return ReturnStressAtTensionApexReturnZone(rTrialStressState); | |
| } | |
| if (IsStressAtTensionCutoffReturnZone(trial_traction)) { | |
| mPlasticityStatus = PlasticityStatus::TENSION_CUT_OFF; | |
| return ReturnStressAtTensionCutoffReturnZone(rTrialStressState, | |
| rElasticConstitutiveTensor, AveragingType); | |
| } | |
| } |
There was a problem hiding this comment.
I agree with this suggestion. 👍
avdg81
left a comment
There was a problem hiding this comment.
Hi Richard,
Thanks a lot for making the tension cut-off of the Mohr-Coulomb model truly optional. With your changes, there's no doubt about when a tension cut-off was intended or not. It also helps to structure the code even more.
I have several questions and suggestions. Hope all of them will help to make your valuable changes stand out even more. 👍
| if (rMaterialProperties.Has(GEO_ENABLE_TENSION_CUT_OFF) && rMaterialProperties[GEO_ENABLE_TENSION_CUT_OFF]) { | ||
| return std::make_optional<TensionCutoff>(rMaterialProperties[GEO_TENSILE_STRENGTH]); | ||
| } | ||
|
|
||
| // The following statement is to support backward compatibility (i.e. GEO_ENABLE_TENSION_CUT_OFF is not specified, | ||
| // but the GEO_TENSILE_STRENGTH is provided), which results in an enabled tension cutoff. | ||
| if (rMaterialProperties.Has(GEO_TENSILE_STRENGTH) && !rMaterialProperties.Has(GEO_ENABLE_TENSION_CUT_OFF)) { | ||
| return std::make_optional<TensionCutoff>(rMaterialProperties[GEO_TENSILE_STRENGTH]); | ||
| } |
There was a problem hiding this comment.
Since the return statements are identical, I would suggest to join the condition checks and put them in a local helper function named WantTensionCutOff (or any other name if you can think of a better one):
| if (rMaterialProperties.Has(GEO_ENABLE_TENSION_CUT_OFF) && rMaterialProperties[GEO_ENABLE_TENSION_CUT_OFF]) { | |
| return std::make_optional<TensionCutoff>(rMaterialProperties[GEO_TENSILE_STRENGTH]); | |
| } | |
| // The following statement is to support backward compatibility (i.e. GEO_ENABLE_TENSION_CUT_OFF is not specified, | |
| // but the GEO_TENSILE_STRENGTH is provided), which results in an enabled tension cutoff. | |
| if (rMaterialProperties.Has(GEO_TENSILE_STRENGTH) && !rMaterialProperties.Has(GEO_ENABLE_TENSION_CUT_OFF)) { | |
| return std::make_optional<TensionCutoff>(rMaterialProperties[GEO_TENSILE_STRENGTH]); | |
| } | |
| if (WantTensionCutOff(rMaterialProperties)) { | |
| return std::make_optional<TensionCutoff>(rMaterialProperties[GEO_TENSILE_STRENGTH]); | |
| } |
where
bool WantTensionCutOff(const Properties& rMaterialProperties)
{
if (rMaterialProperties.Has(GEO_ENABLE_TENSION_CUT_OFF) && rMaterialProperties[GEO_ENABLE_TENSION_CUT_OFF])
return true;
// The following statement is to support backward compatibility (i.e. GEO_ENABLE_TENSION_CUT_OFF is not specified,
// but the GEO_TENSILE_STRENGTH is provided), which results in an enabled tension cutoff.
return !rMaterialProperties.Has(GEO_ENABLE_TENSION_CUT_OFF) && rMaterialProperties.Has(GEO_TENSILE_STRENGTH);
}As you can see, I have also swapped the two conditions in the backward compatibility check. In that way, the check whether the flag GEO_ENABLE_TENSION_CUT_OFF exists always comes first, which is a little bit clearer for me.
|
|
||
| template <typename StressStateType> | ||
| bool CoulombWithTensionCutOffImpl::IsAdmissibleStressState(const StressStateType& rTrialStressState) | ||
| bool CoulombImpl::IsAdmissibleStressState(const StressStateType& rTrialStressState) |
There was a problem hiding this comment.
Perhaps we can extract the checking whether the given trial stress state is admissible for a given yield surface to a new helper function, and then use that to implement this member function.
In the anonymous we can add the following function template:
template <typename YieldSurfaceType, typename StressStateType>
bool IsAdmissibleStressState(const YieldSurfaceType& rYieldSurface, const StressStateType& rTrialStressState)
{
const auto yield_function_value = rYieldSurface.YieldFunctionValue(rTrialStressState);
const auto tolerance = 1.0e-10 * (1.0 + std::abs(yield_function_value));
return yield_function_value < tolerance;
}And then the member function template can be simplified as follows:
template <typename StressStateType>
bool CoulombImpl::IsAdmissibleStressState(const StressStateType& rTrialStressState)
{
if (!::IsAdmissibleStressState(mCoulombYieldSurface, rTrialStressState)) return false;
if (mTensionCutOff && !::IsAdmissibleStressState(*mTensionCutOff, rTrialStressState))
return false;
mPlasticityStatus = PlasticityStatus::ELASTIC;
return true;
}Note that with this new structure in-place, adding the optional cap yield surface becomes straightforward.
| if (mTensionCutOff && IsStressAtTensionApexReturnZone(trial_traction)) { | ||
| mPlasticityStatus = PlasticityStatus::TENSION_APEX; | ||
| return ReturnStressAtTensionApexReturnZone(rTrialStressState); | ||
| } | ||
|
|
||
| if (IsStressAtTensionCutoffReturnZone(trial_traction)) { | ||
| if (mTensionCutOff && IsStressAtTensionCutoffReturnZone(trial_traction)) { | ||
| mPlasticityStatus = PlasticityStatus::TENSION_CUT_OFF; | ||
| return ReturnStressAtTensionCutoffReturnZone(rTrialStressState, | ||
| rElasticConstitutiveTensor, AveragingType); | ||
| } |
There was a problem hiding this comment.
I agree with this suggestion. 👍
| Geo::SigmaTau CoulombImpl::CalculateCornerPoint() const | ||
| { | ||
| const auto tensile_strength = mTensionCutOff.GetTensileStrength(); | ||
| if (const auto apex = mCoulombYieldSurface.CalculateApex(); !mTensionCutOff) return apex; |
There was a problem hiding this comment.
Since apex is not being used by the check itself, I feel we'd better postpone its calculation to the latest possible moment:
| if (const auto apex = mCoulombYieldSurface.CalculateApex(); !mTensionCutOff) return apex; | |
| if (!mTensionCutOff) return mCoulombYieldSurface.CalculateApex(); | |
(And let's add an additional blank line to clearly show that we've handled the first case.)
| std::size_t mMaxNumberOfPlasticIterations{100}; | ||
| PlasticityStatus mPlasticityStatus{PlasticityStatus::ELASTIC}; | ||
| CoulombYieldSurface mCoulombYieldSurface; | ||
| std::optional<TensionCutoff> mTensionCutOff; |
There was a problem hiding this comment.
Would a name like mOptionalTensionCutOff better reflect that the tension cut-off is optional? In other words, that it may or may not be there.
| "GEO_ENABLE_TENSION_CUT_OFF": true, | ||
| "GEO_TENSILE_STRENGTH": 0.0, |
There was a problem hiding this comment.
| "GEO_ENABLE_TENSION_CUT_OFF": true, | |
| "GEO_TENSILE_STRENGTH": 0.0, | |
| "GEO_ENABLE_TENSION_CUT_OFF": false, |
| KRATOS_REGISTER_CONSTITUTIVE_LAW("GeoMohrCoulomb2D", mMohrCoulomb2D) | ||
| KRATOS_REGISTER_CONSTITUTIVE_LAW("GeoMohrCoulomb3D", mMohrCoulomb3D) | ||
| KRATOS_REGISTER_CONSTITUTIVE_LAW("GeoInterfaceCoulomb", mInterfaceCoulomb) |
There was a problem hiding this comment.
These new names are slightly different than what was proposed in the issue:
"GeoMohrCoulombWithTensionCutOff2D" => "GeoMohrCoulombLawPlaneStrain"
"GeoMohrCoulombWithTensionCutOff3D" => "GeoMohrCoulombLaw3D"
"GeoInterfaceCoulombWithTensionCutOff" => "GeoInterfaceCoulombLawPlaneStrain"
I'm wondering why the names are somewhat different?
|
|
||
| const MohrCoulombWithTensionCutOff mMohrCoulombWithTensionCutOff2D{std::make_unique<PlaneStrain>()}; | ||
| const MohrCoulombWithTensionCutOff mMohrCoulombWithTensionCutOff3D{std::make_unique<ThreeDimensional>()}; | ||
| const MohrCoulomb mMohrCoulomb2D{std::make_unique<PlaneStrain>()}; |
There was a problem hiding this comment.
I feel mMohrCoulombPlaneStrain is a better name (or perhaps even better: mMohrCoulombLawPlaneStrain). Perhaps @WPK4FEM wants to weigh in here, too?
| const auto tensile_strength = cohesion / std::tan(MathUtils<>::DegreesToRadians(phi_in_degrees)); | ||
| properties.SetValue(GEO_TENSILE_STRENGTH, tensile_strength); |
There was a problem hiding this comment.
Perhaps it's clearer to be explicit about not wanting a tension cut-off rather than relying on a default value:
properties.SetValue(GEO_ENABLE_TENSION_CUT_OFF, false);| // Act | ||
| auto cauchy_stress_vector = UblasUtilities::CreateVector( | ||
| {tensile_strength + 20.0, tensile_strength + 10.0, tensile_strength, 0.0}); | ||
| const auto sigma_corner = cohesion / std::tan(MathUtils<>::DegreesToRadians(phi_in_degrees)); |
There was a problem hiding this comment.
Perhaps apex is a better name?
| const auto sigma_corner = cohesion / std::tan(MathUtils<>::DegreesToRadians(phi_in_degrees)); | |
| const auto apex = cohesion / std::tan(MathUtils<>::DegreesToRadians(phi_in_degrees)); |
📝 Description
This PR enables the user to specify a Mohr Coulomb model with or without tension cut-off, using the new
GEO_ENABLE_TENSION_CUT_OFFparameter in the material parameters. When the tension cut-off is turned off, the Mohr Coulomb model collapses to a simplified version with just the regular failure zone, elastic zone and apex return.Key changes
GEO_ENABLE_TENSION_CUT_OFFis added as a material parameter for the Mohr Coulomb model. If it's not specified, but a tensile strength is specified, it is assumed the tension cut-off is enabled (for backward compatibility).GEO_ENABLE_TENSION_CUT_OFFwith the correct value is added). For some tests, the tensile strength was specified as cohesion/tan(phi), which is equivalent to disabling the tension cut-off. For the tests where this was the case, theGEO_ENABLE_TENSION_CUT_OFFis set tofalseNote: A PR for the user documentation will be created separately