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

Generated by: LCOV version 1.14