Real
TensorMechanicsPlasticMeanCapTC::dyieldFunction_dintnl(const RankTwoTensor & stress, Real intnl) const
{
  const Real tr = stress.trace();
  const Real t_str = tensile_strength(intnl);
  if (tr >= t_str)
    return -dtensile_strength(intnl);
  const Real c_str = compressive_strength(intnl);
  if (tr <= c_str)
    return dcompressive_strength(intnl);
  const Real dt = dtensile_strength(intnl);
  const Real dc = dcompressive_strength(intnl);
  return (dc - dt) / M_PI * std::sin(M_PI * (tr - c_str) / (t_str - c_str)) + 1.0 / (t_str - c_str) * std::cos(M_PI * (tr - c_str) / (t_str - c_str)) * ((tr - c_str) * dt - (tr - t_str) * dc );
}
RankFourTensor
TensorMechanicsPlasticMeanCapTC::consistentTangentOperator(const RankTwoTensor & trial_stress, Real intnl_old, const RankTwoTensor & stress, Real intnl,
                                                    const RankFourTensor & E_ijkl, const std::vector<Real> & cumulative_pm) const
{
  if (!_use_custom_cto)
    return TensorMechanicsPlasticModel::consistentTangentOperator(trial_stress, intnl_old, stress, intnl, E_ijkl, cumulative_pm);

  Real df_dq;
  Real alpha;
  if (trial_stress.trace() >= tensile_strength(intnl_old))
  {
    df_dq = -dtensile_strength(intnl);
    alpha = 1.0;
  }
  else
  {
    df_dq = dcompressive_strength(intnl);
    alpha = -1.0;
  }

  RankTwoTensor elas;
  for (unsigned int i = 0; i < 3; ++i)
    for (unsigned int j = 0; j < 3; ++j)
      for (unsigned int k = 0; k < 3; ++k)
        elas(i, j) += E_ijkl(i, j, k, k);

  const Real hw = -df_dq + alpha * elas.trace();

  return E_ijkl - alpha / hw * elas.outerProduct(elas);
}
Real
TensorMechanicsPlasticMeanCapTC::dhardPotential_dintnl(const RankTwoTensor & stress, Real intnl) const
{
  const Real tr = stress.trace();
  const Real t_str = tensile_strength(intnl);
  if (tr >= t_str)
    return 0.0;
  const Real c_str = compressive_strength(intnl);
  if (tr <= c_str)
    return 0.0;
  const Real dt = dtensile_strength(intnl);
  const Real dc = dcompressive_strength(intnl);
  return - std::sin(M_PI * (tr - c_str) / (t_str - c_str)) * M_PI / std::pow(t_str - c_str, 2) * ((tr - t_str) * dc - (tr - c_str) * dt);
}
bool
TensorMechanicsPlasticTensileMulti::returnPlane(const std::vector<Real> & eigvals, const std::vector<std::vector<Real> > & n, std::vector<Real> & dpm, RankTwoTensor & returned_stress, const Real & intnl_old, const Real & initial_guess) const
{
  // the returned point, "a", is defined by f2=0 and
  // a = p - dpm[2]*n2.
  // This is a vector equation in
  // principal stress space, and dpm[2] is the third
  // plasticity multiplier (dpm[0]=0=dpm[1] for return
  // to the plane) and "p" is the starting
  // position (p=eigvals).
  // (Kuhn-Tucker demands that dpm[2]>=0, but we leave checking
  // that condition for later.)
  // Since f2=0, we must have a[2]=tensile_strength,
  // so we can just look at the [2] component of the
  // equation, which yields
  // n[2][2]*dpm[2] - eigvals[2] + str = 0
  // For hardening, str=tensile_strength(intnl_old+dpm[2]),
  // and we want to solve for dpm[2].
  // Use Newton-Raphson with initial guess dpm[2] = initial_guess
  dpm[2] = initial_guess;
  Real residual = n[2][2]*dpm[2] - eigvals[2] + tensile_strength(intnl_old + dpm[2]);
  Real jacobian;
  unsigned int iter = 0;
  do {
    jacobian = n[2][2] + dtensile_strength(intnl_old + dpm[2]);
    dpm[2] += -residual/jacobian;
    if (iter > _max_iters) // not converging
      return false;
    residual = n[2][2]*dpm[2] - eigvals[2] + tensile_strength(intnl_old + dpm[2]);
    iter ++;
  } while (residual*residual > _f_tol*_f_tol);

  dpm[0] = 0;
  dpm[1] = 0;
  returned_stress(0, 0) = eigvals[0] - dpm[2]*n[2][0];
  returned_stress(1, 1) = eigvals[1] - dpm[2]*n[2][1];
  returned_stress(2, 2) = eigvals[2] - dpm[2]*n[2][2];
  return true;
}
Real
TensorMechanicsPlasticTensile::dyieldFunction_dintnl(const RankTwoTensor & /*stress*/,
                                                     Real intnl) const
{
  return -dtensile_strength(intnl);
}
void
TensorMechanicsPlasticTensileMulti::dyieldFunction_dintnlV(const RankTwoTensor & /*stress*/, const Real & intnl, std::vector<Real> & df_dintnl) const
{
  df_dintnl.assign(3, -dtensile_strength(intnl));
}
RankFourTensor
TensorMechanicsPlasticTensileMulti::consistentTangentOperator(const RankTwoTensor & trial_stress, const RankTwoTensor & stress, const Real & intnl,
                                                       const RankFourTensor & E_ijkl, const std::vector<Real> & cumulative_pm) const
{
  if (!_use_custom_cto)
    return TensorMechanicsPlasticModel::consistentTangentOperator(trial_stress, stress, intnl, E_ijkl, cumulative_pm);

  mooseAssert(cumulative_pm.size() == 3, "TensorMechanicsPlasticTensileMulti size of cumulative_pm should be 3 but it is " << cumulative_pm.size());

  if (cumulative_pm[2] <= 0) // All cumulative_pm are non-positive, so this is admissible
    return E_ijkl;


  // Need the eigenvalues at the returned configuration
  std::vector<Real> eigvals;
  stress.symmetricEigenvalues(eigvals);

  // need to rotate to and from principal stress space
  // using the eigenvectors of the trial configuration
  // (not the returned configuration).
  std::vector<Real> trial_eigvals;
  RankTwoTensor trial_eigvecs;
  trial_stress.symmetricEigenvaluesEigenvectors(trial_eigvals, trial_eigvecs);

  // The returnMap will have returned to the Tip, Edge or
  // Plane.  The consistentTangentOperator describes the
  // change in stress for an arbitrary change in applied
  // strain.  I assume that the change in strain will not
  // change the type of return (Tip remains Tip, Edge remains
  // Edge, Plane remains Plane).
  // I assume isotropic elasticity.
  //
  // The consistent tangent operator is a little different
  // than cases where no rotation to principal stress space
  // is made during the returnMap.  Let S_ij be the stress
  // in original coordinates, and s_ij be the stress in the
  // principal stress coordinates, so that
  // s_ij = diag(eigvals[0], eigvals[1], eigvals[2])
  // We want dS_ij under an arbitrary change in strain (ep->ep+dep)
  // dS = S(ep+dep) - S(ep)
  //    = R(ep+dep) s(ep+dep) R(ep+dep)^T - R(ep) s(ep) R(ep)^T
  // Here R = the rotation to principal-stress space, ie
  // R_ij = eigvecs[i][j] = i^th component of j^th eigenvector
  // Expanding to first order in dep,
  // dS = R(ep) (s(ep+dep) - s(ep)) R(ep)^T
  //      + dR/dep s(ep) R^T + R(ep) s(ep) dR^T/dep
  // The first line is all that is usually calculated in the
  // consistent tangent operator calculation, and is called
  // cto below.
  // The second line involves changes in the eigenvectors, and
  // is called sec below.

  RankFourTensor cto;
  Real hard = dtensile_strength(intnl);
  Real la = E_ijkl(0,0,1,1);
  Real mu = 0.5*(E_ijkl(0,0,0,0) - la);

  if (cumulative_pm[1] <= 0)
  {
    // only cumulative_pm[2] is positive, so this is return to the Plane
    Real denom = hard + la + 2*mu;
    Real al = la*la/denom;
    Real be = la*(la + 2*mu)/denom;
    Real ga = hard*(la + 2*mu)/denom;
    std::vector<Real> comps(9);
    comps[0] = comps[4] = la + 2*mu - al;
    comps[1] = comps[3] = la - al;
    comps[2] = comps[5] = comps[6] = comps[7] = la - be;
    comps[8] = ga;
    cto.fillFromInputVector(comps, RankFourTensor::principal);
  }
  else if (cumulative_pm[0] <= 0)
  {
    // both cumulative_pm[2] and cumulative_pm[1] are positive, so Edge
    Real denom = 2*hard + 2*la + 2*mu;
    Real al = hard*2*la/denom;
    Real be = hard*(2*la + 2*mu)/denom;
    std::vector<Real> comps(9);
    comps[0] = la + 2*mu - 2*la*la/denom;
    comps[1] = comps[2] = al;
    comps[3] = comps[6] = al;
    comps[4] = comps[5] = comps[7] = comps[8] = be;
    cto.fillFromInputVector(comps, RankFourTensor::principal);
  }
  else
  {
    // all cumulative_pm are positive, so Tip
    Real denom = 3*hard + 3*la + 2*mu;
    std::vector<Real> comps(2);
    comps[0] = hard*(3*la + 2*mu)/denom;
    comps[1] = 0;
    cto.fillFromInputVector(comps, RankFourTensor::symmetric_isotropic);
  }

  cto.rotate(trial_eigvecs);


  // drdsig = change in eigenvectors under a small stress change
  // drdsig(i,j,m,n) = dR(i,j)/dS_mn
  // The formula below is fairly easily derived:
  // S R = R s, so taking the variation
  // dS R + S dR = dR s + R ds, and multiplying by R^T
  // R^T dS R + R^T S dR = R^T dR s + ds .... (eqn 1)
  // I demand that RR^T = 1 = R^T R, and also that
  // (R+dR)(R+dR)^T = 1 = (R+dT)^T (R+dR), which means
  // that dR = R*c, for some antisymmetric c, so Eqn1 reads
  // R^T dS R + s c = c s + ds
  // Grabbing the components of this gives ds/dS (already
  // in RankTwoTensor), and c, which is:
  //   dR_ik/dS_mn = drdsig(i, k, m, n) = trial_eigvecs(m, b)*trial_eigvecs(n, k)*trial_eigvecs(i, b)/(trial_eigvals[k] - trial_eigvals[b]);
  // (sum over b!=k).

  RankFourTensor drdsig;
  for (unsigned k = 0 ; k < 3 ; ++k)
    for (unsigned b = 0 ; b < 3 ; ++b)
    {
      if (b == k)
        continue;
      for (unsigned m = 0 ; m < 3 ; ++m)
        for (unsigned n = 0 ; n < 3 ; ++n)
          for (unsigned i = 0 ; i < 3 ; ++i)
            drdsig(i, k, m, n) += trial_eigvecs(m, b)*trial_eigvecs(n, k)*trial_eigvecs(i, b)/(trial_eigvals[k] - trial_eigvals[b]);
    }



  // With diagla = diag(eigvals[0], eigvals[1], digvals[2])
  // The following implements
  // ans(i, j, a, b) += (drdsig(i, k, m, n)*trial_eigvecs(j, l)*diagla(k, l) + trial_eigvecs(i, k)*drdsig(j, l, m, n)*diagla(k, l))*E_ijkl(m, n, a, b);
  // (sum over k, l, m and n)

  RankFourTensor ans;
  for (unsigned i = 0 ; i < 3 ; ++i)
    for (unsigned j = 0 ; j < 3 ; ++j)
      for (unsigned a = 0 ; a < 3 ; ++a)
        for (unsigned k = 0 ; k < 3 ; ++k)
          for (unsigned m = 0 ; m < 3 ; ++m)
            ans(i, j, a, a) += (drdsig(i, k, m, m)*trial_eigvecs(j, k) + trial_eigvecs(i, k)*drdsig(j, k, m, m))*eigvals[k]*la;  //E_ijkl(m, n, a, b) = la*(m==n)*(a==b);

  for (unsigned i = 0 ; i < 3 ; ++i)
    for (unsigned j = 0 ; j < 3 ; ++j)
      for (unsigned a = 0 ; a < 3 ; ++a)
        for (unsigned b = 0 ; b < 3 ; ++b)
          for (unsigned k = 0 ; k < 3 ; ++k)
          {
            ans(i, j, a, b) += (drdsig(i, k, a, b)*trial_eigvecs(j, k) + trial_eigvecs(i, k)*drdsig(j, k, a, b))*eigvals[k]*mu;  //E_ijkl(m, n, a, b) = mu*(m==a)*(n==b)
            ans(i, j, a, b) += (drdsig(i, k, b, a)*trial_eigvecs(j, k) + trial_eigvecs(i, k)*drdsig(j, k, b, a))*eigvals[k]*mu;  //E_ijkl(m, n, a, b) = mu*(m==b)*(n==a)
          }


  return cto + ans;

}
bool
TensorMechanicsPlasticTensileMulti::returnEdge(const std::vector<Real> & eigvals, const std::vector<std::vector<Real> > & n, std::vector<Real> & dpm, RankTwoTensor & returned_stress, const Real & intnl_old, const Real & initial_guess) const
{
  // work out the point to which we would return, "a".  It is defined by
  // f1 = 0 = f2, and the normality condition:
  //   (eigvals - a).(n1 x n2) = 0,
  // where eigvals is the starting position
  // (it is a vector in principal stress space).
  // To get f1=0=f2, we need a = (a0, str, str), and a0 is found
  // by expanding the normality condition to yield:
  //   a0 = (-str*n1xn2[1] - str*n1xn2[2] + edotn1xn2)/n1xn2[0];
  // where edotn1xn2 = eigvals.(n1 x n2)
  //
  // We need to find the plastic multipliers, dpm, defined by
  //   eigvals - a = dpm[1]*n1 + dpm[2]*n2
  // For the isotropic case, and defining eminusa = eigvals - a,
  // the solution is easy:
  //   dpm[0] = 0;
  //   dpm[1] = (eminusa[1] - eminusa[0])/(n[1][1] - n[1][0]);
  //   dpm[2] = (eminusa[2] - eminusa[0])/(n[2][2] - n[2][0]);
  //
  // Now specialise to the isotropic case.  Define
  //   x = dpm[1] + dpm[2] = (eigvals[1] + eigvals[2] - 2*str)/(n[0][0] + n[0][1])
  // Notice that the RHS is a function of x, so we solve using
  // Newton-Raphson starting with x=initial_guess
  Real x = initial_guess;
  Real denom = n[0][0] + n[0][1];
  Real str = tensile_strength(intnl_old + x);

  if (_strength.modelName().compare("Constant") != 0)
  {
    // Finding x is expensive.  Therefore
    // although x!=0 for Constant Hardening, solution
    // for dpm above demonstrates that we don't
    // actually need to know x to find the dpm for
    // Constant Hardening.
    //
    // However, for nontrivial Hardening, the following
    // is necessary
    Real eig = eigvals[1] + eigvals[2];
    Real residual = denom*x - eig + 2*str;
    Real jacobian;
    unsigned int iter = 0;
    do {
      jacobian = denom + 2*dtensile_strength(intnl_old + x);
      x += -residual/jacobian;
      if (iter > _max_iters) // not converging
        return false;
      str = tensile_strength(intnl_old + x);
      residual = denom*x - eig + 2*str;
      iter ++;
    } while (residual*residual > _f_tol*_f_tol);
  }

  dpm[0] = 0;
  dpm[1] = ((eigvals[1]*n[0][0] - eigvals[2]*n[0][1])/(n[0][0] - n[0][1]) - str)/denom;
  dpm[2] = ((eigvals[2]*n[0][0] - eigvals[1]*n[0][1])/(n[0][0] - n[0][1]) - str)/denom;

  returned_stress(0, 0) = eigvals[0] - n[0][1]*(dpm[1] + dpm[2]);
  returned_stress(1, 1) = returned_stress(2, 2) = str;
  return true;
}
bool
TensorMechanicsPlasticTensileMulti::returnTip(const std::vector<Real> & eigvals, const std::vector<std::vector<Real> > & n, std::vector<Real> & dpm, RankTwoTensor & returned_stress, const Real & intnl_old, const Real & initial_guess) const
{
  // The returned point is defined by f0=f1=f2=0.
  // that is, returned_stress = diag(str, str, str), where
  // str = tensile_strength(intnl),
  // where intnl = intnl_old + dpm[0] + dpm[1] + dpm[2]
  // The 3 plastic multipliers, dpm, are defiend by the normality condition
  //   eigvals - str = dpm[0]*n[0] + dpm[1]*n[1] + dpm[2]*n[2]
  // (Kuhn-Tucker demands that all dpm are non-negative, but we leave
  // that checking for later.)
  // This is a vector equation with solution (A):
  //   dpm[0] = triple(eigvals - str, n[1], n[2])/trip;
  //   dpm[1] = triple(eigvals - str, n[2], n[0])/trip;
  //   dpm[2] = triple(eigvals - str, n[0], n[1])/trip;
  // where trip = triple(n[0], n[1], n[2]).
  // By adding the three components of that solution together
  // we can get an equation for x = dpm[0] + dpm[1] + dpm[2],
  // and then our Newton-Raphson only involves one variable (x).
  // In the following, i specialise to the isotropic situation.

  Real x = initial_guess;
  Real denom = (n[0][0] - n[0][1])*(n[0][0] + 2*n[0][1]);
  Real str = tensile_strength(intnl_old + x);

  if (_strength.modelName().compare("Constant") != 0)
  {
    // Finding x is expensive.  Therefore
    // although x!=0 for Constant Hardening, solution (A)
    // demonstrates that we don't
    // actually need to know x to find the dpm for
    // Constant Hardening.
    //
    // However, for nontrivial Hardening, the following
    // is necessary
    Real eig = eigvals[0] + eigvals[1] + eigvals[2];
    Real bul = (n[0][0] + 2*n[0][1]);

    // and finally, the equation we want to solve is:
    // bul*x - eig + 3*str = 0
    // where str=tensile_strength(intnl_old + x)
    // and x = dpm[0] + dpm[1] + dpm[2]
    // (Note this has units of stress, so using _f_tol as a convergence check is reasonable.)
    // Use Netwon-Raphson with initial guess x = 0
    Real residual = bul*x - eig + 3*str;
    Real jacobian;
    unsigned int iter = 0;
    do {
      jacobian = bul + 3*dtensile_strength(intnl_old + x);
      x += -residual/jacobian;
      if (iter > _max_iters) // not converging
        return false;
      str = tensile_strength(intnl_old + x);
      residual = bul*x - eig + 3*str;
      iter ++;
    } while (residual*residual > _f_tol*_f_tol);
  }

  // The following is the solution (A) written above
  // (dpm[0] = triple(eigvals - str, n[1], n[2])/trip, etc)
  // in the isotropic situation
  dpm[0] = (n[0][0]*(eigvals[0] - str) + n[0][1]*(eigvals[0] - eigvals[1] - eigvals[2] + str))/denom;
  dpm[1] = (n[0][0]*(eigvals[1] - str) + n[0][1]*(eigvals[1] - eigvals[2] - eigvals[0] + str))/denom;
  dpm[2] = (n[0][0]*(eigvals[2] - str) + n[0][1]*(eigvals[2] - eigvals[0] - eigvals[1] + str))/denom;
  returned_stress(0, 0) = returned_stress(1, 1) = returned_stress(2, 2) = str;
  return true;
}
bool
TensorMechanicsPlasticMeanCapTC::returnMap(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
{
  if (!(_use_custom_returnMap))
    return TensorMechanicsPlasticModel::returnMap(trial_stress, intnl_old, E_ijkl, ep_plastic_tolerance, returned_stress, returned_intnl, dpm, delta_dp, yf, trial_stress_inadmissible);

  yf.resize(1);

  Real yf_orig = yieldFunction(trial_stress, intnl_old);

  yf[0] = yf_orig;

  if (yf_orig < _f_tol)
  {
    // the trial_stress is admissible
    trial_stress_inadmissible = false;
    return true;
  }

  trial_stress_inadmissible = true;

  // In the following we want to solve
  // trial_stress - stress = E_ijkl * dpm * r   ...... (1)
  // and either
  // stress.trace() = tensile_strength(intnl)  ...... (2a)
  // intnl = intnl_old + dpm                   ...... (3a)
  // or
  // stress.trace() = compressive_strength(intnl) ... (2b)
  // intnl = intnl_old - dpm                   ...... (3b)
  // The former (2a and 3a) are chosen if
  // trial_stress.trace() > tensile_strength(intnl_old)
  // while the latter (2b and 3b) are chosen if
  // trial_stress.trace() < compressive_strength(intnl_old)
  // The variables we want to solve for are stress, dpm
  // and intnl.  We do this using a Newton approach, starting
  // with stress=trial_stress and intnl=intnl_old and dpm=0
  const bool tensile_failure = (trial_stress.trace() >= tensile_strength(intnl_old));
  const Real dirn = (tensile_failure ? 1.0 : -1.0);

  RankTwoTensor n; // flow direction, which is E_ijkl * r
  for (unsigned i = 0; i < 3; ++i)
    for (unsigned j = 0; j < 3; ++j)
      for (unsigned k = 0; k < 3; ++k)
             n(i, j) += dirn * E_ijkl(i, j, k, k);
  const Real n_trace = n.trace();

  // Perform a Newton-Raphson to find dpm when
  // residual = trial_stress.trace() - tensile_strength(intnl) - dpm * n.trace()  [for tensile_failure=true]
  // or
  // residual = trial_stress.trace() - compressive_strength(intnl) - dpm * n.trace()  [for tensile_failure=false]
  Real trial_trace = trial_stress.trace();
  Real residual;
  Real jac;
  dpm[0] = 0;
  unsigned int iter = 0;
  do {
    if (tensile_failure)
    {
      residual = trial_trace - tensile_strength(intnl_old + dpm[0]) - dpm[0] * n_trace;
      jac = -dtensile_strength(intnl_old + dpm[0]) - n_trace;
    }
    else
    {
      residual = trial_trace - compressive_strength(intnl_old - dpm[0]) - dpm[0] * n_trace;
      jac = -dcompressive_strength(intnl_old - dpm[0]) - n_trace;
    }
    dpm[0] += -residual/jac;
    if (iter > _max_iters) // not converging
      return false;
    iter++;
  } while (residual*residual > _f_tol*_f_tol);

  // set the returned values
  yf[0] = 0;
  returned_intnl = intnl_old + dirn * dpm[0];
  returned_stress = trial_stress - dpm[0] * n;
  delta_dp = dpm[0] * dirn * returned_stress.dtrace();

  return true;
}
Real
TensorMechanicsPlasticWeakPlaneTensileN::dyieldFunction_dintnl(const RankTwoTensor & /*stress*/, const Real & intnl) const
{
  return -dtensile_strength(intnl);
}