Skip to content

Commit

Permalink
Merge branch 'ModifiedCamClayNLE' into 'master'
Browse files Browse the repository at this point in the history
Modified Cam Clay model: original nonlinear hypoelastic version

See merge request ogs/ogs!4604
  • Loading branch information
bilke committed Jul 7, 2023
2 parents 882469e + 106bc7d commit 9ee9bcd
Show file tree
Hide file tree
Showing 17 changed files with 1,462 additions and 90 deletions.
2 changes: 2 additions & 0 deletions MaterialLib/SolidModels/MFront/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ mfront_behaviours_check_library(
GuentherSalzer
Lubby2
Lubby2mod
ModCamClay_semiExpl
ModCamClay_semiExpl_absP
ModCamClay_semiExpl_constE
MohrCoulombAbboSloan
MohrCoulombAbboSloanAniso
Expand Down
88 changes: 65 additions & 23 deletions MaterialLib/SolidModels/MFront/ModCamClay_TriaxTest.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,63 @@
import mtest
import mtest as mtest
import numpy as np
import matplotlib.pyplot as plt

m = mtest.MTest()
mtest.setVerboseMode(mtest.VerboseLevel.VERBOSE_QUIET)
m.setMaximumNumberOfSubSteps(20)
m.setModellingHypothesis("Axisymmetrical")
m.setBehaviour("generic", "src/libBehaviour.so", "ModCamClay_semiExpl_constE")

mcc_models = [
"ModCamClay_semiExpl",
"ModCamClay_semiExpl_absP",
"ModCamClay_semiExpl_constE",
]
controls = ["stress", "strain"]

# Set MCC material model implementation and path
lib_path = "./src/libBehaviour.so"
mcc_model = mcc_models[0]
control = controls[0]

m.setBehaviour("generic", lib_path, mcc_model)

# Material constants (according to Modified Cam clay model Report)
nu = 0.3 # Poisson ratio
E = 2 * (1 + nu) * 20.0e6 # Young's modulus in Pa
la = 7.7e-2 # slope of the virgin consolidation line
ka = 6.6e-3 # slope of the swelling line
M = 1.2 # slope of the critical state line (CSL)
v0 = 1.788 # initial volume ratio
phi0 = 1 - 1 / v0 # initial porosity
la = 7.7e-2 # Slope of the virgin consolidation line
ka = 6.6e-3 # Slope of the swelling line
M = 1.2 # Slope of the critical state line (CSL)
v0 = 1.7857 # Initial volume ratio
pc0 = 200.0e3 # Initial pre-consolidation pressure in Pa
pamb = 1.0e3 # Ambient pressure in Pa
phi0 = 1 - 1 / v0 # Initial porosity
pamb = 0.0 # Ambient pressure in Pa

# Loading programme
tMax = 1.0 # s , total time
nTime = 200
ltime = np.linspace(0.0, tMax, nTime)

p_con = pc0 # confining pressure
p_axi = 587387 # axial pressure, +12614 for reaching CSL
p_con = 200000 # confining pressure
e_con = p_con * (1 - 2 * nu) / E
m.setImposedStress("SRR", {0: 0, 0.02: -p_con, 1.0: -p_con})
m.setImposedStress("STT", {0: 0, 0.02: -p_con, 1.0: -p_con})
# stress-controlled: works only until reaching the CSL
m.setImposedStress("SZZ", {0: 0, 0.02: -p_con, 1.0: -p_axi})
# strain-controlled: works, CSL reached asymptotically for EYY->inf
# m.setImposedStrain('EZZ', {0:0, 0.02:-e_con, 1.0:-130*e_con})

# Young's modulus: consistent initial value for the models
E0 = 3 * (1 - 2 * nu) / (1 - phi0) * p_con / ka

e_con = p_con * (1 - 2 * nu) / E0
e_axi = 16 * e_con

# Environment parameters
m.setExternalStateVariable("Temperature", 293.15)
m.setParameter("AmbientPressure", pamb)

# Material parameters
m.setMaterialProperty("YoungModulus", E)
if mcc_model == "ModCamClay_semiExpl_constE":
m.setMaterialProperty("YoungModulus", E0)
m.setParameter("AmbientPressure", pamb)
print("Young Modulus set to E =", E0 / 1e6, " MPa")
if mcc_model in (
"ModCamClay_semiExpl",
"ModCamClay_semiExpl_absP",
):
m.setMaterialProperty("InitialVolumeRatio", v0)
m.setMaterialProperty("PoissonRatio", nu)
m.setMaterialProperty("CriticalStateLineSlope", M)
m.setMaterialProperty("SwellingLineSlope", ka)
Expand All @@ -50,22 +68,46 @@
m.setInternalStateVariableInitialValue("PreConsolidationPressure", pc0)
m.setInternalStateVariableInitialValue("VolumeRatio", v0)

# Set initial stress and strain state
eps_init = [-e_con, -e_con, -e_con, 0.0]
sig_init = [-p_con, -p_con, -p_con, 0.0]
m.setStress(sig_init)
m.setStrain(eps_init)

m.setImposedStress("SRR", {0: -p_con, 0.02: -p_con, 1.0: -p_con})
m.setImposedStress("STT", {0: -p_con, 0.02: -p_con, 1.0: -p_con})

if control == "stress":
# stress-controlled: works only until reaching the CSL
m.setImposedStress("SZZ", {0: -p_con, 0.02: -p_con, 1.0: -p_axi})
if control == "strain":
# Strain-controlled: works, CSL reached asymptotically for EZZ->inf
m.setImposedStrain("EZZ", {0: -e_con, 0.02: -e_con, 1.0: -e_axi})
print("confining strain in z direction: ", e_con)

s = mtest.MTestCurrentState()
wk = mtest.MTestWorkSpace()
m.completeInitialisation()
m.initializeCurrentState(s)
m.initializeWorkSpace(wk)

# initialize output lists
pCurve = np.array([pamb])
pCurve = np.array([pamb + p_con])
qCurve = np.array([0.0])
eVCurve = np.array([0.0])
eQCurve = np.array([0.0])
lpCurve = np.array([0.0])
pcCurve = np.array([pc0])
phiCurve = np.array([phi0])
strains = np.empty(shape=(6, nTime))
stresses = np.empty(shape=(6, nTime))
strains = np.empty(shape=(4, nTime))
stresses = np.empty(shape=(4, nTime))

# stresses[0][:] = sig_init
for k in range(4):
strains[k][0] = eps_init[k]

for k in range(4):
stresses[k][0] = sig_init[k]

# initialize yield functions
nPoints = 1000
Expand Down Expand Up @@ -151,7 +193,6 @@
print("final von Mises stress: ", vMstress, "Pa")
print("final hydrostatic pressure: ", pressure, "Pa")
print("final pre-consolidation pressure: ", pc, "Pa")
print("confining strain in z direction: ", e_con)

# plots
fig, ax = plt.subplots()
Expand Down Expand Up @@ -184,6 +225,7 @@
ax.set_ylabel("$q$ / Pa")
ax.grid()
ax.legend()
fig.tight_layout()
fig.savefig("ModCamClay_TriaxStudy_YieldSurface.pdf")

fig, ax = plt.subplots()
Expand Down
233 changes: 233 additions & 0 deletions MaterialLib/SolidModels/MFront/ModCamClay_semiExpl.mfront
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
@DSL Implicit;
@Behaviour ModCamClay_semiExpl;
@Author Christian Silbermann, Eric Simo, Miguel Mánica, Thomas Helfer, Thomas Nagel;
@Date 10/01/2023;
@Description{
The modified cam-clay model according to Callari (1998):
"A finite-strain cam-clay model in the framework of multiplicative elasto-plasticity"
but here in a consistent geometrically linear form (linearized volume ratio evolution)
semi-explicit due to explicit volume ratio update at the end of time step,
nonlinear hypoelastic behavior: pressure-dependent bulk modulus, constant Poisson ratio,
incremental formulation assuming constant elastic parameters over the time step,
normalized plastic flow direction, lower limit for a minimal pre-consolidation pressure,
}

/* Domain variables: dt (time increment)
(Input) theta (implicit time integration parameter)
eto, deto (total strain (increment))
eel, deel (elastic strain (increment))
sig (stress)
dlp (plastic increment)
dpc (pre-consolidation pressure increment)

Output: feel (strain residual depending on deel, dlp, dpc)
flp (yield function residual depending on deel, dpc)
fpc (pc evolution residual depending on deel, dlp, dpc, dphi)
df..._dd... partial derivatives of the residuals
*/

@Theta 1.0; // time integration scheme
@Epsilon 1e-14; // tolerance of local stress integration algorithm
@MaximumNumberOfIterations 20; // for local local stress integration algorithm
@ModellingHypotheses{".+"}; // supporting all stress and strain states
@Algorithm NewtonRaphson; //_NumericalJacobian;_LevenbergMarquardt

// material parameters
@MaterialProperty real nu;
@PhysicalBounds nu in [-1:0.5];
nu.setGlossaryName("PoissonRatio");

@MaterialProperty real M;
@PhysicalBounds M in [0:*[;
M.setEntryName("CriticalStateLineSlope");

@MaterialProperty real ka;
@PhysicalBounds ka in [0:*[;
ka.setEntryName("SwellingLineSlope");

@MaterialProperty real la;
@PhysicalBounds la in [0:*[;
la.setEntryName("VirginConsolidationLineSlope");

@MaterialProperty stress pc_char;
pc_char.setEntryName("CharacteristicPreConsolidationPressure");
@PhysicalBounds pc_char in [0:*[;

// Initial value of the volume ratio represents the operating point for the linearization.
@MaterialProperty real v0;
@PhysicalBounds v0 in [1:*[;
v0.setEntryName("InitialVolumeRatio");

// state variables (beside eel):
// A "standard" state variable is a persistent state variable and an integration variable.
@StateVariable real lp;
lp.setGlossaryName("EquivalentPlasticStrain");

// Reduced (normalized) pre-consolidation pressure for better integration performance
@IntegrationVariable strain rpc;

// An auxiliary state variable is a persistent variable but not an integration variable.
@AuxiliaryStateVariable stress pc;
pc.setEntryName("PreConsolidationPressure");

@AuxiliaryStateVariable real epl_V;
epl_V.setEntryName("PlasticVolumetricStrain");

@AuxiliaryStateVariable real v;
@PhysicalBounds v in [1:*[;
v.setEntryName("VolumeRatio"); // Total volume per solid volume = inv(1 - porosity)

// local variables
@LocalVariable StressStensor sig0;
@LocalVariable StiffnessTensor dsig_deel;
@LocalVariable bool withinElasticRange;
@LocalVariable real M2;
@LocalVariable real young;
@LocalVariable real pc_min;
@LocalVariable real rpc_min;

@InitLocalVariables
{
tfel::raise_if(la < ka, "Invalid parameters: la<ka");
M2 = M * M;

// update sig0
sig0 = sig;

// compute elastic stiffness (constant during time step)
const auto p = -trace(sig) / 3;
const auto K = v0 / ka * p;
const auto E = 3.0 * K * (1.0 - 2*nu);

young = E;
rpc = pc / young;
pc_min = 0.5e-8 * pc_char;
rpc_min = pc_min / young;

// stress derivative
dsig_deel = E / (1.0 + nu) * Stensor4::K() + K * Stensor4::IxI();

// elastic trial stress
const auto sig_el = sig0 + dsig_deel * deto;

// elastic estimators
const auto s_el = deviator(sig_el);
const auto q_el = std::sqrt(1.5 * s_el | s_el);
const auto p_el = -trace(sig_el) / 3;

const auto pc_el = pc;
const auto f_el = q_el * q_el + M2 * p_el * (p_el - pc_el);
withinElasticRange = f_el < 0;
}

@ComputeStress {
sig = sig0 + theta * dsig_deel * deel;
}

@Integrator
{
constexpr const auto id2 = Stensor::Id();
constexpr const auto Pr4 = Stensor4::K();
const auto the = v0 / (la - ka);

// elastic range:
if (withinElasticRange)
{
feel -= deto;
return true;
}
// plastic range:
const auto epsr = strain(1.e-12);
// calculate invariants from current stress sig
const auto s = deviator(sig);
const auto q = std::sqrt(1.5 * s | s);
const auto p = -trace(sig) / 3;
// update the internal (state) variables (rpc holds the old value!)
const auto rpc_new = rpc + theta * drpc;
const auto pc_new = rpc_new * young;
// calculate the direction of plastic flow
const auto f = q * q + M2 * p * (p - pc_new);
const auto df_dp = M2 * (2 * p - pc_new);
const auto df_dsig = eval(3 * s - df_dp * id2 / 3);
auto norm = std::sqrt(6 * q * q + df_dp * df_dp / 3); // = std::sqrt(df_dsig|df_dsig);
norm = std::max(norm, epsr * young);
const auto n = df_dsig / norm;
const auto ntr = -df_dp / norm;
// plastic strain and volumetric part
const auto depl = eval(dlp * n);
const auto deplV = trace(depl);

const auto fchar = pc_char * young;

// residual
feel = deel + depl - deto;
flp = f / fchar;
frpc = drpc + deplV * the * (rpc_new - rpc_min);

// auxiliary derivatives
const auto dnorm_dsig = (9 * s - 2 * M2 / 9 * df_dp * id2) / norm;
const auto dn_ddeel = (3 * Pr4 + 2 * M2 / 9 * (id2 ^ id2) - (n ^ dnorm_dsig)) / norm * dsig_deel * theta;
const auto dn_ddrpc = (id2 + df_dp * n / norm) * M2 / (3 * norm) * theta * young;
const auto dfrpc_ddeplV = the * (rpc_new - rpc_min);

// jacobian (all other parts are zero)
dfeel_ddeel += dlp * dn_ddeel;
dfeel_ddlp = n;
dfeel_ddrpc = dlp * dn_ddrpc;

dflp_ddeel = (df_dsig | dsig_deel) * theta / fchar; // in case of problems with zero use:
dflp_ddlp = strain(0); // (q<epsr) ? strain(1) : strain(0);
dflp_ddrpc = -M2 * p * theta / fchar * young;

dfrpc_ddlp = dfrpc_ddeplV * ntr;
dfrpc_ddeel = dfrpc_ddeplV * dlp * (id2 | dn_ddeel);
dfrpc_ddrpc = 1 + deplV * the * theta + dfrpc_ddeplV * dlp * trace(dn_ddrpc);
}

@ComputeFinalStress {
// updating the stress at the end of the time step
sig = sig0 + dsig_deel * deel;
}

// explicit treatment as long as change of v (or e) during time increment is small
@UpdateAuxiliaryStateVariables
{
pc += drpc * young;
const auto deelV = trace(deel);
const auto detoV = trace(deto);
epl_V += detoV - deelV;
v += v0 * detoV;
}

@AdditionalConvergenceChecks
{
if (converged)
{
if (!withinElasticRange)
{
if (dlp < 0)
{
converged = false;
withinElasticRange = true;
}
}
}
}

@TangentOperator // because no Brick StandardElasticity
{
if ((smt == ELASTIC) || (smt == SECANTOPERATOR))
{
Dt = dsig_deel;
}
else if (smt == CONSISTENTTANGENTOPERATOR)
{
Stensor4 Je;
getPartialJacobianInvert(Je);
Dt = dsig_deel * Je;
}
else
{
return false;
}
}
Loading

0 comments on commit 9ee9bcd

Please sign in to comment.