LCOV - code coverage report
Current view: top level - src/userobjects - TensorMechanicsPlasticMohrCoulombMulti.C (source / functions) Hit Total Coverage
Test: idaholab/moose tensor_mechanics: d6b47a Lines: 429 456 94.1 %
Date: 2024-02-27 11:53:14 Functions: 26 28 92.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://www.mooseframework.org
       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 "TensorMechanicsPlasticMohrCoulombMulti.h"
      11             : #include "RankFourTensor.h"
      12             : 
      13             : // Following is for perturbing eigvenvalues.  This looks really bodgy, but works quite well!
      14             : #include "MooseRandom.h"
      15             : #include "libmesh/utility.h"
      16             : 
      17             : registerMooseObject("TensorMechanicsApp", TensorMechanicsPlasticMohrCoulombMulti);
      18             : 
      19             : InputParameters
      20          68 : TensorMechanicsPlasticMohrCoulombMulti::validParams()
      21             : {
      22          68 :   InputParameters params = TensorMechanicsPlasticModel::validParams();
      23          68 :   params.addClassDescription("Non-associative Mohr-Coulomb plasticity with hardening/softening");
      24         136 :   params.addRequiredParam<UserObjectName>(
      25             :       "cohesion", "A TensorMechanicsHardening UserObject that defines hardening of the cohesion");
      26         136 :   params.addRequiredParam<UserObjectName>("friction_angle",
      27             :                                           "A TensorMechanicsHardening UserObject "
      28             :                                           "that defines hardening of the "
      29             :                                           "friction angle (in radians)");
      30         136 :   params.addRequiredParam<UserObjectName>("dilation_angle",
      31             :                                           "A TensorMechanicsHardening UserObject "
      32             :                                           "that defines hardening of the "
      33             :                                           "dilation angle (in radians)");
      34         136 :   params.addParam<unsigned int>("max_iterations",
      35         136 :                                 10,
      36             :                                 "Maximum number of Newton-Raphson iterations "
      37             :                                 "allowed in the custom return-map algorithm. "
      38             :                                 " For highly nonlinear hardening this may "
      39             :                                 "need to be higher than 10.");
      40         136 :   params.addParam<Real>("shift",
      41             :                         "Yield surface is shifted by this amount to avoid problems with "
      42             :                         "defining derivatives when eigenvalues are equal.  If this is "
      43             :                         "larger than f_tol, a warning will be issued.  This may be set "
      44             :                         "very small when using the custom returnMap.  Default = f_tol.");
      45         136 :   params.addParam<bool>("use_custom_returnMap",
      46         136 :                         true,
      47             :                         "Use a custom return-map algorithm for this "
      48             :                         "plasticity model, which may speed up "
      49             :                         "computations considerably.  Set to true "
      50             :                         "only for isotropic elasticity with no "
      51             :                         "hardening of the dilation angle.  In this "
      52             :                         "case you may set 'shift' very small.");
      53             : 
      54          68 :   return params;
      55           0 : }
      56             : 
      57          34 : TensorMechanicsPlasticMohrCoulombMulti::TensorMechanicsPlasticMohrCoulombMulti(
      58          34 :     const InputParameters & parameters)
      59             :   : TensorMechanicsPlasticModel(parameters),
      60          34 :     _cohesion(getUserObject<TensorMechanicsHardeningModel>("cohesion")),
      61          34 :     _phi(getUserObject<TensorMechanicsHardeningModel>("friction_angle")),
      62          34 :     _psi(getUserObject<TensorMechanicsHardeningModel>("dilation_angle")),
      63          68 :     _max_iters(getParam<unsigned int>("max_iterations")),
      64          84 :     _shift(parameters.isParamValid("shift") ? getParam<Real>("shift") : _f_tol),
      65         102 :     _use_custom_returnMap(getParam<bool>("use_custom_returnMap"))
      66             : {
      67          34 :   if (_shift < 0)
      68           0 :     mooseError("Value of 'shift' in TensorMechanicsPlasticMohrCoulombMulti must not be negative\n");
      69          34 :   if (_shift > _f_tol)
      70             :     _console << "WARNING: value of 'shift' in TensorMechanicsPlasticMohrCoulombMulti is probably "
      71           0 :                 "set too high"
      72           0 :              << std::endl;
      73             :   if (LIBMESH_DIM != 3)
      74             :     mooseError("TensorMechanicsPlasticMohrCoulombMulti is only defined for LIBMESH_DIM=3");
      75             :   MooseRandom::seed(0);
      76          34 : }
      77             : 
      78             : unsigned int
      79     2528288 : TensorMechanicsPlasticMohrCoulombMulti::numberSurfaces() const
      80             : {
      81     2528288 :   return 6;
      82             : }
      83             : 
      84             : void
      85       68408 : TensorMechanicsPlasticMohrCoulombMulti::yieldFunctionV(const RankTwoTensor & stress,
      86             :                                                        Real intnl,
      87             :                                                        std::vector<Real> & f) const
      88             : {
      89             :   std::vector<Real> eigvals;
      90       68408 :   stress.symmetricEigenvalues(eigvals);
      91       68408 :   eigvals[0] += _shift;
      92       68408 :   eigvals[2] -= _shift;
      93             : 
      94       68408 :   const Real sinphi = std::sin(phi(intnl));
      95       68408 :   const Real cosphi = std::cos(phi(intnl));
      96       68408 :   const Real cohcos = cohesion(intnl) * cosphi;
      97             : 
      98       68408 :   yieldFunctionEigvals(eigvals[0], eigvals[1], eigvals[2], sinphi, cohcos, f);
      99       68408 : }
     100             : 
     101             : void
     102       94088 : TensorMechanicsPlasticMohrCoulombMulti::yieldFunctionEigvals(
     103             :     Real e0, Real e1, Real e2, Real sinphi, Real cohcos, std::vector<Real> & f) const
     104             : {
     105             :   // Naively it seems a shame to have 6 yield functions active instead of just
     106             :   // 3.  But 3 won't do.  Eg, think of a loading with eigvals[0]=eigvals[1]=eigvals[2]
     107             :   // Then to return to the yield surface would require 2 positive plastic multipliers
     108             :   // and one negative one.  Boo hoo.
     109             : 
     110       94088 :   f.resize(6);
     111       94088 :   f[0] = 0.5 * (e0 - e1) + 0.5 * (e0 + e1) * sinphi - cohcos;
     112       94088 :   f[1] = 0.5 * (e1 - e0) + 0.5 * (e0 + e1) * sinphi - cohcos;
     113       94088 :   f[2] = 0.5 * (e0 - e2) + 0.5 * (e0 + e2) * sinphi - cohcos;
     114       94088 :   f[3] = 0.5 * (e2 - e0) + 0.5 * (e0 + e2) * sinphi - cohcos;
     115       94088 :   f[4] = 0.5 * (e1 - e2) + 0.5 * (e1 + e2) * sinphi - cohcos;
     116       94088 :   f[5] = 0.5 * (e2 - e1) + 0.5 * (e1 + e2) * sinphi - cohcos;
     117       94088 : }
     118             : 
     119             : void
     120        7632 : TensorMechanicsPlasticMohrCoulombMulti::perturbStress(const RankTwoTensor & stress,
     121             :                                                       std::vector<Real> & eigvals,
     122             :                                                       std::vector<RankTwoTensor> & deigvals) const
     123             : {
     124             :   Real small_perturbation;
     125        7632 :   RankTwoTensor shifted_stress = stress;
     126       20827 :   while (eigvals[0] > eigvals[1] - 0.1 * _shift || eigvals[1] > eigvals[2] - 0.1 * _shift)
     127             :   {
     128       52780 :     for (unsigned i = 0; i < 3; ++i)
     129      118755 :       for (unsigned j = 0; j <= i; ++j)
     130             :       {
     131       79170 :         small_perturbation = 0.1 * _shift * 2 * (MooseRandom::rand() - 0.5);
     132       79170 :         shifted_stress(i, j) += small_perturbation;
     133       79170 :         shifted_stress(j, i) += small_perturbation;
     134             :       }
     135       13195 :     shifted_stress.dsymmetricEigenvalues(eigvals, deigvals);
     136             :   }
     137        7632 : }
     138             : 
     139             : void
     140       55568 : TensorMechanicsPlasticMohrCoulombMulti::df_dsig(const RankTwoTensor & stress,
     141             :                                                 Real sin_angle,
     142             :                                                 std::vector<RankTwoTensor> & df) const
     143             : {
     144             :   std::vector<Real> eigvals;
     145             :   std::vector<RankTwoTensor> deigvals;
     146       55568 :   stress.dsymmetricEigenvalues(eigvals, deigvals);
     147             : 
     148       55568 :   if (eigvals[0] > eigvals[1] - 0.1 * _shift || eigvals[1] > eigvals[2] - 0.1 * _shift)
     149        5088 :     perturbStress(stress, eigvals, deigvals);
     150             : 
     151       55568 :   df.resize(6);
     152       55568 :   df[0] = 0.5 * (deigvals[0] - deigvals[1]) + 0.5 * (deigvals[0] + deigvals[1]) * sin_angle;
     153       55568 :   df[1] = 0.5 * (deigvals[1] - deigvals[0]) + 0.5 * (deigvals[0] + deigvals[1]) * sin_angle;
     154       55568 :   df[2] = 0.5 * (deigvals[0] - deigvals[2]) + 0.5 * (deigvals[0] + deigvals[2]) * sin_angle;
     155       55568 :   df[3] = 0.5 * (deigvals[2] - deigvals[0]) + 0.5 * (deigvals[0] + deigvals[2]) * sin_angle;
     156       55568 :   df[4] = 0.5 * (deigvals[1] - deigvals[2]) + 0.5 * (deigvals[1] + deigvals[2]) * sin_angle;
     157       55568 :   df[5] = 0.5 * (deigvals[2] - deigvals[1]) + 0.5 * (deigvals[1] + deigvals[2]) * sin_angle;
     158       55568 : }
     159             : 
     160             : void
     161       20168 : TensorMechanicsPlasticMohrCoulombMulti::dyieldFunction_dstressV(
     162             :     const RankTwoTensor & stress, Real intnl, std::vector<RankTwoTensor> & df_dstress) const
     163             : {
     164       20168 :   const Real sinphi = std::sin(phi(intnl));
     165       20168 :   df_dsig(stress, sinphi, df_dstress);
     166       20168 : }
     167             : 
     168             : void
     169       18936 : TensorMechanicsPlasticMohrCoulombMulti::dyieldFunction_dintnlV(const RankTwoTensor & stress,
     170             :                                                                Real intnl,
     171             :                                                                std::vector<Real> & df_dintnl) const
     172             : {
     173             :   std::vector<Real> eigvals;
     174       18936 :   stress.symmetricEigenvalues(eigvals);
     175       18936 :   eigvals[0] += _shift;
     176       18936 :   eigvals[2] -= _shift;
     177             : 
     178       18936 :   const Real sin_angle = std::sin(phi(intnl));
     179       18936 :   const Real cos_angle = std::cos(phi(intnl));
     180       18936 :   const Real dsin_angle = cos_angle * dphi(intnl);
     181       18936 :   const Real dcos_angle = -sin_angle * dphi(intnl);
     182       18936 :   const Real dcohcos = dcohesion(intnl) * cos_angle + cohesion(intnl) * dcos_angle;
     183             : 
     184       18936 :   df_dintnl.resize(6);
     185       18936 :   df_dintnl[0] = df_dintnl[1] = 0.5 * (eigvals[0] + eigvals[1]) * dsin_angle - dcohcos;
     186       18936 :   df_dintnl[2] = df_dintnl[3] = 0.5 * (eigvals[0] + eigvals[2]) * dsin_angle - dcohcos;
     187       18936 :   df_dintnl[4] = df_dintnl[5] = 0.5 * (eigvals[1] + eigvals[2]) * dsin_angle - dcohcos;
     188       18936 : }
     189             : 
     190             : void
     191       35400 : TensorMechanicsPlasticMohrCoulombMulti::flowPotentialV(const RankTwoTensor & stress,
     192             :                                                        Real intnl,
     193             :                                                        std::vector<RankTwoTensor> & r) const
     194             : {
     195       35400 :   const Real sinpsi = std::sin(psi(intnl));
     196       35400 :   df_dsig(stress, sinpsi, r);
     197       35400 : }
     198             : 
     199             : void
     200       18936 : TensorMechanicsPlasticMohrCoulombMulti::dflowPotential_dstressV(
     201             :     const RankTwoTensor & stress, Real intnl, std::vector<RankFourTensor> & dr_dstress) const
     202             : {
     203             :   std::vector<RankFourTensor> d2eigvals;
     204       18936 :   stress.d2symmetricEigenvalues(d2eigvals);
     205             : 
     206       18936 :   const Real sinpsi = std::sin(psi(intnl));
     207             : 
     208       18936 :   dr_dstress.resize(6);
     209             :   dr_dstress[0] =
     210       56808 :       0.5 * (d2eigvals[0] - d2eigvals[1]) + 0.5 * (d2eigvals[0] + d2eigvals[1]) * sinpsi;
     211             :   dr_dstress[1] =
     212       56808 :       0.5 * (d2eigvals[1] - d2eigvals[0]) + 0.5 * (d2eigvals[0] + d2eigvals[1]) * sinpsi;
     213             :   dr_dstress[2] =
     214       56808 :       0.5 * (d2eigvals[0] - d2eigvals[2]) + 0.5 * (d2eigvals[0] + d2eigvals[2]) * sinpsi;
     215             :   dr_dstress[3] =
     216       56808 :       0.5 * (d2eigvals[2] - d2eigvals[0]) + 0.5 * (d2eigvals[0] + d2eigvals[2]) * sinpsi;
     217             :   dr_dstress[4] =
     218       56808 :       0.5 * (d2eigvals[1] - d2eigvals[2]) + 0.5 * (d2eigvals[1] + d2eigvals[2]) * sinpsi;
     219             :   dr_dstress[5] =
     220       56808 :       0.5 * (d2eigvals[2] - d2eigvals[1]) + 0.5 * (d2eigvals[1] + d2eigvals[2]) * sinpsi;
     221       18936 : }
     222             : 
     223             : void
     224       18936 : TensorMechanicsPlasticMohrCoulombMulti::dflowPotential_dintnlV(
     225             :     const RankTwoTensor & stress, Real intnl, std::vector<RankTwoTensor> & dr_dintnl) const
     226             : {
     227       18936 :   const Real cos_angle = std::cos(psi(intnl));
     228       18936 :   const Real dsin_angle = cos_angle * dpsi(intnl);
     229             : 
     230             :   std::vector<Real> eigvals;
     231             :   std::vector<RankTwoTensor> deigvals;
     232       18936 :   stress.dsymmetricEigenvalues(eigvals, deigvals);
     233             : 
     234       18936 :   if (eigvals[0] > eigvals[1] - 0.1 * _shift || eigvals[1] > eigvals[2] - 0.1 * _shift)
     235        2544 :     perturbStress(stress, eigvals, deigvals);
     236             : 
     237       18936 :   dr_dintnl.resize(6);
     238       18936 :   dr_dintnl[0] = dr_dintnl[1] = 0.5 * (deigvals[0] + deigvals[1]) * dsin_angle;
     239       18936 :   dr_dintnl[2] = dr_dintnl[3] = 0.5 * (deigvals[0] + deigvals[2]) * dsin_angle;
     240       18936 :   dr_dintnl[4] = dr_dintnl[5] = 0.5 * (deigvals[1] + deigvals[2]) * dsin_angle;
     241       18936 : }
     242             : 
     243             : void
     244        6176 : TensorMechanicsPlasticMohrCoulombMulti::activeConstraints(const std::vector<Real> & f,
     245             :                                                           const RankTwoTensor & stress,
     246             :                                                           Real intnl,
     247             :                                                           const RankFourTensor & Eijkl,
     248             :                                                           std::vector<bool> & act,
     249             :                                                           RankTwoTensor & returned_stress) const
     250             : {
     251             :   act.assign(6, false);
     252             : 
     253        6176 :   if (f[0] <= _f_tol && f[1] <= _f_tol && f[2] <= _f_tol && f[3] <= _f_tol && f[4] <= _f_tol &&
     254           0 :       f[5] <= _f_tol)
     255             :   {
     256           0 :     returned_stress = stress;
     257           0 :     return;
     258             :   }
     259             : 
     260             :   Real returned_intnl;
     261        6176 :   std::vector<Real> dpm(6);
     262        6176 :   RankTwoTensor delta_dp;
     263        6176 :   std::vector<Real> yf(6);
     264             :   bool trial_stress_inadmissible;
     265        6176 :   doReturnMap(stress,
     266             :               intnl,
     267             :               Eijkl,
     268             :               0.0,
     269             :               returned_stress,
     270             :               returned_intnl,
     271             :               dpm,
     272             :               delta_dp,
     273             :               yf,
     274             :               trial_stress_inadmissible);
     275             : 
     276       43232 :   for (unsigned i = 0; i < 6; ++i)
     277       37056 :     act[i] = (dpm[i] > 0);
     278             : }
     279             : 
     280             : Real
     281      147720 : TensorMechanicsPlasticMohrCoulombMulti::cohesion(const Real internal_param) const
     282             : {
     283      147720 :   return _cohesion.value(internal_param);
     284             : }
     285             : 
     286             : Real
     287       47272 : TensorMechanicsPlasticMohrCoulombMulti::dcohesion(const Real internal_param) const
     288             : {
     289       47272 :   return _cohesion.derivative(internal_param);
     290             : }
     291             : 
     292             : Real
     293      315608 : TensorMechanicsPlasticMohrCoulombMulti::phi(const Real internal_param) const
     294             : {
     295      315608 :   return _phi.value(internal_param);
     296             : }
     297             : 
     298             : Real
     299       66208 : TensorMechanicsPlasticMohrCoulombMulti::dphi(const Real internal_param) const
     300             : {
     301       66208 :   return _phi.derivative(internal_param);
     302             : }
     303             : 
     304             : Real
     305       83976 : TensorMechanicsPlasticMohrCoulombMulti::psi(const Real internal_param) const
     306             : {
     307       83976 :   return _psi.value(internal_param);
     308             : }
     309             : 
     310             : Real
     311       18936 : TensorMechanicsPlasticMohrCoulombMulti::dpsi(const Real internal_param) const
     312             : {
     313       18936 :   return _psi.derivative(internal_param);
     314             : }
     315             : 
     316             : std::string
     317          24 : TensorMechanicsPlasticMohrCoulombMulti::modelName() const
     318             : {
     319          24 :   return "MohrCoulombMulti";
     320             : }
     321             : 
     322             : bool
     323       13152 : TensorMechanicsPlasticMohrCoulombMulti::returnMap(const RankTwoTensor & trial_stress,
     324             :                                                   Real intnl_old,
     325             :                                                   const RankFourTensor & E_ijkl,
     326             :                                                   Real ep_plastic_tolerance,
     327             :                                                   RankTwoTensor & returned_stress,
     328             :                                                   Real & returned_intnl,
     329             :                                                   std::vector<Real> & dpm,
     330             :                                                   RankTwoTensor & delta_dp,
     331             :                                                   std::vector<Real> & yf,
     332             :                                                   bool & trial_stress_inadmissible) const
     333             : {
     334       13152 :   if (!_use_custom_returnMap)
     335        5376 :     return TensorMechanicsPlasticModel::returnMap(trial_stress,
     336             :                                                   intnl_old,
     337             :                                                   E_ijkl,
     338             :                                                   ep_plastic_tolerance,
     339             :                                                   returned_stress,
     340             :                                                   returned_intnl,
     341             :                                                   dpm,
     342             :                                                   delta_dp,
     343             :                                                   yf,
     344        5376 :                                                   trial_stress_inadmissible);
     345             : 
     346        7776 :   return doReturnMap(trial_stress,
     347             :                      intnl_old,
     348             :                      E_ijkl,
     349             :                      ep_plastic_tolerance,
     350             :                      returned_stress,
     351             :                      returned_intnl,
     352             :                      dpm,
     353             :                      delta_dp,
     354             :                      yf,
     355        7776 :                      trial_stress_inadmissible);
     356             : }
     357             : 
     358             : bool
     359       13952 : TensorMechanicsPlasticMohrCoulombMulti::doReturnMap(const RankTwoTensor & trial_stress,
     360             :                                                     Real intnl_old,
     361             :                                                     const RankFourTensor & E_ijkl,
     362             :                                                     Real ep_plastic_tolerance,
     363             :                                                     RankTwoTensor & returned_stress,
     364             :                                                     Real & returned_intnl,
     365             :                                                     std::vector<Real> & dpm,
     366             :                                                     RankTwoTensor & delta_dp,
     367             :                                                     std::vector<Real> & yf,
     368             :                                                     bool & trial_stress_inadmissible) const
     369             : {
     370             :   mooseAssert(dpm.size() == 6,
     371             :               "TensorMechanicsPlasticMohrCoulombMulti size of dpm should be 6 but it is "
     372             :                   << dpm.size());
     373             : 
     374             :   std::vector<Real> eigvals;
     375       13952 :   RankTwoTensor eigvecs;
     376       13952 :   trial_stress.symmetricEigenvaluesEigenvectors(eigvals, eigvecs);
     377       13952 :   eigvals[0] += _shift;
     378       13952 :   eigvals[2] -= _shift;
     379             : 
     380       13952 :   Real sinphi = std::sin(phi(intnl_old));
     381       13952 :   Real cosphi = std::cos(phi(intnl_old));
     382       13952 :   Real coh = cohesion(intnl_old);
     383       13952 :   Real cohcos = coh * cosphi;
     384             : 
     385       13952 :   yieldFunctionEigvals(eigvals[0], eigvals[1], eigvals[2], sinphi, cohcos, yf);
     386             : 
     387       13952 :   if (yf[0] <= _f_tol && yf[1] <= _f_tol && yf[2] <= _f_tol && yf[3] <= _f_tol && yf[4] <= _f_tol &&
     388        3248 :       yf[5] <= _f_tol)
     389             :   {
     390             :     // purely elastic (trial_stress, intnl_old)
     391        3248 :     trial_stress_inadmissible = false;
     392        3248 :     return true;
     393             :   }
     394             : 
     395       10704 :   trial_stress_inadmissible = true;
     396             :   delta_dp.zero();
     397       10704 :   returned_stress = RankTwoTensor();
     398             : 
     399             :   // these are the normals to the 6 yield surfaces, which are const because of the assumption of no
     400             :   // psi hardening
     401       10704 :   std::vector<RealVectorValue> norm(6);
     402       10704 :   const Real sinpsi = std::sin(psi(intnl_old));
     403       10704 :   const Real oneminus = 0.5 * (1 - sinpsi);
     404       10704 :   const Real oneplus = 0.5 * (1 + sinpsi);
     405       10704 :   norm[0](0) = oneplus;
     406       10704 :   norm[0](1) = -oneminus;
     407       10704 :   norm[0](2) = 0;
     408       10704 :   norm[1](0) = -oneminus;
     409       10704 :   norm[1](1) = oneplus;
     410       10704 :   norm[1](2) = 0;
     411       10704 :   norm[2](0) = oneplus;
     412       10704 :   norm[2](1) = 0;
     413       10704 :   norm[2](2) = -oneminus;
     414       10704 :   norm[3](0) = -oneminus;
     415       10704 :   norm[3](1) = 0;
     416       10704 :   norm[3](2) = oneplus;
     417       10704 :   norm[4](0) = 0;
     418       10704 :   norm[4](1) = oneplus;
     419       10704 :   norm[4](2) = -oneminus;
     420       10704 :   norm[5](0) = 0;
     421       10704 :   norm[5](1) = -oneminus;
     422       10704 :   norm[5](2) = oneplus;
     423             : 
     424             :   // the flow directions are these norm multiplied by Eijkl.
     425             :   // I call the flow directions "n".
     426             :   // In the following I assume that the Eijkl is
     427             :   // for an isotropic situation.  Then I don't have to
     428             :   // rotate to the principal-stress frame, and i don't
     429             :   // have to worry about strange off-diagonal things
     430       10704 :   std::vector<RealVectorValue> n(6);
     431       74928 :   for (unsigned ys = 0; ys < 6; ++ys)
     432      256896 :     for (unsigned i = 0; i < 3; ++i)
     433      770688 :       for (unsigned j = 0; j < 3; ++j)
     434      578016 :         n[ys](i) += E_ijkl(i, i, j, j) * norm[ys](j);
     435       10704 :   const Real mag_E = E_ijkl(0, 0, 0, 0);
     436             : 
     437             :   // With non-zero Poisson's ratio and hardening
     438             :   // it is not computationally cheap to know whether
     439             :   // the trial stress will return to the tip, edge,
     440             :   // or plane.  The following at least
     441             :   // gives a not-completely-stupid guess
     442             :   // trial_order[0] = type of return to try first
     443             :   // trial_order[1] = type of return to try second
     444             :   // trial_order[2] = type of return to try third
     445             :   // trial_order[3] = type of return to try fourth
     446             :   // trial_order[4] = type of return to try fifth
     447             :   // In the following the "binary" stuff indicates the
     448             :   // deactive (0) and active (1) surfaces, eg
     449             :   // 110100 means that surfaces 0, 1 and 3 are active
     450             :   // and 2, 4 and 5 are deactive
     451             :   const unsigned int number_of_return_paths = 5;
     452       10704 :   std::vector<int> trial_order(number_of_return_paths);
     453       10704 :   if (yf[1] > _f_tol && yf[3] > _f_tol && yf[5] > _f_tol)
     454             :   {
     455        6048 :     trial_order[0] = tip110100;
     456        6048 :     trial_order[1] = edge010100;
     457        6048 :     trial_order[2] = plane000100;
     458        6048 :     trial_order[3] = edge000101;
     459        6048 :     trial_order[4] = tip010101;
     460             :   }
     461        4656 :   else if (yf[1] <= _f_tol && yf[3] > _f_tol && yf[5] > _f_tol)
     462             :   {
     463        3456 :     trial_order[0] = edge000101;
     464        3456 :     trial_order[1] = plane000100;
     465        3456 :     trial_order[2] = tip110100;
     466        3456 :     trial_order[3] = tip010101;
     467        3456 :     trial_order[4] = edge010100;
     468             :   }
     469        1200 :   else if (yf[1] <= _f_tol && yf[3] > _f_tol && yf[5] <= _f_tol)
     470             :   {
     471         800 :     trial_order[0] = plane000100;
     472         800 :     trial_order[1] = edge000101;
     473         800 :     trial_order[2] = edge010100;
     474         800 :     trial_order[3] = tip110100;
     475         800 :     trial_order[4] = tip010101;
     476             :   }
     477             :   else
     478             :   {
     479         400 :     trial_order[0] = edge010100;
     480         400 :     trial_order[1] = plane000100;
     481         400 :     trial_order[2] = edge000101;
     482         400 :     trial_order[3] = tip110100;
     483         400 :     trial_order[4] = tip010101;
     484             :   }
     485             : 
     486             :   unsigned trial;
     487       10704 :   bool nr_converged = false;
     488             :   bool kt_success = false;
     489       10704 :   std::vector<RealVectorValue> ntip(3);
     490       10704 :   std::vector<Real> dpmtip(3);
     491             : 
     492       18088 :   for (trial = 0; trial < number_of_return_paths; ++trial)
     493             :   {
     494       18088 :     switch (trial_order[trial])
     495             :     {
     496             :       case tip110100:
     497       24992 :         for (unsigned int i = 0; i < 3; ++i)
     498             :         {
     499       18744 :           ntip[0](i) = n[0](i);
     500       18744 :           ntip[1](i) = n[1](i);
     501       18744 :           ntip[2](i) = n[3](i);
     502             :         }
     503        6248 :         kt_success = returnTip(eigvals,
     504             :                                ntip,
     505             :                                dpmtip,
     506             :                                returned_stress,
     507             :                                intnl_old,
     508             :                                sinphi,
     509             :                                cohcos,
     510             :                                0,
     511             :                                nr_converged,
     512             :                                ep_plastic_tolerance,
     513             :                                yf);
     514        6248 :         if (nr_converged && kt_success)
     515             :         {
     516        3000 :           dpm[0] = dpmtip[0];
     517        3000 :           dpm[1] = dpmtip[1];
     518        3000 :           dpm[3] = dpmtip[2];
     519        3000 :           dpm[2] = dpm[4] = dpm[5] = 0;
     520             :         }
     521             :         break;
     522             : 
     523             :       case tip010101:
     524         448 :         for (unsigned int i = 0; i < 3; ++i)
     525             :         {
     526         336 :           ntip[0](i) = n[1](i);
     527         336 :           ntip[1](i) = n[3](i);
     528         336 :           ntip[2](i) = n[5](i);
     529             :         }
     530         112 :         kt_success = returnTip(eigvals,
     531             :                                ntip,
     532             :                                dpmtip,
     533             :                                returned_stress,
     534             :                                intnl_old,
     535             :                                sinphi,
     536             :                                cohcos,
     537             :                                0,
     538             :                                nr_converged,
     539             :                                ep_plastic_tolerance,
     540             :                                yf);
     541         112 :         if (nr_converged && kt_success)
     542             :         {
     543         112 :           dpm[1] = dpmtip[0];
     544         112 :           dpm[3] = dpmtip[1];
     545         112 :           dpm[5] = dpmtip[2];
     546         112 :           dpm[0] = dpm[2] = dpm[4] = 0;
     547             :         }
     548             :         break;
     549             : 
     550        3560 :       case edge000101:
     551        3560 :         kt_success = returnEdge000101(eigvals,
     552             :                                       n,
     553             :                                       dpm,
     554             :                                       returned_stress,
     555             :                                       intnl_old,
     556             :                                       sinphi,
     557             :                                       cohcos,
     558             :                                       0,
     559             :                                       mag_E,
     560             :                                       nr_converged,
     561             :                                       ep_plastic_tolerance,
     562             :                                       yf);
     563             :         break;
     564             : 
     565        3592 :       case edge010100:
     566        3592 :         kt_success = returnEdge010100(eigvals,
     567             :                                       n,
     568             :                                       dpm,
     569             :                                       returned_stress,
     570             :                                       intnl_old,
     571             :                                       sinphi,
     572             :                                       cohcos,
     573             :                                       0,
     574             :                                       mag_E,
     575             :                                       nr_converged,
     576             :                                       ep_plastic_tolerance,
     577             :                                       yf);
     578             :         break;
     579             : 
     580        4576 :       case plane000100:
     581        4576 :         kt_success = returnPlane(eigvals,
     582             :                                  n,
     583             :                                  dpm,
     584             :                                  returned_stress,
     585             :                                  intnl_old,
     586             :                                  sinphi,
     587             :                                  cohcos,
     588             :                                  0,
     589             :                                  nr_converged,
     590             :                                  ep_plastic_tolerance,
     591             :                                  yf);
     592             :         break;
     593             :     }
     594             : 
     595       18088 :     if (nr_converged && kt_success)
     596             :       break;
     597             :   }
     598             : 
     599       10704 :   if (trial == number_of_return_paths)
     600             :   {
     601           0 :     sinphi = std::sin(phi(intnl_old));
     602           0 :     cosphi = std::cos(phi(intnl_old));
     603           0 :     coh = cohesion(intnl_old);
     604           0 :     cohcos = coh * cosphi;
     605           0 :     yieldFunctionEigvals(eigvals[0], eigvals[1], eigvals[2], sinphi, cohcos, yf);
     606             :     Moose::err << "Trial stress = \n";
     607           0 :     trial_stress.print(Moose::err);
     608             :     Moose::err << "which has eigenvalues = " << eigvals[0] << " " << eigvals[1] << " " << eigvals[2]
     609             :                << "\n";
     610             :     Moose::err << "and yield functions = " << yf[0] << " " << yf[1] << " " << yf[2] << " " << yf[3]
     611             :                << " " << yf[4] << " " << yf[5] << "\n";
     612             :     Moose::err << "Internal parameter = " << intnl_old << std::endl;
     613           0 :     mooseError("TensorMechanicsPlasticMohrCoulombMulti: FAILURE!  You probably need to implement a "
     614             :                "line search if your hardening is too severe, or you need to tune your tolerances "
     615             :                "(eg, yield_function_tolerance should be a little smaller than (young "
     616             :                "modulus)*ep_plastic_tolerance).\n");
     617             :     return false;
     618             :   }
     619             : 
     620             :   // success
     621             : 
     622       10704 :   returned_intnl = intnl_old;
     623       74928 :   for (unsigned i = 0; i < 6; ++i)
     624       64224 :     returned_intnl += dpm[i];
     625       74928 :   for (unsigned i = 0; i < 6; ++i)
     626      256896 :     for (unsigned j = 0; j < 3; ++j)
     627      192672 :       delta_dp(j, j) += dpm[i] * norm[i](j);
     628       10704 :   returned_stress = eigvecs * returned_stress * (eigvecs.transpose());
     629       10704 :   delta_dp = eigvecs * delta_dp * (eigvecs.transpose());
     630             :   return true;
     631             : }
     632             : 
     633             : bool
     634        6360 : TensorMechanicsPlasticMohrCoulombMulti::returnTip(const std::vector<Real> & eigvals,
     635             :                                                   const std::vector<RealVectorValue> & n,
     636             :                                                   std::vector<Real> & dpm,
     637             :                                                   RankTwoTensor & returned_stress,
     638             :                                                   Real intnl_old,
     639             :                                                   Real & sinphi,
     640             :                                                   Real & cohcos,
     641             :                                                   Real initial_guess,
     642             :                                                   bool & nr_converged,
     643             :                                                   Real ep_plastic_tolerance,
     644             :                                                   std::vector<Real> & yf) const
     645             : {
     646             :   // This returns to the Mohr-Coulomb tip using the THREE directions
     647             :   // given in n, and yields the THREE dpm values.  Note that you
     648             :   // must supply THREE suitable n vectors out of the total of SIX
     649             :   // flow directions, and then interpret the THREE dpm values appropriately.
     650             :   //
     651             :   // Eg1.  You supply the flow directions n[0], n[1] and n[3] as
     652             :   //       the "n" vectors.  This is return-to-the-tip via 110100.
     653             :   //       Then the three returned dpm values will be dpm[0], dpm[1] and dpm[3].
     654             : 
     655             :   // Eg2.  You supply the flow directions n[1], n[3] and n[5] as
     656             :   //       the "n" vectors.  This is return-to-the-tip via 010101.
     657             :   //       Then the three returned dpm values will be dpm[1], dpm[3] and dpm[5].
     658             : 
     659             :   // The returned point is defined by the three yield functions (corresonding
     660             :   // to the three supplied flow directions) all being zero.
     661             :   // that is, returned_stress = diag(cohcot, cohcot, cohcot), where
     662             :   // cohcot = cohesion*cosphi/sinphi
     663             :   // where intnl = intnl_old + dpm[0] + dpm[1] + dpm[2]
     664             :   // The 3 plastic multipliers, dpm, are defiend by the normality condition
     665             :   //     eigvals - cohcot = dpm[0]*n[0] + dpm[1]*n[1] + dpm[2]*n[2]
     666             :   // (Kuhn-Tucker demands that all dpm are non-negative, but we leave
     667             :   //  that checking for the end.)
     668             :   // (Remember that these "n" vectors and "dpm" values must be interpreted
     669             :   //  differently depending on what you pass into this function.)
     670             :   // This is a vector equation with solution (A):
     671             :   //   dpm[0] = triple(eigvals - cohcot, n[1], n[2])/trip;
     672             :   //   dpm[1] = triple(eigvals - cohcot, n[2], n[0])/trip;
     673             :   //   dpm[2] = triple(eigvals - cohcot, n[0], n[1])/trip;
     674             :   // where trip = triple(n[0], n[1], n[2]).
     675             :   // By adding the three components of that solution together
     676             :   // we can get an equation for x = dpm[0] + dpm[1] + dpm[2],
     677             :   // and then our Newton-Raphson only involves one variable (x).
     678             :   // In the following, i specialise to the isotropic situation.
     679             : 
     680             :   mooseAssert(n.size() == 3,
     681             :               "TensorMechanicsPlasticMohrCoulombMulti: Custom tip-return algorithm must be "
     682             :               "supplied with n of size 3, whereas yours is "
     683             :                   << n.size());
     684             :   mooseAssert(dpm.size() == 3,
     685             :               "TensorMechanicsPlasticMohrCoulombMulti: Custom tip-return algorithm must be "
     686             :               "supplied with dpm of size 3, whereas yours is "
     687             :                   << dpm.size());
     688             :   mooseAssert(yf.size() == 6,
     689             :               "TensorMechanicsPlasticMohrCoulombMulti: Custom tip-return algorithm must be "
     690             :               "supplied with yf of size 6, whereas yours is "
     691             :                   << yf.size());
     692             : 
     693             :   Real x = initial_guess;
     694        6360 :   const Real trip = triple_product(n[0], n[1], n[2]);
     695        6360 :   sinphi = std::sin(phi(intnl_old + x));
     696        6360 :   Real cosphi = std::cos(phi(intnl_old + x));
     697        6360 :   Real coh = cohesion(intnl_old + x);
     698        6360 :   cohcos = coh * cosphi;
     699        6360 :   Real cohcot = cohcos / sinphi;
     700             : 
     701       10184 :   if (_cohesion.modelName().compare("Constant") != 0 || _phi.modelName().compare("Constant") != 0)
     702             :   {
     703             :     // Finding x is expensive.  Therefore
     704             :     // although x!=0 for Constant Hardening, solution (A)
     705             :     // demonstrates that we don't
     706             :     // actually need to know x to find the dpm for
     707             :     // Constant Hardening.
     708             :     //
     709             :     // However, for nontrivial Hardening, the following
     710             :     // is necessary
     711             :     // cohcot_coeff = [1,1,1].(Cross[n[1], n[2]] + Cross[n[2], n[0]] + Cross[n[0], n[1]])/trip
     712             :     Real cohcot_coeff =
     713        3016 :         (n[0](0) * (n[1](1) - n[1](2) - n[2](1)) + (n[1](2) - n[1](1)) * n[2](0) +
     714        3016 :          (n[1](0) - n[1](2)) * n[2](1) + n[0](2) * (n[1](0) - n[1](1) - n[2](0) + n[2](1)) +
     715        3016 :          n[0](1) * (n[1](2) - n[1](0) + n[2](0) - n[2](2)) +
     716        3016 :          (n[0](0) - n[1](0) + n[1](1)) * n[2](2)) /
     717        3016 :         trip;
     718             :     // eig_term = eigvals.(Cross[n[1], n[2]] + Cross[n[2], n[0]] + Cross[n[0], n[1]])/trip
     719        3016 :     Real eig_term = eigvals[0] *
     720        3016 :                     (-n[0](2) * n[1](1) + n[0](1) * n[1](2) + n[0](2) * n[2](1) -
     721        3016 :                      n[1](2) * n[2](1) - n[0](1) * n[2](2) + n[1](1) * n[2](2)) /
     722        3016 :                     trip;
     723        3016 :     eig_term += eigvals[1] *
     724        3016 :                 (n[0](2) * n[1](0) - n[0](0) * n[1](2) - n[0](2) * n[2](0) + n[1](2) * n[2](0) +
     725        3016 :                  n[0](0) * n[2](2) - n[1](0) * n[2](2)) /
     726             :                 trip;
     727        3016 :     eig_term += eigvals[2] *
     728        3016 :                 (n[0](0) * n[1](1) - n[1](1) * n[2](0) + n[0](1) * n[2](0) - n[0](1) * n[1](0) -
     729        3016 :                  n[0](0) * n[2](1) + n[1](0) * n[2](1)) /
     730             :                 trip;
     731             :     // and finally, the equation we want to solve is:
     732             :     // x - eig_term + cohcot*cohcot_coeff = 0
     733             :     // but i divide by cohcot_coeff so the result has the units of
     734             :     // stress, so using _f_tol as a convergence check is reasonable
     735        3016 :     eig_term /= cohcot_coeff;
     736        3016 :     Real residual = x / cohcot_coeff - eig_term + cohcot;
     737             :     Real jacobian;
     738             :     Real deriv_phi;
     739             :     Real deriv_coh;
     740             :     unsigned int iter = 0;
     741             :     do
     742             :     {
     743        6104 :       deriv_phi = dphi(intnl_old + x);
     744        6104 :       deriv_coh = dcohesion(intnl_old + x);
     745        6104 :       jacobian = 1.0 / cohcot_coeff + deriv_coh * cosphi / sinphi -
     746        6104 :                  coh * deriv_phi / Utility::pow<2>(sinphi);
     747        6104 :       x += -residual / jacobian;
     748             : 
     749        6104 :       if (iter > _max_iters) // not converging
     750             :       {
     751           0 :         nr_converged = false;
     752           0 :         return false;
     753             :       }
     754             : 
     755        6104 :       sinphi = std::sin(phi(intnl_old + x));
     756        6104 :       cosphi = std::cos(phi(intnl_old + x));
     757        6104 :       coh = cohesion(intnl_old + x);
     758        6104 :       cohcos = coh * cosphi;
     759        6104 :       cohcot = cohcos / sinphi;
     760        6104 :       residual = x / cohcot_coeff - eig_term + cohcot;
     761        6104 :       iter++;
     762        6104 :     } while (residual * residual > _f_tol * _f_tol / 100);
     763             :   }
     764             : 
     765             :   // so the NR process converged, but we must
     766             :   // calculate the individual dpm values and
     767             :   // check Kuhn-Tucker
     768        6360 :   nr_converged = true;
     769        6360 :   if (x < -3 * ep_plastic_tolerance)
     770             :     // obviously at least one of the dpm are < -ep_plastic_tolerance.  No point in proceeding.  This
     771             :     // is a potential weak-point: if the user has set _f_tol quite large, and ep_plastic_tolerance
     772             :     // quite small, the above NR process will quickly converge, but the solution may be wrong and
     773             :     // violate Kuhn-Tucker.
     774             :     return false;
     775             : 
     776             :   // The following is the solution (A) written above
     777             :   // (dpm[0] = triple(eigvals - cohcot, n[1], n[2])/trip, etc)
     778             :   // in the isotropic situation
     779             :   RealVectorValue v;
     780        5664 :   v(0) = eigvals[0] - cohcot;
     781        5664 :   v(1) = eigvals[1] - cohcot;
     782        5664 :   v(2) = eigvals[2] - cohcot;
     783        5664 :   dpm[0] = triple_product(v, n[1], n[2]) / trip;
     784        5664 :   dpm[1] = triple_product(v, n[2], n[0]) / trip;
     785        5664 :   dpm[2] = triple_product(v, n[0], n[1]) / trip;
     786             : 
     787        5664 :   if (dpm[0] < -ep_plastic_tolerance || dpm[1] < -ep_plastic_tolerance ||
     788             :       dpm[2] < -ep_plastic_tolerance)
     789             :     // Kuhn-Tucker failure.  No point in proceeding
     790             :     return false;
     791             : 
     792             :   // Kuhn-Tucker has succeeded: just need returned_stress and yf values
     793             :   // I do not use the dpm to calculate returned_stress, because that
     794             :   // might add error (and computational effort), simply:
     795        3112 :   returned_stress(0, 0) = returned_stress(1, 1) = returned_stress(2, 2) = cohcot;
     796             :   // So by construction the yield functions are all zero
     797        3112 :   yf[0] = yf[1] = yf[2] = yf[3] = yf[4] = yf[5] = 0;
     798        3112 :   return true;
     799             : }
     800             : 
     801             : bool
     802        4576 : TensorMechanicsPlasticMohrCoulombMulti::returnPlane(const std::vector<Real> & eigvals,
     803             :                                                     const std::vector<RealVectorValue> & n,
     804             :                                                     std::vector<Real> & dpm,
     805             :                                                     RankTwoTensor & returned_stress,
     806             :                                                     Real intnl_old,
     807             :                                                     Real & sinphi,
     808             :                                                     Real & cohcos,
     809             :                                                     Real initial_guess,
     810             :                                                     bool & nr_converged,
     811             :                                                     Real ep_plastic_tolerance,
     812             :                                                     std::vector<Real> & yf) const
     813             : {
     814             :   // This returns to the Mohr-Coulomb plane using n[3] (ie 000100)
     815             :   //
     816             :   // The returned point is defined by the f[3]=0 and
     817             :   //    a = eigvals - dpm[3]*n[3]
     818             :   // where "a" is the returned point and dpm[3] is the plastic multiplier.
     819             :   // This equation is a vector equation in principal stress space.
     820             :   // (Kuhn-Tucker also demands that dpm[3]>=0, but we leave checking
     821             :   // that condition for the end.)
     822             :   // Since f[3]=0, we must have
     823             :   // a[2]*(1+sinphi) + a[0]*(-1+sinphi) - 2*coh*cosphi = 0
     824             :   // which gives dpm[3] as the solution of
     825             :   //     alpha*dpm[3] + eigvals[2] - eigvals[0] + beta*sinphi - 2*coh*cosphi = 0
     826             :   // with alpha = n[3](0) - n[3](2) - (n[3](2) + n[3](0))*sinphi
     827             :   //      beta = eigvals[2] + eigvals[0]
     828             : 
     829             :   mooseAssert(n.size() == 6,
     830             :               "TensorMechanicsPlasticMohrCoulombMulti: Custom plane-return algorithm must be "
     831             :               "supplied with n of size 6, whereas yours is "
     832             :                   << n.size());
     833             :   mooseAssert(dpm.size() == 6,
     834             :               "TensorMechanicsPlasticMohrCoulombMulti: Custom plane-return algorithm must be "
     835             :               "supplied with dpm of size 6, whereas yours is "
     836             :                   << dpm.size());
     837             :   mooseAssert(yf.size() == 6,
     838             :               "TensorMechanicsPlasticMohrCoulombMulti: Custom tip-return algorithm must be "
     839             :               "supplied with yf of size 6, whereas yours is "
     840             :                   << yf.size());
     841             : 
     842        4576 :   dpm[3] = initial_guess;
     843        4576 :   sinphi = std::sin(phi(intnl_old + dpm[3]));
     844        4576 :   Real cosphi = std::cos(phi(intnl_old + dpm[3]));
     845        4576 :   Real coh = cohesion(intnl_old + dpm[3]);
     846        4576 :   cohcos = coh * cosphi;
     847             : 
     848        4576 :   Real alpha = n[3](0) - n[3](2) - (n[3](2) + n[3](0)) * sinphi;
     849             :   Real deriv_phi;
     850             :   Real dalpha;
     851        4576 :   const Real beta = eigvals[2] + eigvals[0];
     852             :   Real deriv_coh;
     853             : 
     854             :   Real residual =
     855        4576 :       alpha * dpm[3] + eigvals[2] - eigvals[0] + beta * sinphi - 2.0 * cohcos; // this is 2*yf[3]
     856             :   Real jacobian;
     857             : 
     858             :   const Real f_tol2 = Utility::pow<2>(_f_tol);
     859             :   unsigned int iter = 0;
     860             :   do
     861             :   {
     862        7832 :     deriv_phi = dphi(intnl_old + dpm[3]);
     863        7832 :     dalpha = -(n[3](2) + n[3](0)) * cosphi * deriv_phi;
     864        7832 :     deriv_coh = dcohesion(intnl_old + dpm[3]);
     865        7832 :     jacobian = alpha + dalpha * dpm[3] + beta * cosphi * deriv_phi - 2.0 * deriv_coh * cosphi +
     866        7832 :                2.0 * coh * sinphi * deriv_phi;
     867             : 
     868        7832 :     dpm[3] -= residual / jacobian;
     869        7832 :     if (iter > _max_iters) // not converging
     870             :     {
     871           0 :       nr_converged = false;
     872           0 :       return false;
     873             :     }
     874             : 
     875        7832 :     sinphi = std::sin(phi(intnl_old + dpm[3]));
     876        7832 :     cosphi = std::cos(phi(intnl_old + dpm[3]));
     877        7832 :     coh = cohesion(intnl_old + dpm[3]);
     878        7832 :     cohcos = coh * cosphi;
     879        7832 :     alpha = n[3](0) - n[3](2) - (n[3](2) + n[3](0)) * sinphi;
     880        7832 :     residual = alpha * dpm[3] + eigvals[2] - eigvals[0] + beta * sinphi - 2.0 * cohcos;
     881        7832 :     iter++;
     882        7832 :   } while (residual * residual > f_tol2);
     883             : 
     884             :   // so the NR process converged, but we must
     885             :   // check Kuhn-Tucker
     886        4576 :   nr_converged = true;
     887        4576 :   if (dpm[3] < -ep_plastic_tolerance)
     888             :     // Kuhn-Tucker failure
     889             :     return false;
     890             : 
     891       18304 :   for (unsigned i = 0; i < 3; ++i)
     892       13728 :     returned_stress(i, i) = eigvals[i] - dpm[3] * n[3](i);
     893        4576 :   yieldFunctionEigvals(
     894             :       returned_stress(0, 0), returned_stress(1, 1), returned_stress(2, 2), sinphi, cohcos, yf);
     895             : 
     896             :   // by construction abs(yf[3]) = abs(residual/2) < _f_tol/2
     897        4576 :   if (yf[0] > _f_tol || yf[1] > _f_tol || yf[2] > _f_tol || yf[4] > _f_tol || yf[5] > _f_tol)
     898             :     // Kuhn-Tucker failure
     899             :     return false;
     900             : 
     901             :   // success!
     902        4272 :   dpm[0] = dpm[1] = dpm[2] = dpm[4] = dpm[5] = 0;
     903        4272 :   return true;
     904             : }
     905             : 
     906             : bool
     907        3560 : TensorMechanicsPlasticMohrCoulombMulti::returnEdge000101(const std::vector<Real> & eigvals,
     908             :                                                          const std::vector<RealVectorValue> & n,
     909             :                                                          std::vector<Real> & dpm,
     910             :                                                          RankTwoTensor & returned_stress,
     911             :                                                          Real intnl_old,
     912             :                                                          Real & sinphi,
     913             :                                                          Real & cohcos,
     914             :                                                          Real initial_guess,
     915             :                                                          Real mag_E,
     916             :                                                          bool & nr_converged,
     917             :                                                          Real ep_plastic_tolerance,
     918             :                                                          std::vector<Real> & yf) const
     919             : {
     920             :   // This returns to the Mohr-Coulomb edge
     921             :   // with 000101 being active.  This means that
     922             :   // f3=f5=0.  Denoting the returned stress by "a"
     923             :   // (in principal stress space), this means that
     924             :   // a0=a1 = (2Ccosphi + a2(1+sinphi))/(sinphi-1)
     925             :   //
     926             :   // Also, a = eigvals - dpm3*n3 - dpm5*n5,
     927             :   // where the dpm are the plastic multipliers
     928             :   // and the n are the flow directions.
     929             :   //
     930             :   // Hence we have 5 equations and 5 unknowns (a,
     931             :   // dpm3 and dpm5).  Eliminating all unknowns
     932             :   // except for x = dpm3+dpm5 gives the residual below
     933             :   // (I used mathematica)
     934             : 
     935             :   Real x = initial_guess;
     936        3560 :   sinphi = std::sin(phi(intnl_old + x));
     937        3560 :   Real cosphi = std::cos(phi(intnl_old + x));
     938        3560 :   Real coh = cohesion(intnl_old + x);
     939        3560 :   cohcos = coh * cosphi;
     940             : 
     941             :   const Real numer_const =
     942        3560 :       -n[3](2) * eigvals[0] - n[5](1) * eigvals[0] + n[5](2) * eigvals[0] + n[3](2) * eigvals[1] +
     943        3560 :       n[5](0) * eigvals[1] - n[5](2) * eigvals[1] - n[5](0) * eigvals[2] + n[5](1) * eigvals[2] +
     944        3560 :       n[3](0) * (-eigvals[1] + eigvals[2]) - n[3](1) * (-eigvals[0] + eigvals[2]);
     945        3560 :   const Real numer_coeff1 = 2 * (-n[3](0) + n[3](1) + n[5](0) - n[5](1));
     946             :   const Real numer_coeff2 =
     947        3560 :       n[5](2) * (eigvals[0] - eigvals[1]) + n[3](2) * (-eigvals[0] + eigvals[1]) +
     948        3560 :       n[5](1) * (eigvals[0] + eigvals[2]) + (n[3](0) - n[5](0)) * (eigvals[1] + eigvals[2]) -
     949        3560 :       n[3](1) * (eigvals[0] + eigvals[2]);
     950        3560 :   Real numer = numer_const + numer_coeff1 * cohcos + numer_coeff2 * sinphi;
     951        3560 :   const Real denom_const = -n[3](2) * (n[5](0) - n[5](1)) - n[3](1) * (-n[5](0) + n[5](2)) +
     952        3560 :                            n[3](0) * (-n[5](1) + n[5](2));
     953        3560 :   const Real denom_coeff = -n[3](2) * (n[5](0) - n[5](1)) - n[3](1) * (n[5](0) + n[5](2)) +
     954        3560 :                            n[3](0) * (n[5](1) + n[5](2));
     955        3560 :   Real denom = denom_const + denom_coeff * sinphi;
     956        3560 :   Real residual = -x + numer / denom;
     957             : 
     958             :   Real deriv_phi;
     959             :   Real deriv_coh;
     960             :   Real jacobian;
     961        3560 :   const Real tol = Utility::pow<2>(_f_tol / (mag_E * 10.0));
     962             :   unsigned int iter = 0;
     963             :   do
     964             :   {
     965             :     do
     966             :     {
     967        6240 :       deriv_phi = dphi(intnl_old + dpm[3]);
     968        6240 :       deriv_coh = dcohesion(intnl_old + dpm[3]);
     969        6240 :       jacobian = -1 +
     970        6240 :                  (numer_coeff1 * deriv_coh * cosphi - numer_coeff1 * coh * sinphi * deriv_phi +
     971        6240 :                   numer_coeff2 * cosphi * deriv_phi) /
     972             :                      denom -
     973        6240 :                  numer * denom_coeff * cosphi * deriv_phi / denom / denom;
     974        6240 :       x -= residual / jacobian;
     975        6240 :       if (iter > _max_iters) // not converging
     976             :       {
     977           0 :         nr_converged = false;
     978           0 :         return false;
     979             :       }
     980             : 
     981        6240 :       sinphi = std::sin(phi(intnl_old + x));
     982        6240 :       cosphi = std::cos(phi(intnl_old + x));
     983        6240 :       coh = cohesion(intnl_old + x);
     984        6240 :       cohcos = coh * cosphi;
     985        6240 :       numer = numer_const + numer_coeff1 * cohcos + numer_coeff2 * sinphi;
     986        6240 :       denom = denom_const + denom_coeff * sinphi;
     987        6240 :       residual = -x + numer / denom;
     988        6240 :       iter++;
     989        6240 :     } while (residual * residual > tol);
     990             : 
     991             :     // now must ensure that yf[3] and yf[5] are both "zero"
     992             :     const Real dpm3minusdpm5 =
     993        3560 :         (2.0 * (eigvals[0] - eigvals[1]) + x * (n[3](1) - n[3](0) + n[5](1) - n[5](0))) /
     994        3560 :         (n[3](0) - n[3](1) + n[5](1) - n[5](0));
     995        3560 :     dpm[3] = (x + dpm3minusdpm5) / 2.0;
     996        3560 :     dpm[5] = (x - dpm3minusdpm5) / 2.0;
     997             : 
     998       14240 :     for (unsigned i = 0; i < 3; ++i)
     999       10680 :       returned_stress(i, i) = eigvals[i] - dpm[3] * n[3](i) - dpm[5] * n[5](i);
    1000        3560 :     yieldFunctionEigvals(
    1001             :         returned_stress(0, 0), returned_stress(1, 1), returned_stress(2, 2), sinphi, cohcos, yf);
    1002        3560 :   } while (yf[3] * yf[3] > _f_tol * _f_tol && yf[5] * yf[5] > _f_tol * _f_tol);
    1003             : 
    1004             :   // so the NR process converged, but we must
    1005             :   // check Kuhn-Tucker
    1006        3560 :   nr_converged = true;
    1007             : 
    1008        3560 :   if (dpm[3] < -ep_plastic_tolerance || dpm[5] < -ep_plastic_tolerance)
    1009             :     // Kuhn-Tucker failure.    This is a potential weak-point: if the user has set _f_tol quite
    1010             :     // large, and ep_plastic_tolerance quite small, the above NR process will quickly converge, but
    1011             :     // the solution may be wrong and violate Kuhn-Tucker.
    1012             :     return false;
    1013             : 
    1014        1888 :   if (yf[0] > _f_tol || yf[1] > _f_tol || yf[2] > _f_tol || yf[4] > _f_tol)
    1015             :     // Kuhn-Tucker failure
    1016             :     return false;
    1017             : 
    1018             :   // success
    1019        1632 :   dpm[0] = dpm[1] = dpm[2] = dpm[4] = 0;
    1020        1632 :   return true;
    1021             : }
    1022             : 
    1023             : bool
    1024        3592 : TensorMechanicsPlasticMohrCoulombMulti::returnEdge010100(const std::vector<Real> & eigvals,
    1025             :                                                          const std::vector<RealVectorValue> & n,
    1026             :                                                          std::vector<Real> & dpm,
    1027             :                                                          RankTwoTensor & returned_stress,
    1028             :                                                          Real intnl_old,
    1029             :                                                          Real & sinphi,
    1030             :                                                          Real & cohcos,
    1031             :                                                          Real initial_guess,
    1032             :                                                          Real mag_E,
    1033             :                                                          bool & nr_converged,
    1034             :                                                          Real ep_plastic_tolerance,
    1035             :                                                          std::vector<Real> & yf) const
    1036             : {
    1037             :   // This returns to the Mohr-Coulomb edge
    1038             :   // with 010100 being active.  This means that
    1039             :   // f1=f3=0.  Denoting the returned stress by "a"
    1040             :   // (in principal stress space), this means that
    1041             :   // a1=a2 = (2Ccosphi + a0(1-sinphi))/(sinphi+1)
    1042             :   //
    1043             :   // Also, a = eigvals - dpm1*n1 - dpm3*n3,
    1044             :   // where the dpm are the plastic multipliers
    1045             :   // and the n are the flow directions.
    1046             :   //
    1047             :   // Hence we have 5 equations and 5 unknowns (a,
    1048             :   // dpm1 and dpm3).  Eliminating all unknowns
    1049             :   // except for x = dpm1+dpm3 gives the residual below
    1050             :   // (I used mathematica)
    1051             : 
    1052             :   Real x = initial_guess;
    1053        3592 :   sinphi = std::sin(phi(intnl_old + x));
    1054        3592 :   Real cosphi = std::cos(phi(intnl_old + x));
    1055        3592 :   Real coh = cohesion(intnl_old + x);
    1056        3592 :   cohcos = coh * cosphi;
    1057             : 
    1058        3592 :   const Real numer_const = -n[1](2) * eigvals[0] - n[3](1) * eigvals[0] + n[3](2) * eigvals[0] -
    1059        3592 :                            n[1](0) * eigvals[1] + n[1](2) * eigvals[1] + n[3](0) * eigvals[1] -
    1060        3592 :                            n[3](2) * eigvals[1] + n[1](0) * eigvals[2] - n[3](0) * eigvals[2] +
    1061        3592 :                            n[3](1) * eigvals[2] - n[1](1) * (-eigvals[0] + eigvals[2]);
    1062        3592 :   const Real numer_coeff1 = 2 * (n[1](1) - n[1](2) - n[3](1) + n[3](2));
    1063             :   const Real numer_coeff2 =
    1064        3592 :       n[3](2) * (-eigvals[0] - eigvals[1]) + n[1](2) * (eigvals[0] + eigvals[1]) +
    1065        3592 :       n[3](1) * (eigvals[0] + eigvals[2]) + (n[1](0) - n[3](0)) * (eigvals[1] - eigvals[2]) -
    1066        3592 :       n[1](1) * (eigvals[0] + eigvals[2]);
    1067        3592 :   Real numer = numer_const + numer_coeff1 * cohcos + numer_coeff2 * sinphi;
    1068        3592 :   const Real denom_const = -n[1](0) * (n[3](1) - n[3](2)) + n[1](2) * (-n[3](0) + n[3](1)) +
    1069        3592 :                            n[1](1) * (-n[3](2) + n[3](0));
    1070             :   const Real denom_coeff =
    1071        3592 :       n[1](0) * (n[3](1) - n[3](2)) + n[1](2) * (n[3](0) + n[3](1)) - n[1](1) * (n[3](0) + n[3](2));
    1072        3592 :   Real denom = denom_const + denom_coeff * sinphi;
    1073        3592 :   Real residual = -x + numer / denom;
    1074             : 
    1075             :   Real deriv_phi;
    1076             :   Real deriv_coh;
    1077             :   Real jacobian;
    1078        3592 :   const Real tol = Utility::pow<2>(_f_tol / (mag_E * 10.0));
    1079             :   unsigned int iter = 0;
    1080             :   do
    1081             :   {
    1082             :     do
    1083             :     {
    1084        8160 :       deriv_phi = dphi(intnl_old + dpm[3]);
    1085        8160 :       deriv_coh = dcohesion(intnl_old + dpm[3]);
    1086        8160 :       jacobian = -1 +
    1087        8160 :                  (numer_coeff1 * deriv_coh * cosphi - numer_coeff1 * coh * sinphi * deriv_phi +
    1088        8160 :                   numer_coeff2 * cosphi * deriv_phi) /
    1089             :                      denom -
    1090        8160 :                  numer * denom_coeff * cosphi * deriv_phi / denom / denom;
    1091        8160 :       x -= residual / jacobian;
    1092        8160 :       if (iter > _max_iters) // not converging
    1093             :       {
    1094           0 :         nr_converged = false;
    1095           0 :         return false;
    1096             :       }
    1097             : 
    1098        8160 :       sinphi = std::sin(phi(intnl_old + x));
    1099        8160 :       cosphi = std::cos(phi(intnl_old + x));
    1100        8160 :       coh = cohesion(intnl_old + x);
    1101        8160 :       cohcos = coh * cosphi;
    1102        8160 :       numer = numer_const + numer_coeff1 * cohcos + numer_coeff2 * sinphi;
    1103        8160 :       denom = denom_const + denom_coeff * sinphi;
    1104        8160 :       residual = -x + numer / denom;
    1105        8160 :       iter++;
    1106        8160 :     } while (residual * residual > tol);
    1107             : 
    1108             :     // now must ensure that yf[1] and yf[3] are both "zero"
    1109             :     Real dpm1minusdpm3 =
    1110        3592 :         (2 * (eigvals[1] - eigvals[2]) + x * (n[1](2) - n[1](1) + n[3](2) - n[3](1))) /
    1111        3592 :         (n[1](1) - n[1](2) + n[3](2) - n[3](1));
    1112        3592 :     dpm[1] = (x + dpm1minusdpm3) / 2.0;
    1113        3592 :     dpm[3] = (x - dpm1minusdpm3) / 2.0;
    1114             : 
    1115       14368 :     for (unsigned i = 0; i < 3; ++i)
    1116       10776 :       returned_stress(i, i) = eigvals[i] - dpm[1] * n[1](i) - dpm[3] * n[3](i);
    1117        3592 :     yieldFunctionEigvals(
    1118             :         returned_stress(0, 0), returned_stress(1, 1), returned_stress(2, 2), sinphi, cohcos, yf);
    1119        3592 :   } while (yf[1] * yf[1] > _f_tol * _f_tol && yf[3] * yf[3] > _f_tol * _f_tol);
    1120             : 
    1121             :   // so the NR process converged, but we must
    1122             :   // check Kuhn-Tucker
    1123        3592 :   nr_converged = true;
    1124             : 
    1125        3592 :   if (dpm[1] < -ep_plastic_tolerance || dpm[3] < -ep_plastic_tolerance)
    1126             :     // Kuhn-Tucker failure.    This is a potential weak-point: if the user has set _f_tol quite
    1127             :     // large, and ep_plastic_tolerance quite small, the above NR process will quickly converge, but
    1128             :     // the solution may be wrong and violate Kuhn-Tucker
    1129             :     return false;
    1130             : 
    1131        1688 :   if (yf[0] > _f_tol || yf[2] > _f_tol || yf[4] > _f_tol || yf[5] > _f_tol)
    1132             :     // Kuhn-Tucker failure
    1133             :     return false;
    1134             : 
    1135             :   // success
    1136        1688 :   dpm[0] = dpm[2] = dpm[4] = dpm[5] = 0;
    1137        1688 :   return true;
    1138             : }
    1139             : 
    1140             : bool
    1141           0 : TensorMechanicsPlasticMohrCoulombMulti::KuhnTuckerOK(const std::vector<Real> & yf,
    1142             :                                                      const std::vector<Real> & dpm,
    1143             :                                                      Real ep_plastic_tolerance) const
    1144             : {
    1145             :   mooseAssert(
    1146             :       yf.size() == 6,
    1147             :       "TensorMechanicsPlasticMohrCoulomb::KuhnTuckerOK requires yf to be size 6, but your is "
    1148             :           << yf.size());
    1149             :   mooseAssert(
    1150             :       dpm.size() == 6,
    1151             :       "TensorMechanicsPlasticMohrCoulomb::KuhnTuckerOK requires dpm to be size 6, but your is "
    1152             :           << dpm.size());
    1153           0 :   for (unsigned i = 0; i < 6; ++i)
    1154           0 :     if (!TensorMechanicsPlasticModel::KuhnTuckerSingleSurface(yf[i], dpm[i], ep_plastic_tolerance))
    1155             :       return false;
    1156             :   return true;
    1157             : }
    1158             : 
    1159             : bool
    1160           0 : TensorMechanicsPlasticMohrCoulombMulti::useCustomReturnMap() const
    1161             : {
    1162           0 :   return _use_custom_returnMap;
    1163             : }

Generated by: LCOV version 1.14