void
InteractionIntegral::computeTFields(RankTwoTensor & aux_stress, RankTwoTensor & grad_disp)
{

  Real t = _theta;
  Real st = std::sin(t);
  Real ct = std::cos(t);
  Real stsq = Utility::pow<2>(st);
  Real ctsq = Utility::pow<2>(ct);
  Real ctcu = Utility::pow<3>(ct);
  Real oneOverPiR = 1.0 / (libMesh::pi * _r);

  aux_stress.zero();
  aux_stress(0, 0) = -oneOverPiR * ctcu;
  aux_stress(0, 1) = -oneOverPiR * st * ctsq;
  aux_stress(1, 0) = -oneOverPiR * st * ctsq;
  aux_stress(1, 1) = -oneOverPiR * ct * stsq;
  aux_stress(2, 2) = -oneOverPiR * _poissons_ratio * (ctcu + ct * stsq);

  grad_disp.zero();
  grad_disp(0, 0) = oneOverPiR / (4.0 * _youngs_modulus) *
                    (ct * (4.0 * Utility::pow<2>(_poissons_ratio) - 3.0 + _poissons_ratio) -
                     std::cos(3.0 * t) * (1.0 + _poissons_ratio));
  grad_disp(0, 1) = -oneOverPiR / (4.0 * _youngs_modulus) *
                    (st * (4.0 * Utility::pow<2>(_poissons_ratio) - 3.0 + _poissons_ratio) +
                     std::sin(3.0 * t) * (1.0 + _poissons_ratio));
}
 bool
 stznewHEVPFlowRatePowerLawJ2::computeDirection(unsigned int qp, RankTwoTensor & val) const
 {
   RankTwoTensor pk2_dev = computePK2Deviatoric(_pk2[qp], _ce[qp]);
  //  Real s_xx = _tensor[qp](0,0);
  //  Real s_yy = _tensor[qp](1,1);
  //  Real tau_xy= _tensor[qp](0,1);
  //  Real s_mean = 1.0/3.0*(s_xx+s_yy);
  //  Real PI = 3.1415926;
  //  Real tau_max = std::pow((s_xx-s_yy)/2*(s_xx-s_yy)/2+tau_xy*tau_xy,0.5);
  //  Real eqv_stress = tau_max;
  //  RankTwoTensor pk2_dev = computePK2Deviatoric(_pk2[qp], _ce[qp]);
   Real eqv_stress = computeEqvStress(pk2_dev, _ce[qp]);
   RankTwoTensor dirnew ;
   Real anglec;
   Real angles;
   Real PI= 3.1415926;
   Real theta= 0.25*PI+0.5*(_phiangle/180.0*PI);
   anglec = std::cos(theta);
   angles = std::sin(theta);
   dirnew.zero();

   dirnew(0,0)=2.0*anglec*angles;
   dirnew(1,1)=-2.0*anglec*angles;
   val.zero();
   if (eqv_stress > _strength[qp])
     {  val = 1.5/eqv_stress * _ce[qp] * pk2_dev * _ce[qp];
        //val = 1.5/eqv_stress*pk2_dev*_ce[qp];
      //  val = dirnew;
      //
      //
     }

   return true;
 }
Real
PorousFlowDispersiveFlux::computeQpResidual()
{
    RealVectorValue flux = 0.0;
    RealVectorValue velocity;
    Real velocity_abs;
    RankTwoTensor v2;
    RankTwoTensor dispersion;
    dispersion.zero();
    Real diffusion;

    for (unsigned int ph = 0; ph < _num_phases; ++ph)
    {
        // Diffusive component
        diffusion = _porosity_qp[_qp] * _tortuosity[_qp][ph] * _diffusion_coeff[_qp][ph][_fluid_component];

        // Calculate Darcy velocity
        velocity = (_permeability[_qp] * (_grad_p[_qp][ph] - _fluid_density_qp[_qp][ph] *
                                          _gravity) * _relative_permeability[_qp][ph] / _fluid_viscosity[_qp][ph]);
        velocity_abs = std::sqrt(velocity * velocity);

        if (velocity_abs > 0.0)
        {
            v2.vectorOuterProduct(velocity, velocity);

            // Add longitudinal dispersion to diffusive component
            diffusion += _disp_trans[ph] * velocity_abs;
            dispersion = (_disp_long[ph] - _disp_trans[ph]) * v2 / velocity_abs;
        }

        flux += _fluid_density_qp[_qp][ph] * (diffusion * _identity_tensor + dispersion) * _grad_mass_frac[_qp][ph][_fluid_component];
    }
    return _grad_test[_i][_qp] * flux;
}
bool
HEVPFlowRatePowerLawJ2::computeDirection(unsigned int qp, RankTwoTensor & val) const
{
  RankTwoTensor pk2_dev = computePK2Deviatoric(_pk2[qp], _ce[qp]);
  Real eqv_stress = computeEqvStress(pk2_dev, _ce[qp]);

  val.zero();
  if (eqv_stress > 0.0)
    val = 1.5 / eqv_stress * _ce[qp] * pk2_dev * _ce[qp];

  return true;
}
Exemple #5
0
bool
FlowRateModel::computeFlowDirection(RankTwoTensor & flow_dirn,
                                    const RankTwoTensor & pk2,
                                    const RankTwoTensor & ce,
                                    const std::vector<Real> & /*internal_var*/,
                                    const unsigned int /*start_index*/,
                                    const unsigned int /*size*/) const
{
  RankTwoTensor pk2_dev = computePK2Deviatoric(pk2, ce);
  Real eqv_stress = computeEqvStress(pk2_dev, ce);

  flow_dirn.zero();
  if (eqv_stress > 0.0)
    flow_dirn = 3.0/(2.0 * eqv_stress) * ce * pk2_dev * ce;

  return true;
}
 bool
 stzRHEVPFlowRatePowerLawJ2::computeTensorDerivative(unsigned int qp, const std::string & coupled_var_name, RankTwoTensor & val) const
 {
   val.zero();

   if (_pk2_prop_name == coupled_var_name)
   {
     RankTwoTensor pk2_dev = computePK2Deviatoric(_pk2[qp], _ce[qp]);
     Real eqv_stress = computeEqvStress(pk2_dev, _ce[qp]);
       Real dflowrate_dseqv;
      if (eqv_stress>_strength[qp])
	  //   dflowrate_dseqv = 1/_t_tau*std::exp(-1/_chiv[qp])*std::exp(eqv_stress/_grain_pressure/_chiv[qp])* \
	  (1.0/(_grain_pressure*_chiv[qp])*(1.0-_strength[qp]/eqv_stress)+_strength[qp]/(eqv_stress*eqv_stress));
    dflowrate_dseqv = _v*std::exp(-1/_chiv[qp])*(1.0-1.0/(eqv_stress*eqv_stress));


     RankTwoTensor tau = pk2_dev * _ce[qp];
     RankTwoTensor dseqv_dpk2dev;

     dseqv_dpk2dev.zero();
     if (eqv_stress > _strength[qp])
       dseqv_dpk2dev = 1.5/eqv_stress * tau * _ce[qp];

     RankTwoTensor ce_inv = _ce[qp].inverse();

     RankFourTensor dpk2dev_dpk2;
     for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
       for (unsigned int j = 0; j < LIBMESH_DIM; ++j)
         for (unsigned int k = 0; k < LIBMESH_DIM; ++k)
           for (unsigned int l = 0; l < LIBMESH_DIM; ++l)
           {
             dpk2dev_dpk2(i, j, k, l) = 0.0;
             if (i==k && j==l)
               dpk2dev_dpk2(i, j, k, l) = 1.0;
             dpk2dev_dpk2(i, j, k, l) -= ce_inv(i, j) * _ce[qp](k, l)/3.0;
           }
     val = dflowrate_dseqv * dpk2dev_dpk2.transposeMajor() * dseqv_dpk2dev;
   }
   return true;
 }
bool
HEVPFlowRatePowerLawJ2::computeTensorDerivative(unsigned int qp,
                                                const std::string & coupled_var_name,
                                                RankTwoTensor & val) const
{
  val.zero();

  if (_pk2_prop_name == coupled_var_name)
  {
    RankTwoTensor pk2_dev = computePK2Deviatoric(_pk2[qp], _ce[qp]);
    Real eqv_stress = computeEqvStress(pk2_dev, _ce[qp]);
    Real dflowrate_dseqv = _ref_flow_rate * _flow_rate_exponent *
                           std::pow(eqv_stress / _strength[qp], _flow_rate_exponent - 1.0) /
                           _strength[qp];

    RankTwoTensor tau = pk2_dev * _ce[qp];
    RankTwoTensor dseqv_dpk2dev;

    dseqv_dpk2dev.zero();
    if (eqv_stress > 0.0)
      dseqv_dpk2dev = 1.5 / eqv_stress * tau * _ce[qp];

    RankTwoTensor ce_inv = _ce[qp].inverse();

    RankFourTensor dpk2dev_dpk2;
    for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
      for (unsigned int j = 0; j < LIBMESH_DIM; ++j)
        for (unsigned int k = 0; k < LIBMESH_DIM; ++k)
          for (unsigned int l = 0; l < LIBMESH_DIM; ++l)
          {
            dpk2dev_dpk2(i, j, k, l) = 0.0;
            if (i == k && j == l)
              dpk2dev_dpk2(i, j, k, l) = 1.0;
            dpk2dev_dpk2(i, j, k, l) -= ce_inv(i, j) * _ce[qp](k, l) / 3.0;
          }
    val = dflowrate_dseqv * dpk2dev_dpk2.transposeMajor() * dseqv_dpk2dev;
  }
  return true;
}
void
RecomputeRadialReturn::computeStress(RankTwoTensor & strain_increment,
                                     RankTwoTensor & inelastic_strain_increment,
                                     RankTwoTensor & stress_new)
{
  // Given the stretching, update the inelastic strain
  // Compute the stress in the intermediate configuration while retaining the stress history

  // compute the deviatoric trial stress and trial strain from the current intermediate configuration
  RankTwoTensor deviatoric_trial_stress = stress_new.deviatoric();

  // compute the effective trial stress
  Real dev_trial_stress_squared = deviatoric_trial_stress.doubleContraction(deviatoric_trial_stress);
  Real effective_trial_stress = std::sqrt(3.0 / 2.0 * dev_trial_stress_squared);

  //If the effective trial stress is zero, so should the inelastic strain increment be zero
  //In that case skip the entire iteration if the effective trial stress is zero
  if (effective_trial_stress != 0.0)
  {
    computeStressInitialize(effective_trial_stress);

    // Use Newton sub-iteration to determine the scalar effective inelastic strain increment
    Real scalar_effective_inelastic_strain = 0;
    unsigned int iteration = 0;
    Real residual = 10;  // use a large number here to guarantee at least one loop through while
    Real norm_residual = 10;
    Real first_norm_residual = 10;

    // create an output string with iteration information when errors occur
    std::string iteration_output;

    while (iteration < _max_its &&
          norm_residual > _absolute_tolerance &&
          (norm_residual/first_norm_residual) > _relative_tolerance)
    {
      iterationInitialize(scalar_effective_inelastic_strain);

      residual = computeResidual(effective_trial_stress, scalar_effective_inelastic_strain);
      norm_residual = std::abs(residual);
      if (iteration == 0)
      {
        first_norm_residual = norm_residual;
        if (first_norm_residual == 0)
          first_norm_residual = 1;
      }

      Real derivative = computeDerivative(effective_trial_stress, scalar_effective_inelastic_strain);

      scalar_effective_inelastic_strain -= residual / derivative;

      if (_output_iteration_info || _output_iteration_info_on_error)
      {
        iteration_output = "In the element " + Moose::stringify(_current_elem->id()) +
                         + " and the qp point " + Moose::stringify(_qp) + ": \n" +
                         + " iteration = " + Moose::stringify(iteration ) + "\n" +
                         + " effective trial stress = " + Moose::stringify(effective_trial_stress) + "\n" +
                         + " scalar effective inelastic strain = " + Moose::stringify(scalar_effective_inelastic_strain) +"\n" +
                         + " relative residual = " + Moose::stringify(norm_residual/first_norm_residual) + "\n" +
                         + " relative tolerance = " + Moose::stringify(_relative_tolerance) + "\n" +
                         + " absolute residual = " + Moose::stringify(norm_residual) + "\n" +
                         + " absolute tolerance = " + Moose::stringify(_absolute_tolerance) + "\n";
      }

      iterationFinalize(scalar_effective_inelastic_strain);
      ++iteration;
    }

    if (_output_iteration_info)
      _console << iteration_output << std::endl;

    if (iteration == _max_its &&
      norm_residual > _absolute_tolerance &&
      (norm_residual/first_norm_residual) > _relative_tolerance)
    {
      if (_output_iteration_info_on_error)
        Moose::err << iteration_output;

      mooseError("Exceeded maximum iterations in RecomputeRadialReturn solve for material: " << _name << ".  Rerun with  'output_iteration_info_on_error = true' for more information.");
    }

    // compute inelastic strain increments while avoiding a potential divide by zero
    inelastic_strain_increment = deviatoric_trial_stress;
    inelastic_strain_increment *= (3.0 / 2.0 * scalar_effective_inelastic_strain / effective_trial_stress);
  }
  else
    inelastic_strain_increment.zero();

  strain_increment -= inelastic_strain_increment;
  stress_new = _elasticity_tensor[_qp] * (strain_increment + _elastic_strain_old[_qp]);

  computeStressFinalize(inelastic_strain_increment);
}
Exemple #9
0
void mooseSetToZero<RankTwoTensor>(RankTwoTensor & v)
{
  v.zero();
}
bool
TensorMechanicsPlasticTensileMulti::doReturnMap(const RankTwoTensor & trial_stress, const Real & intnl_old, const RankFourTensor & E_ijkl,
                                                Real /*ep_plastic_tolerance*/, RankTwoTensor & returned_stress, Real & returned_intnl,
                                                std::vector<Real> & dpm, RankTwoTensor & delta_dp, std::vector<Real> & yf,
                                                bool & trial_stress_inadmissible) const
{
  mooseAssert(dpm.size() == 3, "TensorMechanicsPlasticTensileMulti size of dpm should be 3 but it is " << dpm.size());

  std::vector<Real> eigvals;
  RankTwoTensor eigvecs;
  trial_stress.symmetricEigenvaluesEigenvectors(eigvals, eigvecs);
  eigvals[0] += _shift;
  eigvals[2] -= _shift;

  Real str = tensile_strength(intnl_old);

  yf.resize(3);
  yf[0] = eigvals[0] - str;
  yf[1] = eigvals[1] - str;
  yf[2] = eigvals[2] - str;

  if (yf[0] <= _f_tol && yf[1] <= _f_tol && yf[2] <= _f_tol)
  {
    // purely elastic (trial_stress, intnl_old)
    trial_stress_inadmissible = false;
    return true;
  }

  trial_stress_inadmissible = true;
  delta_dp.zero();
  returned_stress.zero();

  // In the following i often assume that E_ijkl is
  // for an isotropic situation.  This reduces FLOPS
  // substantially which is important since the returnMap
  // is potentially the most compute-intensive function
  // of a simulation.
  // In many comments i write the general expression, and
  // i hope that might guide future coders if they are
  // generalising to a non-istropic E_ijkl

  // n[alpha] = E_ijkl*r[alpha]_kl expressed in principal stress space
  // (alpha = 0, 1, 2, corresponding to the three surfaces)
  // Note that in principal stress space, the flow
  // directions are, expressed in 'vector' form,
  // r[0] = (1,0,0), r[1] = (0,1,0), r[2] = (0,0,1).
  // Similar for _n:
  // so _n[0] = E_ij00*r[0], _n[1] = E_ij11*r[1], _n[2] = E_ij22*r[2]
  // In the following I assume that the E_ijkl is
  // for an isotropic situation.
  // In the anisotropic situation, we couldn't express
  // the flow directions as vectors in the same principal
  // stress space as the stress: they'd be full rank-2 tensors
  std::vector<std::vector<Real> > n(3);
  for (unsigned i = 0 ; i < 3 ; ++i)
    n[i].resize(3);
  n[0][0] = E_ijkl(0,0,0,0);
  n[0][1] = E_ijkl(1,1,0,0);
  n[0][2] = E_ijkl(2,2,0,0);
  n[1][0] = E_ijkl(0,0,1,1);
  n[1][1] = E_ijkl(1,1,1,1);
  n[1][2] = E_ijkl(2,2,1,1);
  n[2][0] = E_ijkl(0,0,2,2);
  n[2][1] = E_ijkl(1,1,2,2);
  n[2][2] = E_ijkl(2,2,2,2);


  // With non-zero Poisson's ratio and hardening
  // it is not computationally cheap to know whether
  // the trial stress will return to the tip, edge,
  // or plane.  The following is correct for zero
  // Poisson's ratio and no hardening, and at least
  // gives a not-completely-stupid guess in the
  // more general case.
  // trial_order[0] = type of return to try first
  // trial_order[1] = type of return to try second
  // trial_order[2] = type of return to try third
  std::vector<int> trial_order(3);
  if (yf[0] > 0) // all the yield functions are positive, since eigvals are ordered eigvals[0] <= eigvals[1] <= eigvals[2]
  {
    trial_order[0] = tip;
    trial_order[1] = edge;
    trial_order[2] = plane;
  }
  else if (yf[1] > 0)  // two yield functions are positive
  {
    trial_order[0] = edge;
    trial_order[1] = tip;
    trial_order[2] = plane;
  }
  else
  {
    trial_order[0] = plane;
    trial_order[1] = edge;
    trial_order[2] = tip;
  }

  unsigned trial;
  bool nr_converged;
  for (trial = 0 ; trial < 3 ; ++trial)
  {
    switch (trial_order[trial])
    {
      case tip:
        nr_converged = returnTip(eigvals, n, dpm, returned_stress, intnl_old, 0);
        break;
      case edge:
        nr_converged = returnEdge(eigvals, n, dpm, returned_stress, intnl_old, 0);
        break;
      case plane:
        nr_converged = returnPlane(eigvals, n, dpm, returned_stress, intnl_old, 0);
        break;
    }
    str = tensile_strength(intnl_old + dpm[0] + dpm[1] + dpm[2]);
    if (nr_converged && KuhnTuckerOK(returned_stress, dpm, str))
      break;
  }

  if (trial == 3)
  {
    Moose::err << "Trial stress = \n";
    trial_stress.print(Moose::err);
    Moose::err << "Internal parameter = " << intnl_old << "\n";
    mooseError("TensorMechanicsPlasticTensileMulti: FAILURE!  You probably need to implement a line search\n");
    // failure - must place yield function values at trial stress into yf
    str = tensile_strength(intnl_old);
    yf[0] = eigvals[0] - str;
    yf[1] = eigvals[1] - str;
    yf[2] = eigvals[2] - str;
    return false;
  }

  // success

  returned_intnl = intnl_old;
  for (unsigned i = 0 ; i < 3 ; ++i)
  {
    yf[i] = returned_stress(i, i) - str;
    delta_dp(i, i) = dpm[i];
    returned_intnl += dpm[i];
  }
  returned_stress = eigvecs*returned_stress*(eigvecs.transpose());
  delta_dp = eigvecs*delta_dp*(eigvecs.transpose());
  return true;
}
 bool
 stznewHEVPFlowRatePowerLawJ2::computeTensorDerivative(unsigned int qp, const std::string & coupled_var_name, RankTwoTensor & val) const
 {
   val.zero();

   if (_pk2_prop_name == coupled_var_name)
   {
     RankTwoTensor pk2_dev = computePK2Deviatoric(_pk2[qp], _ce[qp]);
    //  Real s_xx = _tensor[qp](0,0);
    //  Real s_yy = _tensor[qp](1,1);
    //  Real tau_xy= _tensor[qp](0,1);
    //  Real s_mean = 1.0/3.0*(s_xx+s_yy);
    //  Real PI = 3.1415926;
    //  Real tau_max = std::pow((s_xx-s_yy)/2*(s_xx-s_yy)/2+tau_xy*tau_xy,0.5);
    //  Real eqv_stress = tau_max ;
     Real eqv_stress = computeEqvStress(pk2_dev, _ce[qp]);

     Real _pressure_eff=_grain_pressure-_biot*_pf[qp];
    //  Real _t_taunew = _agrain[qp]/std::sqrt(_grain_pressure/_rho);
    Real _t_taunew = _agrain[qp]/std::sqrt(_pressure_eff/_rho);
     //   Real dflowrate_dseqv = _ref_flow_rate * _flow_rate_exponent * std::pow(eqv_stress/_strength[qp],_flow_rate_exponent-1.0)/_strength[qp];
 //  stz
     //   Real dflowrate_dseqv = 1/_t_tau*std::exp(-1/_chiv[qp])*std::exp(eqv_stress/_grain_pressure/_chiv[qp])* \
     //	    (1/(_grain_pressure*_chiv[qp])*(1-_strength[qp]/eqv_stress)+_strength[qp]/(eqv_stress*eqv_stress))*(eqv_stress>_strength[qp]);
//
       Real dflowrate_dseqv;
      if (eqv_stress>_strength[qp])
	  //   dflowrate_dseqv = 1/_t_tau*std::exp(-1/_chiv[qp])*std::exp(eqv_stress/_grain_pressure/_chiv[qp])* \
	  (1.0/(_grain_pressure*_chiv[qp])*(1.0-_strength[qp]/eqv_stress)+_strength[qp]/(eqv_stress*eqv_stress));
  //   Original Stuff
  {
    dflowrate_dseqv = 1/_t_taunew*std::exp(-1/_chiv[qp])*std::exp(eqv_stress/_pressure_eff/_chiv[qp])* \
    (1.0/(_pressure_eff*_chiv[qp])*(1.0-_strength[qp]/eqv_stress)+_strength[qp]/(eqv_stress*eqv_stress));
  } // Modified
  //  {
      //dflowrate_dseqv = 1.0/_t_taunew*std::exp(-1.0/_chiv[qp])*(-1.0*_strength[qp])/(eqv_stress*eqv_stress);
  //  }	//  dflowrate_dseqv = 1/_t_tau*std::exp(-1/_chi)*std::exp(eqv_stress/_grain_pressure/_chi)* \
	      //  (1.0/(_grain_pressure*_chi)*(1.0-_strength[qp]/eqv_stress)+_strength[qp]/(eqv_stress*eqv_stress));

     RankTwoTensor tau = pk2_dev * _ce[qp];
     RankTwoTensor dseqv_dpk2dev;

     dseqv_dpk2dev.zero();
     if (eqv_stress > _strength[qp])
       dseqv_dpk2dev = 1.5/eqv_stress * tau * _ce[qp];

     RankTwoTensor ce_inv = _ce[qp].inverse();

     RankFourTensor dpk2dev_dpk2;
     for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
       for (unsigned int j = 0; j < LIBMESH_DIM; ++j)
         for (unsigned int k = 0; k < LIBMESH_DIM; ++k)
           for (unsigned int l = 0; l < LIBMESH_DIM; ++l)
           {
             dpk2dev_dpk2(i, j, k, l) = 0.0;
             if (i==k && j==l)
               dpk2dev_dpk2(i, j, k, l) = 1.0;
             dpk2dev_dpk2(i, j, k, l) -= ce_inv(i, j) * _ce[qp](k, l)/3.0;
           }
     val = dflowrate_dseqv * dpk2dev_dpk2.transposeMajor() * dseqv_dpk2dev;
   }
   return true;
 }
void
InteractionIntegral::computeAuxFields(RankTwoTensor & aux_stress, RankTwoTensor & grad_disp)
{
  RealVectorValue k(0.0);
  if (_sif_mode == SifMethod::KI)
    k(0) = 1.0;
  else if (_sif_mode == SifMethod::KII)
    k(1) = 1.0;
  else if (_sif_mode == SifMethod::KIII)
    k(2) = 1.0;

  Real t = _theta;
  Real t2 = _theta / 2.0;
  Real tt2 = 3.0 * _theta / 2.0;
  Real st = std::sin(t);
  Real ct = std::cos(t);
  Real st2 = std::sin(t2);
  Real ct2 = std::cos(t2);
  Real stt2 = std::sin(tt2);
  Real ctt2 = std::cos(tt2);
  Real ct2sq = Utility::pow<2>(ct2);
  Real ct2cu = Utility::pow<3>(ct2);
  Real sqrt2PiR = std::sqrt(2.0 * libMesh::pi * _r);

  // Calculate auxiliary stress tensor
  aux_stress.zero();

  aux_stress(0, 0) =
      1.0 / sqrt2PiR * (k(0) * ct2 * (1.0 - st2 * stt2) - k(1) * st2 * (2.0 + ct2 * ctt2));
  aux_stress(1, 1) = 1.0 / sqrt2PiR * (k(0) * ct2 * (1.0 + st2 * stt2) + k(1) * st2 * ct2 * ctt2);
  aux_stress(0, 1) = 1.0 / sqrt2PiR * (k(0) * ct2 * st2 * ctt2 + k(1) * ct2 * (1.0 - st2 * stt2));
  aux_stress(0, 2) = -1.0 / sqrt2PiR * k(2) * st2;
  aux_stress(1, 2) = 1.0 / sqrt2PiR * k(2) * ct2;
  // plane stress
  // Real s33 = 0;
  // plane strain
  aux_stress(2, 2) = _poissons_ratio * (aux_stress(0, 0) + aux_stress(1, 1));

  aux_stress(1, 0) = aux_stress(0, 1);
  aux_stress(2, 0) = aux_stress(0, 2);
  aux_stress(2, 1) = aux_stress(1, 2);

  // Calculate x1 derivative of auxiliary displacements
  grad_disp.zero();

  grad_disp(0, 0) = k(0) / (4.0 * _shear_modulus * sqrt2PiR) *
                        (ct * ct2 * _kappa + ct * ct2 - 2.0 * ct * ct2cu + st * st2 * _kappa +
                         st * st2 - 6.0 * st * st2 * ct2sq) +
                    k(1) / (4.0 * _shear_modulus * sqrt2PiR) *
                        (ct * st2 * _kappa + ct * st2 + 2.0 * ct * st2 * ct2sq - st * ct2 * _kappa +
                         3.0 * st * ct2 - 6.0 * st * ct2cu);

  grad_disp(0, 1) = k(0) / (4.0 * _shear_modulus * sqrt2PiR) *
                        (ct * st2 * _kappa + ct * st2 - 2.0 * ct * st2 * ct2sq - st * ct2 * _kappa -
                         5.0 * st * ct2 + 6.0 * st * ct2cu) +
                    k(1) / (4.0 * _shear_modulus * sqrt2PiR) *
                        (-ct * ct2 * _kappa + 3.0 * ct * ct2 - 2.0 * ct * ct2cu -
                         st * st2 * _kappa + 3.0 * st * st2 - 6.0 * st * st2 * ct2sq);

  grad_disp(0, 2) = k(2) / (_shear_modulus * sqrt2PiR) * (st2 * ct - ct2 * st);
}
void
ComputeMultipleInelasticStress::updateQpState(RankTwoTensor & elastic_strain_increment,
                                              RankTwoTensor & combined_inelastic_strain_increment)
{
  if (_output_iteration_info == true)
  {
    _console << std::endl
             << "iteration output for ComputeMultipleInelasticStress solve:"
             << " time=" << _t << " int_pt=" << _qp << std::endl;
  }
  Real l2norm_delta_stress;
  Real first_l2norm_delta_stress = 1.0;
  unsigned int counter = 0;

  std::vector<RankTwoTensor> inelastic_strain_increment;
  inelastic_strain_increment.resize(_num_models);

  for (unsigned i_rmm = 0; i_rmm < _models.size(); ++i_rmm)
    inelastic_strain_increment[i_rmm].zero();

  RankTwoTensor stress_max, stress_min;

  do
  {
    for (unsigned i_rmm = 0; i_rmm < _num_models; ++i_rmm)
    {
      _models[i_rmm]->setQp(_qp);

      // initially assume the strain is completely elastic
      elastic_strain_increment = _strain_increment[_qp];
      // and subtract off all inelastic strain increments calculated so far
      // except the one that we're about to calculate
      for (unsigned j_rmm = 0; j_rmm < _num_models; ++j_rmm)
        if (i_rmm != j_rmm)
          elastic_strain_increment -= inelastic_strain_increment[j_rmm];

      // form the trial stress, with the check for changed elasticity constants
      if (_is_elasticity_tensor_guaranteed_isotropic || !_perform_finite_strain_rotations)
      {
        _stress[_qp] =
            _elasticity_tensor[_qp] * (_elastic_strain_old[_qp] + elastic_strain_increment);
        // InitialStress Deprecation: remove these lines
        if (_perform_finite_strain_rotations)
          rotateQpInitialStress();
        addQpInitialStress();
      }
      else
        _stress[_qp] = _stress_old[_qp] + _elasticity_tensor[_qp] * elastic_strain_increment;

      // given a trial stress (_stress[_qp]) and a strain increment (elastic_strain_increment)
      // let the i^th model produce an admissible stress (as _stress[_qp]), and decompose
      // the strain increment into an elastic part (elastic_strain_increment) and an
      // inelastic part (inelastic_strain_increment[i_rmm])
      computeAdmissibleState(i_rmm,
                             elastic_strain_increment,
                             inelastic_strain_increment[i_rmm],
                             _consistent_tangent_operator[i_rmm]);

      if (i_rmm == 0)
      {
        stress_max = _stress[_qp];
        stress_min = _stress[_qp];
      }
      else
      {
        for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
        {
          for (unsigned int j = 0; j < LIBMESH_DIM; ++j)
          {
            if (_stress[_qp](i, j) > stress_max(i, j))
              stress_max(i, j) = _stress[_qp](i, j);
            else if (stress_min(i, j) > _stress[_qp](i, j))
              stress_min(i, j) = _stress[_qp](i, j);
          }
        }
      }
    }

    // now check convergence in the stress:
    // once the change in stress is within tolerance after each recompute material
    // consider the stress to be converged
    l2norm_delta_stress = (stress_max - stress_min).L2norm();
    if (counter == 0 && l2norm_delta_stress > 0.0)
      first_l2norm_delta_stress = l2norm_delta_stress;

    if (_output_iteration_info == true)
    {
      _console << "stress iteration number = " << counter << "\n"
               << " relative l2 norm delta stress = "
               << (0 == first_l2norm_delta_stress ? 0
                                                  : l2norm_delta_stress / first_l2norm_delta_stress)
               << "\n"
               << " stress convergence relative tolerance = " << _relative_tolerance << "\n"
               << " absolute l2 norm delta stress = " << l2norm_delta_stress << "\n"
               << " stress convergence absolute tolerance = " << _absolute_tolerance << std::endl;
    }
    ++counter;
  } while (counter < _max_iterations && l2norm_delta_stress > _absolute_tolerance &&
           (l2norm_delta_stress / first_l2norm_delta_stress) > _relative_tolerance &&
           _num_models != 1);

  if (counter == _max_iterations && l2norm_delta_stress > _absolute_tolerance &&
      (l2norm_delta_stress / first_l2norm_delta_stress) > _relative_tolerance)
    throw MooseException("Max stress iteration hit during ComputeMultipleInelasticStress solve!");

  combined_inelastic_strain_increment.zero();
  for (unsigned i_rmm = 0; i_rmm < _num_models; ++i_rmm)
    combined_inelastic_strain_increment +=
        _inelastic_weights[i_rmm] * inelastic_strain_increment[i_rmm];

  if (_fe_problem.currentlyComputingJacobian())
    computeQpJacobianMult();

  _matl_timestep_limit[_qp] = 0.0;
  for (unsigned i_rmm = 0; i_rmm < _num_models; ++i_rmm)
    _matl_timestep_limit[_qp] += 1.0 / _models[i_rmm]->computeTimeStepLimit();

  if (MooseUtils::absoluteFuzzyEqual(_matl_timestep_limit[_qp], 0.0))
  {
    _matl_timestep_limit[_qp] = std::numeric_limits<Real>::max();
  }
  else
  {
    _matl_timestep_limit[_qp] = 1.0 / _matl_timestep_limit[_qp];
  }
}
bool
TensorMechanicsPlasticMohrCoulombMulti::doReturnMap(const RankTwoTensor & trial_stress, Real intnl_old, const RankFourTensor & E_ijkl,
                                                    Real ep_plastic_tolerance, RankTwoTensor & returned_stress, Real & returned_intnl,
                                                    std::vector<Real> & dpm, RankTwoTensor & delta_dp, std::vector<Real> & yf,
                                                    bool & trial_stress_inadmissible) const
{
  mooseAssert(dpm.size() == 6, "TensorMechanicsPlasticMohrCoulombMulti size of dpm should be 6 but it is " << dpm.size());

  std::vector<Real> eigvals;
  RankTwoTensor eigvecs;
  trial_stress.symmetricEigenvaluesEigenvectors(eigvals, eigvecs);
  eigvals[0] += _shift;
  eigvals[2] -= _shift;

  Real sinphi = std::sin(phi(intnl_old));
  Real cosphi = std::cos(phi(intnl_old));
  Real coh = cohesion(intnl_old);
  Real cohcos = coh*cosphi;

  yieldFunctionEigvals(eigvals[0], eigvals[1], eigvals[2], sinphi, cohcos, yf);

  if (yf[0] <= _f_tol && yf[1] <= _f_tol && yf[2] <= _f_tol && yf[3] <= _f_tol && yf[4] <= _f_tol && yf[5] <= _f_tol)
  {
    // purely elastic (trial_stress, intnl_old)
    trial_stress_inadmissible = false;
    return true;
  }

  trial_stress_inadmissible = true;
  delta_dp.zero();
  returned_stress = RankTwoTensor();

  // these are the normals to the 6 yield surfaces, which are const because of the assumption of no psi hardening
  std::vector<RealVectorValue> norm(6);
  const Real sinpsi = std::sin(psi(intnl_old));
  const Real oneminus = 0.5*(1 - sinpsi);
  const Real oneplus = 0.5*(1 + sinpsi);
  norm[0](0) = oneplus; norm[0](1) = -oneminus; norm[0](2) = 0;
  norm[1](0) = -oneminus; norm[1](1) = oneplus; norm[1](2) = 0;
  norm[2](0) = oneplus; norm[2](1) = 0; norm[2](2) = -oneminus;
  norm[3](0) = -oneminus; norm[3](1) = 0; norm[3](2) = oneplus;
  norm[4](0) = 0; norm[4](1) = oneplus; norm[4](2) = -oneminus;
  norm[5](0) = 0; norm[5](1) = -oneminus; norm[5](2) = oneplus;

  // the flow directions are these norm multiplied by Eijkl.
  // I call the flow directions "n".
  // In the following I assume that the Eijkl is
  // for an isotropic situation.  Then I don't have to
  // rotate to the principal-stress frame, and i don't
  // have to worry about strange off-diagonal things
  std::vector<RealVectorValue> n(6);
  for (unsigned ys = 0; ys < 6; ++ys)
    for (unsigned i = 0; i < 3; ++i)
      for (unsigned j = 0; j < 3; ++j)
        n[ys](i) += E_ijkl(i,i,j,j)*norm[ys](j);
  const Real mag_E = E_ijkl(0, 0, 0, 0);

  // With non-zero Poisson's ratio and hardening
  // it is not computationally cheap to know whether
  // the trial stress will return to the tip, edge,
  // or plane.  The following at least
  // gives a not-completely-stupid guess
  // trial_order[0] = type of return to try first
  // trial_order[1] = type of return to try second
  // trial_order[2] = type of return to try third
  // trial_order[3] = type of return to try fourth
  // trial_order[4] = type of return to try fifth
  // In the following the "binary" stuff indicates the
  // deactive (0) and active (1) surfaces, eg
  // 110100 means that surfaces 0, 1 and 3 are active
  // and 2, 4 and 5 are deactive
  const unsigned int number_of_return_paths = 5;
  std::vector<int> trial_order(number_of_return_paths);
  if (yf[1] > _f_tol && yf[3] > _f_tol && yf[5] > _f_tol)
  {
    trial_order[0] = tip110100;
    trial_order[1] = edge010100;
    trial_order[2] = plane000100;
    trial_order[3] = edge000101;
    trial_order[4] = tip010101;
  }
  else if (yf[1] <= _f_tol && yf[3] > _f_tol && yf[5] > _f_tol)
  {
    trial_order[0] = edge000101;
    trial_order[1] = plane000100;
    trial_order[2] = tip110100;
    trial_order[3] = tip010101;
    trial_order[4] = edge010100;
  }
  else if (yf[1] <= _f_tol && yf[3] > _f_tol && yf[5] <= _f_tol)
  {
    trial_order[0] = plane000100;
    trial_order[1] = edge000101;
    trial_order[2] = edge010100;
    trial_order[3] = tip110100;
    trial_order[4] = tip010101;
  }
  else
  {
    trial_order[0] = edge010100;
    trial_order[1] = plane000100;
    trial_order[2] = edge000101;
    trial_order[3] = tip110100;
    trial_order[4] = tip010101;
  }

  unsigned trial;
  bool nr_converged = false;
  bool kt_success = false;
  std::vector<RealVectorValue> ntip(3);
  std::vector<Real> dpmtip(3);

  for (trial = 0; trial < number_of_return_paths; ++trial)
  {
    switch (trial_order[trial])
    {
      case tip110100:
        for (unsigned int i = 0; i < 3; ++i)
        {
          ntip[0](i) = n[0](i);
          ntip[1](i) = n[1](i);
          ntip[2](i) = n[3](i);
        }
        kt_success = returnTip(eigvals, ntip, dpmtip, returned_stress, intnl_old, sinphi, cohcos, 0, nr_converged, ep_plastic_tolerance, yf);
        if (nr_converged && kt_success)
        {
          dpm[0] = dpmtip[0];
          dpm[1] = dpmtip[1];
          dpm[3] = dpmtip[2];
          dpm[2] = dpm[4] = dpm[5] = 0;
        }
        break;

      case tip010101:
        for (unsigned int i = 0; i < 3; ++i)
        {
          ntip[0](i) = n[1](i);
          ntip[1](i) = n[3](i);
          ntip[2](i) = n[5](i);
        }
        kt_success = returnTip(eigvals, ntip, dpmtip, returned_stress, intnl_old, sinphi, cohcos, 0, nr_converged, ep_plastic_tolerance, yf);
        if (nr_converged && kt_success)
        {
          dpm[1] = dpmtip[0];
          dpm[3] = dpmtip[1];
          dpm[5] = dpmtip[2];
          dpm[0] = dpm[2] = dpm[4] = 0;
        }
        break;

      case edge000101:
        kt_success = returnEdge000101(eigvals, n, dpm, returned_stress, intnl_old, sinphi, cohcos, 0, mag_E, nr_converged, ep_plastic_tolerance, yf);
        break;

      case edge010100:
        kt_success = returnEdge010100(eigvals, n, dpm, returned_stress, intnl_old, sinphi, cohcos, 0, mag_E, nr_converged, ep_plastic_tolerance, yf);
        break;

      case plane000100:
        kt_success = returnPlane(eigvals, n, dpm, returned_stress, intnl_old, sinphi, cohcos, 0, nr_converged, ep_plastic_tolerance, yf);
        break;
    }

    if (nr_converged && kt_success)
      break;
  }

  if (trial == number_of_return_paths)
  {
    sinphi = std::sin(phi(intnl_old));
    cosphi = std::cos(phi(intnl_old));
    coh = cohesion(intnl_old);
    cohcos = coh*cosphi;
    yieldFunctionEigvals(eigvals[0], eigvals[1], eigvals[2], sinphi, cohcos, yf);
    Moose::err << "Trial stress = \n";
    trial_stress.print(Moose::err);
    Moose::err << "which has eigenvalues = " << eigvals[0] << " " << eigvals[1] << " " << eigvals[2] << "\n";
    Moose::err << "and yield functions = " << yf[0] << " " << yf[1] << " " << yf[2] << " " << yf[3] << " " << yf[4] << " " << yf[5] << "\n";
    Moose::err << "Internal parameter = " << intnl_old << "\n";
    mooseError("TensorMechanicsPlasticMohrCoulombMulti: FAILURE!  You probably need to implement a line search if your hardening is too severe, or you need to tune your tolerances (eg, yield_function_tolerance should be a little smaller than (young modulus)*ep_plastic_tolerance).\n");
    return false;
  }

  // success

  returned_intnl = intnl_old;
  for (unsigned i = 0; i < 6; ++i)
    returned_intnl += dpm[i];
  for (unsigned i = 0; i < 6; ++i)
    for (unsigned j = 0; j < 3; ++j)
      delta_dp(j, j) += dpm[i]*norm[i](j);
  returned_stress = eigvecs*returned_stress*(eigvecs.transpose());
  delta_dp = eigvecs*delta_dp*(eigvecs.transpose());
  return true;
}
Real
PorousFlowDispersiveFlux::computeQpJac(unsigned int jvar) const
{
    // If the variable is not a valid PorousFlow variable, set the Jacobian to 0
    if (_dictator.notPorousFlowVariable(jvar))
        return 0.0;

    const unsigned int pvar = _dictator.porousFlowVariableNum(jvar);

    RealVectorValue velocity;
    Real velocity_abs;
    RankTwoTensor v2;
    RankTwoTensor dispersion;
    dispersion.zero();
    Real diffusion;
    RealVectorValue flux = 0.0;
    RealVectorValue dflux = 0.0;

    for (unsigned int ph = 0; ph < _num_phases; ++ph)
    {
        // Diffusive component
        diffusion = _porosity_qp[_qp] * _tortuosity[_qp][ph] * _diffusion_coeff[_qp][ph][_fluid_component];

        // Calculate Darcy velocity
        velocity = (_permeability[_qp] * (_grad_p[_qp][ph] - _fluid_density_qp[_qp][ph] *
                                          _gravity) * _relative_permeability[_qp][ph] / _fluid_viscosity[_qp][ph]);
        velocity_abs = std::sqrt(velocity * velocity);

        if (velocity_abs > 0.0)
        {
            v2.vectorOuterProduct(velocity, velocity);

            // Add longitudinal dispersion to diffusive component
            diffusion += _disp_trans[ph] * velocity_abs;
            dispersion = (_disp_long[ph] - _disp_trans[ph]) * v2 / velocity_abs;
        }

        // Derivative of Darcy velocity
        RealVectorValue dvelocity = _dpermeability_dvar[_qp][pvar] * _phi[_j][_qp] * (_grad_p[_qp][ph] - _fluid_density_qp[_qp][ph]*_gravity);
        for (unsigned i = 0; i < LIBMESH_DIM; ++i)
            dvelocity += _dpermeability_dgradvar[_qp][i][pvar] * _grad_phi[_j][_qp](i) * (_grad_p[_qp][ph] - _fluid_density_qp[_qp][ph] * _gravity);
        dvelocity += _permeability[_qp] * (_grad_phi[_j][_qp] * _dgrad_p_dgrad_var[_qp][ph][pvar] - _phi[_j][_qp] * _dfluid_density_qp_dvar[_qp][ph][pvar] * _gravity);
        dvelocity += _permeability[_qp] * (_dgrad_p_dvar[_qp][ph][pvar] * _phi[_j][_qp]);

        Real dvelocity_abs = 0.0;
        if (velocity_abs > 0.0)
            dvelocity_abs = velocity * dvelocity / velocity_abs;

        // Derivative of diffusion term (note: dispersivity is assumed constant)
        Real ddiffusion = _phi[_j][_qp] * _dporosity_qp_dvar[_qp][pvar] * _tortuosity[_qp][ph] * _diffusion_coeff[_qp][ph][_fluid_component];
        ddiffusion += _phi[_j][_qp] * _porosity_qp[_qp] * _dtortuosity_dvar[_qp][ph][pvar] * _diffusion_coeff[_qp][ph][_fluid_component];
        ddiffusion += _phi[_j][_qp] * _porosity_qp[_qp] * _tortuosity[_qp][ph] * _ddiffusion_coeff_dvar[_qp][ph][_fluid_component][pvar];
        ddiffusion += _disp_trans[ph] * dvelocity_abs;

        // Derivative of dispersion term (note: dispersivity is assumed constant)
        RankTwoTensor ddispersion;
        ddispersion.zero();
        if (velocity_abs > 0.0)
        {
            RankTwoTensor dv2a, dv2b;
            dv2a.vectorOuterProduct(velocity, dvelocity);
            dv2b.vectorOuterProduct(dvelocity, velocity);
            ddispersion = (_disp_long[ph] - _disp_trans[ph]) * (dv2a + dv2b) / velocity_abs;
            ddispersion -= (_disp_long[ph] - _disp_trans[ph]) * v2 * dvelocity_abs / velocity_abs / velocity_abs;
        }

        dflux += _phi[_j][_qp] * _dfluid_density_qp_dvar[_qp][ph][pvar] * (diffusion * _identity_tensor + dispersion) * _grad_mass_frac[_qp][ph][_fluid_component];
        dflux += _fluid_density_qp[_qp][ph] * (ddiffusion * _identity_tensor + ddispersion) * _grad_mass_frac[_qp][ph][_fluid_component];
        dflux += _fluid_density_qp[_qp][ph] * (diffusion * _identity_tensor + dispersion) * _dmass_frac_dvar[_qp][ph][_fluid_component][pvar] * _grad_phi[_j][_qp];
    }

    return _grad_test[_i][_qp] * dflux;
}