LCOV - code coverage report
Current view: top level - src/materials - FiniteStrainPlasticMaterial.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: f45d79 Lines: 141 146 96.6 %
Date: 2025-07-25 05:00:39 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "FiniteStrainPlasticMaterial.h"
      11             : #include "libmesh/utility.h"
      12             : 
      13             : registerMooseObject("SolidMechanicsApp", FiniteStrainPlasticMaterial);
      14             : 
      15             : InputParameters
      16          72 : FiniteStrainPlasticMaterial::validParams()
      17             : {
      18          72 :   InputParameters params = ComputeStressBase::validParams();
      19             : 
      20         144 :   params.addRequiredParam<std::vector<Real>>(
      21             :       "yield_stress",
      22             :       "Input data as pairs of equivalent plastic strain and yield stress: Should "
      23             :       "start with equivalent plastic strain 0");
      24         144 :   params.addParam<Real>("rtol", 1e-8, "Plastic strain NR tolerance");
      25         144 :   params.addParam<Real>("ftol", 1e-4, "Consistency condition NR tolerance");
      26         144 :   params.addParam<Real>("eptol", 1e-7, "Equivalent plastic strain NR tolerance");
      27          72 :   params.addClassDescription("Associative J2 plasticity with isotropic hardening.");
      28             : 
      29          72 :   return params;
      30           0 : }
      31             : 
      32          54 : FiniteStrainPlasticMaterial::FiniteStrainPlasticMaterial(const InputParameters & parameters)
      33             :   : ComputeStressBase(parameters),
      34          54 :     _yield_stress_vector(getParam<std::vector<Real>>("yield_stress")), // Read from input file
      35          54 :     _plastic_strain(declareProperty<RankTwoTensor>(_base_name + "plastic_strain")),
      36         108 :     _plastic_strain_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "plastic_strain")),
      37          54 :     _eqv_plastic_strain(declareProperty<Real>(_base_name + "eqv_plastic_strain")),
      38         108 :     _eqv_plastic_strain_old(getMaterialPropertyOld<Real>(_base_name + "eqv_plastic_strain")),
      39         108 :     _stress_old(getMaterialPropertyOld<RankTwoTensor>(_base_name + "stress")),
      40         108 :     _strain_increment(getMaterialProperty<RankTwoTensor>(_base_name + "strain_increment")),
      41         108 :     _rotation_increment(getMaterialProperty<RankTwoTensor>(_base_name + "rotation_increment")),
      42          54 :     _elasticity_tensor_name(_base_name + "elasticity_tensor"),
      43          54 :     _elasticity_tensor(getMaterialPropertyByName<RankFourTensor>(_elasticity_tensor_name)),
      44         108 :     _rtol(getParam<Real>("rtol")),
      45         108 :     _ftol(getParam<Real>("ftol")),
      46         108 :     _eptol(getParam<Real>("eptol")),
      47          54 :     _deltaOuter(RankTwoTensor::Identity().times<i_, j_, k_, l_>(RankTwoTensor::Identity())),
      48         108 :     _deltaMixed(RankTwoTensor::Identity().times<i_, k_, j_, l_>(RankTwoTensor::Identity()))
      49             : {
      50          54 : }
      51             : 
      52             : void
      53         192 : FiniteStrainPlasticMaterial::initQpStatefulProperties()
      54             : {
      55         192 :   ComputeStressBase::initQpStatefulProperties();
      56         192 :   _plastic_strain[_qp].zero();
      57         192 :   _eqv_plastic_strain[_qp] = 0.0;
      58         192 : }
      59             : 
      60             : void
      61       33856 : FiniteStrainPlasticMaterial::computeQpStress()
      62             : {
      63             : 
      64             :   // perform the return-mapping algorithm
      65       33856 :   returnMap(_stress_old[_qp],
      66       33856 :             _eqv_plastic_strain_old[_qp],
      67       33856 :             _plastic_strain_old[_qp],
      68       33856 :             _strain_increment[_qp],
      69       33856 :             _elasticity_tensor[_qp],
      70       33856 :             _stress[_qp],
      71       33856 :             _eqv_plastic_strain[_qp],
      72       33856 :             _plastic_strain[_qp]);
      73             : 
      74             :   // Rotate the stress tensor to the current configuration
      75       33856 :   _stress[_qp] = _rotation_increment[_qp] * _stress[_qp] * _rotation_increment[_qp].transpose();
      76             : 
      77             :   // Rotate plastic strain tensor to the current configuration
      78       33856 :   _plastic_strain[_qp] =
      79       33856 :       _rotation_increment[_qp] * _plastic_strain[_qp] * _rotation_increment[_qp].transpose();
      80             : 
      81             :   // Calculate the elastic strain_increment
      82       33856 :   _elastic_strain[_qp] = _mechanical_strain[_qp] - _plastic_strain[_qp];
      83             : 
      84       33856 :   _Jacobian_mult[_qp] = _elasticity_tensor[_qp];
      85       33856 : }
      86             : 
      87             : /**
      88             :  * Implements the return-map algorithm via a Newton-Raphson process.
      89             :  * This idea is fully explained in Simo and Hughes "Computational
      90             :  * Inelasticity" Springer 1997, for instance, as well as many other
      91             :  * books on plasticity.
      92             :  * The basic idea is as follows.
      93             :  * Given: sig_old - the stress at the start of the "time step"
      94             :  *        plastic_strain_old - the plastic strain at the start of the "time step"
      95             :  *        eqvpstrain_old - equivalent plastic strain at the start of the "time step"
      96             :  *                         (In general we would be given some number of internal
      97             :  *                         parameters that the yield function depends upon.)
      98             :  *        delta_d - the prescribed strain increment for this "time step"
      99             :  * we want to determine the following parameters at the end of this "time step":
     100             :  *        sig - the stress
     101             :  *        plastic_strain - the plastic strain
     102             :  *        eqvpstrain - the equivalent plastic strain (again, in general, we would
     103             :  *                     have an arbitrary number of internal parameters).
     104             :  *
     105             :  * To determine these parameters, introduce
     106             :  *    the "yield function", f
     107             :  *    the "consistency parameter", flow_incr
     108             :  *    the "flow potential", flow_dirn_ij
     109             :  *    the "internal potential", internalPotential (in general there are as many internalPotential
     110             :  *        functions as there are internal parameters).
     111             :  * All three of f, flow_dirn_ij, and internalPotential, are functions of
     112             :  * sig and eqvpstrain.
     113             :  * To find sig, plastic_strain and eqvpstrain, we need to solve the following
     114             :  *   resid_ij = 0
     115             :  *   f = 0
     116             :  *   rep = 0
     117             :  * This is done by using Newton-Raphson.
     118             :  * There are 8 equations here: six from resid_ij=0 (more generally there are nine
     119             :  * but in this case resid is symmetric); one from f=0; one from rep=0 (more generally, for N
     120             :  * internal parameters there are N of these equations).
     121             :  *
     122             :  * resid_ij = flow_incr*flow_dirn_ij - (plastic_strain - plastic_strain_old)_ij
     123             :  *          = flow_incr*flow_dirn_ij - (E^{-1}(trial_stress - sig))_ij
     124             :  * Here trial_stress = E*(strain - plastic_strain_old)
     125             :  *      sig = E*(strain - plastic_strain)
     126             :  * Note: flow_dirn_ij is evaluated at sig and eqvpstrain (not the old values).
     127             :  *
     128             :  * f is the yield function, evaluated at sig and eqvpstrain
     129             :  *
     130             :  * rep = -flow_incr*internalPotential - (eqvpstrain - eqvpstrain_old)
     131             :  * Here internalPotential are evaluated at sig and eqvpstrain (not the old values).
     132             :  *
     133             :  * The Newton-Raphson procedure has sig, flow_incr, and eqvpstrain as its
     134             :  * variables.  Therefore we need the derivatives of resid_ij, f, and rep
     135             :  * with respect to these parameters
     136             :  *
     137             :  * In this associative J2 with isotropic hardening, things are a little more specialised.
     138             :  * (1) f = sqrt(3*s_ij*s_ij/2) - K(eqvpstrain)    (this is called "isotropic hardening")
     139             :  * (2) associativity means that flow_dirn_ij = df/d(sig_ij) = s_ij*sqrt(3/2/(s_ij*s_ij)), and
     140             :  *     this means that flow_dirn_ij*flow_dirn_ij = 3/2, so when resid_ij=0, we get
     141             :  *     (plastic_strain_dot)_ij*(plastic_strain_dot)_ij = (3/2)*flow_incr^2, where
     142             :  *     plastic_strain_dot = plastic_strain - plastic_strain_old
     143             :  * (3) The definition of equivalent plastic strain is through
     144             :  *     eqvpstrain_dot = sqrt(2*plastic_strain_dot_ij*plastic_strain_dot_ij/3), so
     145             :  *     using (2), we obtain eqvpstrain_dot = flow_incr, and this yields
     146             :  *     internalPotential = -1 in the "rep" equation.
     147             :  */
     148             : void
     149       33856 : FiniteStrainPlasticMaterial::returnMap(const RankTwoTensor & sig_old,
     150             :                                        const Real eqvpstrain_old,
     151             :                                        const RankTwoTensor & plastic_strain_old,
     152             :                                        const RankTwoTensor & delta_d,
     153             :                                        const RankFourTensor & E_ijkl,
     154             :                                        RankTwoTensor & sig,
     155             :                                        Real & eqvpstrain,
     156             :                                        RankTwoTensor & plastic_strain)
     157             : {
     158             :   // the yield function, must be non-positive
     159             :   // Newton-Raphson sets this to zero if trial stress enters inadmissible region
     160             :   Real f;
     161             : 
     162             :   // the consistency parameter, must be non-negative
     163             :   // change in plastic strain in this timestep = flow_incr*flow_potential
     164       33856 :   Real flow_incr = 0.0;
     165             : 
     166             :   // direction of flow defined by the potential
     167       33856 :   RankTwoTensor flow_dirn;
     168             : 
     169             :   // Newton-Raphson sets this zero
     170             :   // resid_ij = flow_incr*flow_dirn_ij - (plastic_strain - plastic_strain_old)
     171       33856 :   RankTwoTensor resid;
     172             : 
     173             :   // Newton-Raphson sets this zero
     174             :   // rep = -flow_incr*internalPotential - (eqvpstrain - eqvpstrain_old)
     175             :   Real rep;
     176             : 
     177             :   // change in the stress (sig) in a Newton-Raphson iteration
     178       33856 :   RankTwoTensor ddsig;
     179             : 
     180             :   // change in the consistency parameter in a Newton-Raphson iteration
     181       33856 :   Real dflow_incr = 0.0;
     182             : 
     183             :   // change in equivalent plastic strain in one Newton-Raphson iteration
     184             :   Real deqvpstrain = 0.0;
     185             : 
     186             :   // convenience variable that holds the change in plastic strain incurred during the return
     187             :   // delta_dp = plastic_strain - plastic_strain_old
     188             :   // delta_dp = E^{-1}*(trial_stress - sig), where trial_stress = E*(strain - plastic_strain_old)
     189       33856 :   RankTwoTensor delta_dp;
     190             : 
     191             :   // d(yieldFunction)/d(stress)
     192       33856 :   RankTwoTensor df_dsig;
     193             : 
     194             :   // d(resid_ij)/d(sigma_kl)
     195       33856 :   RankFourTensor dr_dsig;
     196             : 
     197             :   // dr_dsig_inv_ijkl*dr_dsig_klmn = 0.5*(de_ij de_jn + de_ij + de_jm), where de_ij = 1 if i=j, but
     198             :   // zero otherwise
     199       33856 :   RankFourTensor dr_dsig_inv;
     200             : 
     201             :   // d(yieldFunction)/d(eqvpstrain)
     202             :   Real fq;
     203             : 
     204             :   // yield stress at the start of this "time step" (ie, evaluated with
     205             :   // eqvpstrain_old).  It is held fixed during the Newton-Raphson return,
     206             :   // even if eqvpstrain != eqvpstrain_old.
     207             :   Real yield_stress;
     208             : 
     209             :   // measures of whether the Newton-Raphson process has converged
     210             :   Real err1, err2, err3;
     211             : 
     212             :   // number of Newton-Raphson iterations performed
     213             :   unsigned int iter = 0;
     214             : 
     215             :   // maximum number of Newton-Raphson iterations allowed
     216             :   unsigned int maxiter = 100;
     217             : 
     218             :   // plastic loading occurs if yieldFunction > toly
     219             :   Real toly = 1.0e-8;
     220             : 
     221             :   // Assume this strain increment does not induce any plasticity
     222             :   // This is the elastic-predictor
     223       33856 :   sig = sig_old + E_ijkl * delta_d; // the trial stress
     224       33856 :   eqvpstrain = eqvpstrain_old;
     225       33856 :   plastic_strain = plastic_strain_old;
     226             : 
     227       33856 :   yield_stress = getYieldStress(eqvpstrain); // yield stress at this equivalent plastic strain
     228       33856 :   if (yieldFunction(sig, yield_stress) > toly)
     229             :   {
     230             :     // the sig just calculated is inadmissable.  We must return to the yield surface.
     231             :     // This is done iteratively, using a Newton-Raphson process.
     232             : 
     233             :     delta_dp.zero();
     234             : 
     235       33856 :     sig = sig_old + E_ijkl * delta_d; // this is the elastic predictor
     236             : 
     237       33856 :     flow_dirn = flowPotential(sig);
     238             : 
     239       33856 :     resid = flow_dirn * flow_incr - delta_dp; // Residual 1 - refer Hughes Simo
     240       33856 :     f = yieldFunction(sig, yield_stress);
     241       33856 :     rep = -eqvpstrain + eqvpstrain_old - flow_incr * internalPotential(); // Residual 3 rep=0
     242             : 
     243       33856 :     err1 = resid.L2norm();
     244             :     err2 = std::abs(f);
     245             :     err3 = std::abs(rep);
     246             : 
     247      160832 :     while ((err1 > _rtol || err2 > _ftol || err3 > _eptol) &&
     248             :            iter < maxiter) // Stress update iteration (hardness fixed)
     249             :     {
     250      126976 :       iter++;
     251             : 
     252      126976 :       df_dsig = dyieldFunction_dstress(sig);
     253      126976 :       getJac(sig, E_ijkl, flow_incr, dr_dsig);   // gets dr_dsig = d(resid_ij)/d(sig_kl)
     254      126976 :       fq = dyieldFunction_dinternal(eqvpstrain); // d(f)/d(eqvpstrain)
     255             : 
     256             :       /**
     257             :        * The linear system is
     258             :        *   ( dr_dsig  flow_dirn  0  )( ddsig       )   ( - resid )
     259             :        *   ( df_dsig     0       fq )( dflow_incr  ) = ( - f     )
     260             :        *   (   0         1       -1 )( deqvpstrain )   ( - rep   )
     261             :        * The zeroes are: d(resid_ij)/d(eqvpstrain) = flow_dirn*d(df/d(sig_ij))/d(eqvpstrain) = 0
     262             :        * and df/d(flow_dirn) = 0  (this is always true, even for general hardening and
     263             :        * non-associative)
     264             :        * and d(rep)/d(sig_ij) = -flow_incr*d(internalPotential)/d(sig_ij) = 0
     265             :        */
     266             : 
     267      126976 :       dr_dsig_inv = dr_dsig.invSymm();
     268             : 
     269             :       /**
     270             :        * Because of the zeroes and ones, the linear system is not impossible to
     271             :        * solve by hand.
     272             :        * NOTE: andy believes there was originally a sign-error in the next line.  The
     273             :        *       next line is unchanged, however andy's definition of fq is negative of
     274             :        *       the original definition of fq.  andy can't see any difference in any tests!
     275             :        */
     276      126976 :       dflow_incr = (f - df_dsig.doubleContraction(dr_dsig_inv * resid) + fq * rep) /
     277      126976 :                    (df_dsig.doubleContraction(dr_dsig_inv * flow_dirn) - fq);
     278      126976 :       ddsig =
     279      253952 :           dr_dsig_inv *
     280      126976 :           (-resid -
     281      126976 :            flow_dirn * dflow_incr); // from solving the top row of linear system, given dflow_incr
     282      126976 :       deqvpstrain =
     283             :           rep + dflow_incr; // from solving the bottom row of linear system, given dflow_incr
     284             : 
     285             :       // update the variables
     286      126976 :       flow_incr += dflow_incr;
     287      126976 :       delta_dp -= E_ijkl.invSymm() * ddsig;
     288      126976 :       sig += ddsig;
     289      126976 :       eqvpstrain += deqvpstrain;
     290             : 
     291             :       // evaluate the RHS equations ready for next Newton-Raphson iteration
     292      126976 :       flow_dirn = flowPotential(sig);
     293      126976 :       resid = flow_dirn * flow_incr - delta_dp;
     294      126976 :       f = yieldFunction(sig, yield_stress);
     295      126976 :       rep = -eqvpstrain + eqvpstrain_old - flow_incr * internalPotential();
     296             : 
     297      126976 :       err1 = resid.L2norm();
     298             :       err2 = std::abs(f);
     299             :       err3 = std::abs(rep);
     300             :     }
     301             : 
     302       33856 :     if (iter >= maxiter)
     303           0 :       mooseError("Constitutive failure");
     304             : 
     305       33856 :     plastic_strain += delta_dp;
     306             :   }
     307       33856 : }
     308             : 
     309             : Real
     310      194688 : FiniteStrainPlasticMaterial::yieldFunction(const RankTwoTensor & stress, const Real yield_stress)
     311             : {
     312      194688 :   return getSigEqv(stress) - yield_stress;
     313             : }
     314             : 
     315             : RankTwoTensor
     316      541760 : FiniteStrainPlasticMaterial::dyieldFunction_dstress(const RankTwoTensor & sig)
     317             : {
     318      541760 :   RankTwoTensor deriv = sig.dsecondInvariant();
     319      541760 :   deriv *= std::sqrt(3.0 / sig.secondInvariant()) / 2.0;
     320      541760 :   return deriv;
     321             : }
     322             : 
     323             : Real
     324      126976 : FiniteStrainPlasticMaterial::dyieldFunction_dinternal(const Real equivalent_plastic_strain)
     325             : {
     326      126976 :   return -getdYieldStressdPlasticStrain(equivalent_plastic_strain);
     327             : }
     328             : 
     329             : RankTwoTensor
     330      287808 : FiniteStrainPlasticMaterial::flowPotential(const RankTwoTensor & sig)
     331             : {
     332      287808 :   return dyieldFunction_dstress(sig); // this plasticity model assumes associative flow
     333             : }
     334             : 
     335             : Real
     336      160832 : FiniteStrainPlasticMaterial::internalPotential()
     337             : {
     338      160832 :   return -1;
     339             : }
     340             : 
     341             : Real
     342      321664 : FiniteStrainPlasticMaterial::getSigEqv(const RankTwoTensor & stress)
     343             : {
     344      321664 :   return std::sqrt(3 * stress.secondInvariant());
     345             : }
     346             : 
     347             : // Jacobian for stress update algorithm
     348             : void
     349      126976 : FiniteStrainPlasticMaterial::getJac(const RankTwoTensor & sig,
     350             :                                     const RankFourTensor & E_ijkl,
     351             :                                     Real flow_incr,
     352             :                                     RankFourTensor & dresid_dsig)
     353             : {
     354      126976 :   RankTwoTensor sig_dev, df_dsig, flow_dirn;
     355      126976 :   RankTwoTensor dfi_dft, dfi_dsig;
     356      126976 :   RankFourTensor dft_dsig, dfd_dft, dfd_dsig;
     357             :   Real sig_eqv;
     358             :   Real f1, f2, f3;
     359      126976 :   RankFourTensor temp;
     360             : 
     361      126976 :   sig_dev = sig.deviatoric();
     362      126976 :   sig_eqv = getSigEqv(sig);
     363      126976 :   df_dsig = dyieldFunction_dstress(sig);
     364      126976 :   flow_dirn = flowPotential(sig);
     365             : 
     366      126976 :   f1 = 3.0 / (2.0 * sig_eqv);
     367      126976 :   f2 = f1 / 3.0;
     368      126976 :   f3 = 9.0 / (4.0 * Utility::pow<3>(sig_eqv));
     369             : 
     370      126976 :   dft_dsig = f1 * _deltaMixed - f2 * _deltaOuter - f3 * sig_dev.outerProduct(sig_dev);
     371             : 
     372      126976 :   dfd_dsig = dft_dsig;
     373      126976 :   dresid_dsig = E_ijkl.invSymm() + dfd_dsig * flow_incr;
     374      126976 : }
     375             : 
     376             : // Obtain yield stress for a given equivalent plastic strain (input)
     377             : Real
     378       33856 : FiniteStrainPlasticMaterial::getYieldStress(const Real eqpe)
     379             : {
     380             :   unsigned nsize;
     381             : 
     382       33856 :   nsize = _yield_stress_vector.size();
     383             : 
     384       33856 :   if (_yield_stress_vector[0] > 0.0 || nsize % 2 > 0) // Error check for input inconsitency
     385           0 :     mooseError("Error in yield stress input: Should be a vector with eqv plastic strain and yield "
     386             :                "stress pair values.\n");
     387             : 
     388             :   unsigned int ind = 0;
     389             :   Real tol = 1e-8;
     390             : 
     391      111872 :   while (ind < nsize)
     392             :   {
     393      111872 :     if (std::abs(eqpe - _yield_stress_vector[ind]) < tol)
     394        3424 :       return _yield_stress_vector[ind + 1];
     395             : 
     396      108448 :     if (ind + 2 < nsize)
     397             :     {
     398      108448 :       if (eqpe > _yield_stress_vector[ind] && eqpe < _yield_stress_vector[ind + 2])
     399       30432 :         return _yield_stress_vector[ind + 1] +
     400       30432 :                (eqpe - _yield_stress_vector[ind]) /
     401       30432 :                    (_yield_stress_vector[ind + 2] - _yield_stress_vector[ind]) *
     402       30432 :                    (_yield_stress_vector[ind + 3] - _yield_stress_vector[ind + 1]);
     403             :     }
     404             :     else
     405           0 :       return _yield_stress_vector[nsize - 1];
     406             : 
     407             :     ind += 2;
     408             :   }
     409             : 
     410             :   return 0.0;
     411             : }
     412             : 
     413             : Real
     414      126976 : FiniteStrainPlasticMaterial::getdYieldStressdPlasticStrain(const Real eqpe)
     415             : {
     416             :   unsigned nsize;
     417             : 
     418      126976 :   nsize = _yield_stress_vector.size();
     419             : 
     420      126976 :   if (_yield_stress_vector[0] > 0.0 || nsize % 2 > 0) // Error check for input inconsitency
     421           0 :     mooseError("Error in yield stress input: Should be a vector with eqv plastic strain and yield "
     422             :                "stress pair values.\n");
     423             : 
     424             :   unsigned int ind = 0;
     425             : 
     426      440000 :   while (ind < nsize)
     427             :   {
     428      440000 :     if (ind + 2 < nsize)
     429             :     {
     430      440000 :       if (eqpe >= _yield_stress_vector[ind] && eqpe < _yield_stress_vector[ind + 2])
     431      126976 :         return (_yield_stress_vector[ind + 3] - _yield_stress_vector[ind + 1]) /
     432      126976 :                (_yield_stress_vector[ind + 2] - _yield_stress_vector[ind]);
     433             :     }
     434             :     else
     435             :       return 0.0;
     436             : 
     437             :     ind += 2;
     438             :   }
     439             : 
     440             :   return 0.0;
     441             : }

Generated by: LCOV version 1.14