// Jacobian for stress update algorithm
void
FiniteStrainPlasticMaterial::getJac(const RankTwoTensor & sig,
                                    const RankFourTensor & E_ijkl,
                                    Real flow_incr,
                                    RankFourTensor & dresid_dsig)
{
  RankTwoTensor sig_dev, df_dsig, flow_dirn;
  RankTwoTensor dfi_dft, dfi_dsig;
  RankFourTensor dft_dsig, dfd_dft, dfd_dsig;
  Real sig_eqv;
  Real f1, f2, f3;
  RankFourTensor temp;

  sig_dev = sig.deviatoric();
  sig_eqv = getSigEqv(sig);
  df_dsig = dyieldFunction_dstress(sig);
  flow_dirn = flowPotential(sig);

  f1 = 3.0 / (2.0 * sig_eqv);
  f2 = f1 / 3.0;
  f3 = 9.0 / (4.0 * Utility::pow<3>(sig_eqv));

  dft_dsig = f1 * _deltaMixed - f2 * _deltaOuter - f3 * sig_dev.outerProduct(sig_dev);

  dfd_dsig = dft_dsig;
  dresid_dsig = E_ijkl.invSymm() + dfd_dsig * flow_incr;
}
void
TensorMechanicsPlasticModel::dyieldFunction_dstressV(const RankTwoTensor & stress,
                                                     Real intnl,
                                                     std::vector<RankTwoTensor> & df_dstress) const
{
  df_dstress.assign(1, dyieldFunction_dstress(stress, intnl));
}
RankTwoTensor
TensorMechanicsPlasticOrthotropic::flowPotential(const RankTwoTensor & stress, Real intnl) const
{
  if (_associative)
  {
    const RankTwoTensor a = dyieldFunction_dstress(stress, intnl);
    return a / a.L2norm();
  }
  else
    return TensorMechanicsPlasticJ2::dyieldFunction_dstress(stress, intnl);
}
void
FiniteStrainPlasticBase::checkDerivatives()
{
  _console << "\n ++++++++++++++ \nChecking the derivatives\n";
  outputAndCheckDebugParameters();

  _console << "dyieldFunction_dstress.  Relative L2 norms.\n";
  std::vector<RankTwoTensor> df_dstress;
  std::vector<RankTwoTensor> fddf_dstress;
  dyieldFunction_dstress(_fspb_debug_stress, _fspb_debug_intnl, df_dstress);
  fddyieldFunction_dstress(_fspb_debug_stress, _fspb_debug_intnl, fddf_dstress);
  for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
  {
    _console << "alpha = " << alpha << " Relative L2norm = " << 2*(df_dstress[alpha] - fddf_dstress[alpha]).L2norm()/(df_dstress[alpha] + fddf_dstress[alpha]).L2norm() << "\n";
    _console << "Coded:\n";
    df_dstress[alpha].print();
    _console << "Finite difference:\n";
    fddf_dstress[alpha].print();
  }

  _console << "dflowPotential_dstress.  Relative L2 norms.\n";
  std::vector<RankFourTensor> dr_dstress;
  std::vector<RankFourTensor> fddr_dstress;
  dflowPotential_dstress(_fspb_debug_stress, _fspb_debug_intnl, dr_dstress);
  fddflowPotential_dstress(_fspb_debug_stress, _fspb_debug_intnl, fddr_dstress);
  for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
  {
    _console << "alpha = " << alpha << " Relative L2norm = " << 2*(dr_dstress[alpha] - fddr_dstress[alpha]).L2norm()/(dr_dstress[alpha] + fddr_dstress[alpha]).L2norm() << "\n";
    _console << "Coded:\n";
    dr_dstress[alpha].print();
    _console << "Finite difference:\n";
    fddr_dstress[alpha].print();
  }

  _console << "dflowPotential_dintnl.  Relative L2 norms.\n";
  std::vector<std::vector<RankTwoTensor> > dr_dintnl;
  std::vector<std::vector<RankTwoTensor> > fddr_dintnl;
  dflowPotential_dintnl(_fspb_debug_stress, _fspb_debug_intnl, dr_dintnl);
  fddflowPotential_dintnl(_fspb_debug_stress, _fspb_debug_intnl, fddr_dintnl);
  for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
  {
    for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a)
    {
      _console << "alpha = " << alpha << " a = " << a << " Relative L2norm = " << 2*(dr_dintnl[alpha][a] - fddr_dintnl[alpha][a]).L2norm()/(dr_dintnl[alpha][a] + fddr_dintnl[alpha][a]).L2norm() << "\n";
      _console << "Coded:\n";
      dr_dintnl[alpha][a].print();
      _console << "Finite difference:\n";
      fddr_dintnl[alpha][a].print();
    }
  }

}
void
MultiPlasticityLinearSystem::eliminateLinearDependence(const RankTwoTensor & stress, const std::vector<Real> & intnl, const std::vector<Real> & f, const std::vector<RankTwoTensor> & r, const std::vector<bool> & active, std::vector<bool> & deactivated_due_to_ld)
{
  deactivated_due_to_ld.resize(_num_surfaces, false);

  unsigned num_active = r.size();

  if (num_active <= 1)
    return;

  std::vector<double> s;
  int info = singularValuesOfR(r, s);
  if (info != 0)
    mooseError("In finding the SVD in the return-map algorithm, the PETSC LAPACK gesvd routine returned with error code " << info);

  // num_lin_dep are the number of linearly dependent
  // "r vectors", if num_active <= 6
  unsigned int num_lin_dep = 0;
  for (unsigned i = s.size() - 1 ; i > 0 ; --i)
    if (s[i] < _svd_tol*s[0])
      num_lin_dep++;
    else
      break;

  if (num_lin_dep == 0 && num_active <= 6)
    return;

  // From here on, some flow directions are linearly dependent

  // Find the signed "distance" of the current (stress, internal) configuration
  // from the yield surfaces.  This distance will not be precise, but
  // i want to preferentially deactivate yield surfaces that are close
  // to the current stress point.
  std::vector<RankTwoTensor> df_dstress;
  dyieldFunction_dstress(stress, intnl, active, df_dstress);

  typedef std::pair<Real, unsigned> pair_for_sorting;
  std::vector<pair_for_sorting> dist(num_active);
  for (unsigned i = 0 ; i < num_active ; ++i)
  {
    dist[i].first = f[i]/df_dstress[i].L2norm();
    dist[i].second = i;
  }
  std::sort(dist.begin(), dist.end()); // sorted in ascending order

  // There is a potential problem when we have equal f[i], for it can give oscillations
  bool equals_detected = false;
  for (unsigned i = 0 ; i < num_active - 1 ; ++i)
    if (std::abs(dist[i].first - dist[i + 1].first) < _min_f_tol)
    {
      equals_detected = true;
      dist[i].first += _min_f_tol*(MooseRandom::rand() - 0.5);
    }
  if (equals_detected)
    std::sort(dist.begin(), dist.end()); // sorted in ascending order


  std::vector<bool> scheduled_for_deactivation;
  scheduled_for_deactivation.assign(num_active, false);


  // In the following loop we go through all the flow directions, from
  // the one with the largest dist, to the one with the smallest dist,
  // adding them one-by-one into r_tmp.  Upon each addition we check
  // for linear-dependence.  if LD is found, we schedule the most
  // recently added flow direction for deactivation, and pop it
  // back off r_tmp
  unsigned current_yf;
  std::vector<RankTwoTensor> r_tmp(1);
  current_yf = dist[num_active - 1].second;
  r_tmp[0] = r[current_yf]; // the one with largest dist
  unsigned num_kept_active = 1;
  for (unsigned yf_to_try = 2 ; yf_to_try <= num_active ; ++yf_to_try)
  {
    current_yf = dist[num_active - yf_to_try].second;
    if (num_active == 2) // shortcut to we don't have to singularValuesOfR
      scheduled_for_deactivation[current_yf] = true;
    else if (num_kept_active >= 6) // shortcut to we don't have to singularValuesOfR: there can never be > 6 linearly-independent r vectors
      scheduled_for_deactivation[current_yf] = true;
    else
    {
      r_tmp.push_back(r[current_yf]);
      info = singularValuesOfR(r_tmp, s);
      if (s[s.size() - 1] < _svd_tol*s[0])
      {
        scheduled_for_deactivation[current_yf] = true;
        r_tmp.pop_back();
        num_lin_dep--;
      }
      else
        num_kept_active++;
      if (num_lin_dep == 0 && num_active <= 6)
        // have taken out all the vectors that were linearly dependent
        // so no point continuing
        break;
    }
  }

  unsigned int old_active_number = 0;
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
    if (active[surface])
    {
      if (scheduled_for_deactivation[old_active_number])
        deactivated_due_to_ld[surface] = true;
      old_active_number++;
    }
}
void
MultiPlasticityLinearSystem::calculateJacobian(const RankTwoTensor & stress, const std::vector<Real> & intnl, const std::vector<Real> & pm, const RankFourTensor & E_inv, const std::vector<bool> & active, const std::vector<bool> & deactivated_due_to_ld, std::vector<std::vector<Real> > & jac)
{
  // see comments at the start of .h file

  mooseAssert(intnl.size() == _num_models, "Size of intnl is " << intnl.size() << " which is incorrect in calculateJacobian");
  mooseAssert(pm.size() == _num_surfaces, "Size of pm is " << pm.size() << " which is incorrect in calculateJacobian");
  mooseAssert(active.size() == _num_surfaces, "Size of active is " << active.size() << " which is incorrect in calculateJacobian");
  mooseAssert(deactivated_due_to_ld.size() == _num_surfaces, "Size of deactivated_due_to_ld is " << deactivated_due_to_ld.size() << " which is incorrect in calculateJacobian");


  unsigned ind = 0;
  unsigned active_surface_ind = 0;


  std::vector<bool> active_surface(_num_surfaces); // active and not deactivated_due_to_ld
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
    active_surface[surface] = (active[surface] && !deactivated_due_to_ld[surface]);
  unsigned num_active_surface = 0;
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
    if (active_surface[surface])
      num_active_surface++;

  std::vector<bool> active_model(_num_models); // whether a model has surfaces that are active and not deactivated_due_to_ld
  for (unsigned model = 0 ; model < _num_models ; ++model)
    active_model[model] = anyActiveSurfaces(model, active_surface);

  unsigned num_active_model = 0;
  for (unsigned model = 0 ; model < _num_models ; ++model)
    if (active_model[model])
      num_active_model++;

  ind = 0;
  std::vector<unsigned int> active_model_index(_num_models);
  for (unsigned model = 0 ; model < _num_models ; ++model)
    if (active_model[model])
      active_model_index[model] = ind++;
    else
      active_model_index[model] = _num_models+1; // just a dummy, that will probably cause a crash if something goes wrong



  std::vector<RankTwoTensor> df_dstress;
  dyieldFunction_dstress(stress, intnl, active_surface, df_dstress);

  std::vector<Real> df_dintnl;
  dyieldFunction_dintnl(stress, intnl, active_surface, df_dintnl);

  std::vector<RankTwoTensor> r;
  flowPotential(stress, intnl, active, r);

  std::vector<RankFourTensor> dr_dstress;
  dflowPotential_dstress(stress, intnl, active, dr_dstress);

  std::vector<RankTwoTensor> dr_dintnl;
  dflowPotential_dintnl(stress, intnl, active, dr_dintnl);

  std::vector<Real> h;
  hardPotential(stress, intnl, active, h);

  std::vector<RankTwoTensor> dh_dstress;
  dhardPotential_dstress(stress, intnl, active, dh_dstress);

  std::vector<Real> dh_dintnl;
  dhardPotential_dintnl(stress, intnl, active, dh_dintnl);




  // d(epp)/dstress = sum_{active alpha} pm[alpha]*dr_dstress
  RankFourTensor depp_dstress;
  ind = 0;
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
    if (active[surface]) // includes deactivated_due_to_ld
      depp_dstress += pm[surface]*dr_dstress[ind++];
  depp_dstress += E_inv;

  // d(epp)/dpm_{active_surface_index} = r_{active_surface_index}
  std::vector<RankTwoTensor> depp_dpm;
  depp_dpm.resize(num_active_surface);
  ind = 0;
  active_surface_ind = 0;
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
  {
    if (active[surface])
    {
      if (active_surface[surface]) // do not include the deactived_due_to_ld, since their pm are not dofs in the NR
        depp_dpm[active_surface_ind++] = r[ind];
      ind++;
    }
  }

  // d(epp)/dintnl_{active_model_index} = sum(pm[asdf]*dr_dintnl[fdsa])
  std::vector<RankTwoTensor> depp_dintnl;
  depp_dintnl.assign(num_active_model, RankTwoTensor());
  ind = 0;
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
  {
    if (active[surface])
    {
      unsigned int model_num = modelNumber(surface);
      if (active_model[model_num]) // only include models with surfaces which are still active after deactivated_due_to_ld
        depp_dintnl[active_model_index[model_num]] += pm[surface]*dr_dintnl[ind];
      ind++;
    }
  }


  // df_dstress has been calculated above
  // df_dpm is always zero
  // df_dintnl has been calculated above, but only the active_surface+active_model stuff needs to be included in Jacobian: see below

  std::vector<RankTwoTensor> dic_dstress;
  dic_dstress.assign(num_active_model, RankTwoTensor());
  ind = 0;
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
  {
    if (active[surface])
    {
      unsigned int model_num = modelNumber(surface);
      if (active_model[model_num]) // only include ic for models with active_surface (ie, if model only contains deactivated_due_to_ld don't include it)
        dic_dstress[active_model_index[model_num]] += pm[surface]*dh_dstress[ind];
      ind++;
    }
  }


  std::vector<std::vector<Real> > dic_dpm;
  dic_dpm.resize(num_active_model);
  ind = 0;
  active_surface_ind = 0;
  for (unsigned model = 0 ; model < num_active_model ; ++model)
    dic_dpm[model].assign(num_active_surface, 0);
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
  {
    if (active[surface])
    {
      if (active_surface[surface]) // only take derivs wrt active-but-not-deactivated_due_to_ld pm
      {
        unsigned int model_num = modelNumber(surface);
        // if (active_model[model_num]) // do not need this check as if the surface has active_surface, the model must be deemed active!
          dic_dpm[active_model_index[model_num]][active_surface_ind] = h[ind];
        active_surface_ind++;
      }
      ind++;
    }
  }


  std::vector<std::vector<Real> > dic_dintnl;
  dic_dintnl.resize(num_active_model);
  for (unsigned model = 0 ; model < num_active_model ; ++model)
  {
    dic_dintnl[model].assign(num_active_model, 0);
    dic_dintnl[model][model] = 1; // deriv wrt internal parameter
  }
  ind = 0;
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
  {
    if (active[surface])
    {
      unsigned int model_num = modelNumber(surface);
      if (active_model[model_num]) // only the models that contain surfaces that are still active after deactivation_due_to_ld
        dic_dintnl[active_model_index[model_num]][active_model_index[model_num]] += pm[surface]*dh_dintnl[ind];
      ind++;
    }
  }



  unsigned int dim = 3;
  unsigned int system_size = 6 + num_active_surface + num_active_model; // "6" comes from symmeterizing epp
  jac.resize(system_size);
  for (unsigned i = 0 ; i < system_size ; ++i)
    jac[i].assign(system_size, 0);

  unsigned int row_num = 0;
  unsigned int col_num = 0;
  for (unsigned i = 0 ; i < dim ; ++i)
    for (unsigned j = 0 ; j <= i ; ++j)
    {
      for (unsigned k = 0 ; k < dim ; ++k)
        for (unsigned l = 0 ; l <= k ; ++l)
          jac[col_num][row_num++] = depp_dstress(i, j, k, l) + (k != l ? depp_dstress(i, j, l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i)
      for (unsigned surface = 0 ; surface < num_active_surface ; ++surface)
        jac[col_num][row_num++] = depp_dpm[surface](i, j);
      for (unsigned a = 0 ; a < num_active_model ; ++a)
        jac[col_num][row_num++] = depp_dintnl[a](i, j);
      row_num = 0;
      col_num++;
    }

  ind = 0;
  for (unsigned surface = 0 ; surface < _num_surfaces ; ++surface)
    if (active_surface[surface])
    {
      for (unsigned k = 0 ; k < dim ; ++k)
        for (unsigned l = 0 ; l <= k ; ++l)
        jac[col_num][row_num++] = df_dstress[ind](k, l) + (k != l ? df_dstress[ind](l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i)
      for (unsigned beta = 0 ; beta < num_active_surface ; ++beta)
        jac[col_num][row_num++] = 0; // df_dpm
      for (unsigned model = 0 ; model < _num_models ; ++model)
        if (active_model[model]) // only use df_dintnl for models in active_model
        {
          if (modelNumber(surface) == model)
            jac[col_num][row_num++] = df_dintnl[ind];
          else
            jac[col_num][row_num++] = 0;
        }
      ind++;
      row_num = 0;
      col_num++;
    }

  for (unsigned a = 0 ; a < num_active_model ; ++a)
  {
    for (unsigned k = 0 ; k < dim ; ++k)
      for (unsigned l = 0 ; l <= k ; ++l)
        jac[col_num][row_num++] = dic_dstress[a](k, l) + (k != l ? dic_dstress[a](l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i)
    for (unsigned alpha = 0 ; alpha < num_active_surface ; ++alpha)
      jac[col_num][row_num++] = dic_dpm[a][alpha];
    for (unsigned b = 0 ; b < num_active_model ; ++b)
      jac[col_num][row_num++] = dic_dintnl[a][b];
    row_num = 0;
    col_num++;
  }

  mooseAssert(col_num == system_size, "Incorrect filling of cols in Jacobian");
}
RankTwoTensor
TensorMechanicsPlasticTensile::flowPotential(const RankTwoTensor & stress, Real intnl) const
{
  // This plasticity is associative so
  return dyieldFunction_dstress(stress, intnl);
}
void
MultiPlasticityDebugger::checkDerivatives()
{
  Moose::err
      << "\n\n++++++++++++++++++++++++\nChecking the derivatives\n++++++++++++++++++++++++\n";
  outputAndCheckDebugParameters();

  std::vector<bool> act;
  act.assign(_num_surfaces, true);

  Moose::err << "\ndyieldFunction_dstress.  Relative L2 norms.\n";
  std::vector<RankTwoTensor> df_dstress;
  std::vector<RankTwoTensor> fddf_dstress;
  dyieldFunction_dstress(_fspb_debug_stress, _fspb_debug_intnl, act, df_dstress);
  fddyieldFunction_dstress(_fspb_debug_stress, _fspb_debug_intnl, fddf_dstress);
  for (unsigned surface = 0; surface < _num_surfaces; ++surface)
  {
    Moose::err << "surface = " << surface << " Relative L2norm = "
               << 2 * (df_dstress[surface] - fddf_dstress[surface]).L2norm() /
                      (df_dstress[surface] + fddf_dstress[surface]).L2norm()
               << "\n";
    Moose::err << "Coded:\n";
    df_dstress[surface].print();
    Moose::err << "Finite difference:\n";
    fddf_dstress[surface].print();
  }

  Moose::err << "\ndyieldFunction_dintnl.\n";
  std::vector<Real> df_dintnl;
  dyieldFunction_dintnl(_fspb_debug_stress, _fspb_debug_intnl, act, df_dintnl);
  Moose::err << "Coded:\n";
  for (unsigned surface = 0; surface < _num_surfaces; ++surface)
    Moose::err << df_dintnl[surface] << " ";
  Moose::err << "\n";
  std::vector<Real> fddf_dintnl;
  fddyieldFunction_dintnl(_fspb_debug_stress, _fspb_debug_intnl, fddf_dintnl);
  Moose::err << "Finite difference:\n";
  for (unsigned surface = 0; surface < _num_surfaces; ++surface)
    Moose::err << fddf_dintnl[surface] << " ";
  Moose::err << "\n";

  Moose::err << "\ndflowPotential_dstress.  Relative L2 norms.\n";
  std::vector<RankFourTensor> dr_dstress;
  std::vector<RankFourTensor> fddr_dstress;
  dflowPotential_dstress(_fspb_debug_stress, _fspb_debug_intnl, act, dr_dstress);
  fddflowPotential_dstress(_fspb_debug_stress, _fspb_debug_intnl, fddr_dstress);
  for (unsigned surface = 0; surface < _num_surfaces; ++surface)
  {
    Moose::err << "surface = " << surface << " Relative L2norm = "
               << 2 * (dr_dstress[surface] - fddr_dstress[surface]).L2norm() /
                      (dr_dstress[surface] + fddr_dstress[surface]).L2norm()
               << "\n";
    Moose::err << "Coded:\n";
    dr_dstress[surface].print();
    Moose::err << "Finite difference:\n";
    fddr_dstress[surface].print();
  }

  Moose::err << "\ndflowPotential_dintnl.  Relative L2 norms.\n";
  std::vector<RankTwoTensor> dr_dintnl;
  std::vector<RankTwoTensor> fddr_dintnl;
  dflowPotential_dintnl(_fspb_debug_stress, _fspb_debug_intnl, act, dr_dintnl);
  fddflowPotential_dintnl(_fspb_debug_stress, _fspb_debug_intnl, fddr_dintnl);
  for (unsigned surface = 0; surface < _num_surfaces; ++surface)
  {
    Moose::err << "surface = " << surface << " Relative L2norm = "
               << 2 * (dr_dintnl[surface] - fddr_dintnl[surface]).L2norm() /
                      (dr_dintnl[surface] + fddr_dintnl[surface]).L2norm()
               << "\n";
    Moose::err << "Coded:\n";
    dr_dintnl[surface].print();
    Moose::err << "Finite difference:\n";
    fddr_dintnl[surface].print();
  }
}
RankTwoTensor
TensorMechanicsPlasticJ2::flowPotential(const RankTwoTensor & stress, Real intnl) const
{
  return dyieldFunction_dstress(stress, intnl);
}
RankTwoTensor
FiniteStrainPlasticMaterial::flowPotential(const RankTwoTensor & sig)
{
  return dyieldFunction_dstress(sig); // this plasticity model assumes associative flow
}
/**
 * Implements the return-map algorithm via a Newton-Raphson process.
 * This idea is fully explained in Simo and Hughes "Computational
 * Inelasticity" Springer 1997, for instance, as well as many other
 * books on plasticity.
 * The basic idea is as follows.
 * Given: sig_old - the stress at the start of the "time step"
 *        plastic_strain_old - the plastic strain at the start of the "time step"
 *        eqvpstrain_old - equivalent plastic strain at the start of the "time step"
 *                         (In general we would be given some number of internal
 *                         parameters that the yield function depends upon.)
 *        delta_d - the prescribed strain increment for this "time step"
 * we want to determine the following parameters at the end of this "time step":
 *        sig - the stress
 *        plastic_strain - the plastic strain
 *        eqvpstrain - the equivalent plastic strain (again, in general, we would
 *                     have an arbitrary number of internal parameters).
 *
 * To determine these parameters, introduce
 *    the "yield function", f
 *    the "consistency parameter", flow_incr
 *    the "flow potential", flow_dirn_ij
 *    the "internal potential", internalPotential (in general there are as many internalPotential
 *        functions as there are internal parameters).
 * All three of f, flow_dirn_ij, and internalPotential, are functions of
 * sig and eqvpstrain.
 * To find sig, plastic_strain and eqvpstrain, we need to solve the following
 *   resid_ij = 0
 *   f = 0
 *   rep = 0
 * This is done by using Newton-Raphson.
 * There are 8 equations here: six from resid_ij=0 (more generally there are nine
 * but in this case resid is symmetric); one from f=0; one from rep=0 (more generally, for N
 * internal parameters there are N of these equations).
 *
 * resid_ij = flow_incr*flow_dirn_ij - (plastic_strain - plastic_strain_old)_ij
 *          = flow_incr*flow_dirn_ij - (E^{-1}(trial_stress - sig))_ij
 * Here trial_stress = E*(strain - plastic_strain_old)
 *      sig = E*(strain - plastic_strain)
 * Note: flow_dirn_ij is evaluated at sig and eqvpstrain (not the old values).
 *
 * f is the yield function, evaluated at sig and eqvpstrain
 *
 * rep = -flow_incr*internalPotential - (eqvpstrain - eqvpstrain_old)
 * Here internalPotential are evaluated at sig and eqvpstrain (not the old values).
 *
 * The Newton-Raphson procedure has sig, flow_incr, and eqvpstrain as its
 * variables.  Therefore we need the derivatives of resid_ij, f, and rep
 * with respect to these parameters
 *
 * In this associative J2 with isotropic hardening, things are a little more specialised.
 * (1) f = sqrt(3*s_ij*s_ij/2) - K(eqvpstrain)    (this is called "isotropic hardening")
 * (2) associativity means that flow_dirn_ij = df/d(sig_ij) = s_ij*sqrt(3/2/(s_ij*s_ij)), and
 *     this means that flow_dirn_ij*flow_dirn_ij = 3/2, so when resid_ij=0, we get
 *     (plastic_strain_dot)_ij*(plastic_strain_dot)_ij = (3/2)*flow_incr^2, where
 *     plastic_strain_dot = plastic_strain - plastic_strain_old
 * (3) The definition of equivalent plastic strain is through
 *     eqvpstrain_dot = sqrt(2*plastic_strain_dot_ij*plastic_strain_dot_ij/3), so
 *     using (2), we obtain eqvpstrain_dot = flow_incr, and this yields
 *     internalPotential = -1 in the "rep" equation.
 */
void
FiniteStrainPlasticMaterial::returnMap(const RankTwoTensor & sig_old,
                                       const Real eqvpstrain_old,
                                       const RankTwoTensor & plastic_strain_old,
                                       const RankTwoTensor & delta_d,
                                       const RankFourTensor & E_ijkl,
                                       RankTwoTensor & sig,
                                       Real & eqvpstrain,
                                       RankTwoTensor & plastic_strain)
{
  // the yield function, must be non-positive
  // Newton-Raphson sets this to zero if trial stress enters inadmissible region
  Real f;

  // the consistency parameter, must be non-negative
  // change in plastic strain in this timestep = flow_incr*flow_potential
  Real flow_incr = 0.0;

  // direction of flow defined by the potential
  RankTwoTensor flow_dirn;

  // Newton-Raphson sets this zero
  // resid_ij = flow_incr*flow_dirn_ij - (plastic_strain - plastic_strain_old)
  RankTwoTensor resid;

  // Newton-Raphson sets this zero
  // rep = -flow_incr*internalPotential - (eqvpstrain - eqvpstrain_old)
  Real rep;

  // change in the stress (sig) in a Newton-Raphson iteration
  RankTwoTensor ddsig;

  // change in the consistency parameter in a Newton-Raphson iteration
  Real dflow_incr = 0.0;

  // change in equivalent plastic strain in one Newton-Raphson iteration
  Real deqvpstrain = 0.0;

  // convenience variable that holds the change in plastic strain incurred during the return
  // delta_dp = plastic_strain - plastic_strain_old
  // delta_dp = E^{-1}*(trial_stress - sig), where trial_stress = E*(strain - plastic_strain_old)
  RankTwoTensor delta_dp;

  // d(yieldFunction)/d(stress)
  RankTwoTensor df_dsig;

  // d(resid_ij)/d(sigma_kl)
  RankFourTensor dr_dsig;

  // dr_dsig_inv_ijkl*dr_dsig_klmn = 0.5*(de_ij de_jn + de_ij + de_jm), where de_ij = 1 if i=j, but
  // zero otherwise
  RankFourTensor dr_dsig_inv;

  // d(yieldFunction)/d(eqvpstrain)
  Real fq;

  // yield stress at the start of this "time step" (ie, evaluated with
  // eqvpstrain_old).  It is held fixed during the Newton-Raphson return,
  // even if eqvpstrain != eqvpstrain_old.
  Real yield_stress;

  // measures of whether the Newton-Raphson process has converged
  Real err1, err2, err3;

  // number of Newton-Raphson iterations performed
  unsigned int iter = 0;

  // maximum number of Newton-Raphson iterations allowed
  unsigned int maxiter = 100;

  // plastic loading occurs if yieldFunction > toly
  Real toly = 1.0e-8;

  // Assume this strain increment does not induce any plasticity
  // This is the elastic-predictor
  sig = sig_old + E_ijkl * delta_d; // the trial stress
  eqvpstrain = eqvpstrain_old;
  plastic_strain = plastic_strain_old;

  yield_stress = getYieldStress(eqvpstrain); // yield stress at this equivalent plastic strain
  if (yieldFunction(sig, yield_stress) > toly)
  {
    // the sig just calculated is inadmissable.  We must return to the yield surface.
    // This is done iteratively, using a Newton-Raphson process.

    delta_dp.zero();

    sig = sig_old + E_ijkl * delta_d; // this is the elastic predictor

    flow_dirn = flowPotential(sig);

    resid = flow_dirn * flow_incr - delta_dp; // Residual 1 - refer Hughes Simo
    f = yieldFunction(sig, yield_stress);
    rep = -eqvpstrain + eqvpstrain_old - flow_incr * internalPotential(); // Residual 3 rep=0

    err1 = resid.L2norm();
    err2 = std::abs(f);
    err3 = std::abs(rep);

    while ((err1 > _rtol || err2 > _ftol || err3 > _eptol) &&
           iter < maxiter) // Stress update iteration (hardness fixed)
    {
      iter++;

      df_dsig = dyieldFunction_dstress(sig);
      getJac(sig, E_ijkl, flow_incr, dr_dsig);   // gets dr_dsig = d(resid_ij)/d(sig_kl)
      fq = dyieldFunction_dinternal(eqvpstrain); // d(f)/d(eqvpstrain)

      /**
       * The linear system is
       *   ( dr_dsig  flow_dirn  0  )( ddsig       )   ( - resid )
       *   ( df_dsig     0       fq )( dflow_incr  ) = ( - f     )
       *   (   0         1       -1 )( deqvpstrain )   ( - rep   )
       * The zeroes are: d(resid_ij)/d(eqvpstrain) = flow_dirn*d(df/d(sig_ij))/d(eqvpstrain) = 0
       * and df/d(flow_dirn) = 0  (this is always true, even for general hardening and
       * non-associative)
       * and d(rep)/d(sig_ij) = -flow_incr*d(internalPotential)/d(sig_ij) = 0
       */

      dr_dsig_inv = dr_dsig.invSymm();

      /**
       * Because of the zeroes and ones, the linear system is not impossible to
       * solve by hand.
       * NOTE: andy believes there was originally a sign-error in the next line.  The
       *       next line is unchanged, however andy's definition of fq is negative of
       *       the original definition of fq.  andy can't see any difference in any tests!
       */
      dflow_incr = (f - df_dsig.doubleContraction(dr_dsig_inv * resid) + fq * rep) /
                   (df_dsig.doubleContraction(dr_dsig_inv * flow_dirn) - fq);
      ddsig =
          dr_dsig_inv *
          (-resid -
           flow_dirn * dflow_incr); // from solving the top row of linear system, given dflow_incr
      deqvpstrain =
          rep + dflow_incr; // from solving the bottom row of linear system, given dflow_incr

      // update the variables
      flow_incr += dflow_incr;
      delta_dp -= E_ijkl.invSymm() * ddsig;
      sig += ddsig;
      eqvpstrain += deqvpstrain;

      // evaluate the RHS equations ready for next Newton-Raphson iteration
      flow_dirn = flowPotential(sig);
      resid = flow_dirn * flow_incr - delta_dp;
      f = yieldFunction(sig, yield_stress);
      rep = -eqvpstrain + eqvpstrain_old - flow_incr * internalPotential();

      err1 = resid.L2norm();
      err2 = std::abs(f);
      err3 = std::abs(rep);
    }

    if (iter >= maxiter)
      mooseError("Constitutive failure");

    plastic_strain += delta_dp;
  }
}
RankTwoTensor
TensorMechanicsPlasticSimpleTester::flowPotential(const RankTwoTensor & stress, const Real & intnl) const
{
  return dyieldFunction_dstress(stress, intnl);
}
void
FiniteStrainPlasticBase::calculateJacobian(const RankTwoTensor & stress, const std::vector<Real> & intnl, const std::vector<Real> & pm, const RankFourTensor & E_inv, std::vector<std::vector<Real> > & jac)
{
  // construct quantities used in the Newton-Raphson linear system
  // These should have been defined by the derived classes, if
  // they are different from the default functions given elsewhere
  // in this class
  std::vector<RankTwoTensor> df_dstress;
  dyieldFunction_dstress(stress, intnl, df_dstress);

  std::vector<std::vector<Real> > df_dintnl;
  dyieldFunction_dintnl(stress, intnl, df_dintnl);

  std::vector<RankTwoTensor> r;
  flowPotential(stress, intnl, r);

  std::vector<RankFourTensor> dr_dstress;
  dflowPotential_dstress(stress, intnl, dr_dstress);

  std::vector<std::vector<RankTwoTensor> > dr_dintnl;
  dflowPotential_dintnl(stress, intnl, dr_dintnl);

  std::vector<std::vector<Real> > h;
  hardPotential(stress, intnl, h);

  std::vector<std::vector<RankTwoTensor> > dh_dstress;
  dhardPotential_dstress(stress, intnl, dh_dstress);

  std::vector<std::vector<std::vector<Real> > > dh_dintnl;
  dhardPotential_dintnl(stress, intnl, dh_dintnl);


  // construct matrix entries
  // In the following
  // epp = pm*r - E_inv*(trial_stress - stress) = pm*r - delta_dp
  // f = yield function
  // ic = intnl - intnl_old + pm*h

  RankFourTensor depp_dstress;
  for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
    depp_dstress += pm[alpha]*dr_dstress[alpha];
  depp_dstress += E_inv;

  std::vector<RankTwoTensor> depp_dpm;
  depp_dpm.resize(numberOfYieldFunctions());
  for (unsigned alpha = 0; alpha < numberOfYieldFunctions() ; ++alpha)
    depp_dpm[alpha] = r[alpha];

  std::vector<RankTwoTensor> depp_dintnl;
  depp_dintnl.assign(numberOfInternalParameters(), RankTwoTensor());
  for (unsigned a = 0; a < numberOfInternalParameters() ; ++a)
    for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
      depp_dintnl[a] += pm[alpha]*dr_dintnl[alpha][a];

  // df_dstress has been calculated above
  // df_dpm is always zero
  // df_dintnl has been calculated above

  std::vector<RankTwoTensor> dic_dstress;
  dic_dstress.assign(numberOfInternalParameters(), RankTwoTensor());
  for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a)
    for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
      dic_dstress[a] += pm[alpha]*dh_dstress[a][alpha];

  std::vector<std::vector<Real> > dic_dpm;
  dic_dpm.resize(numberOfInternalParameters());
  for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a)
  {
    dic_dpm[a].resize(numberOfYieldFunctions());
    for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
      dic_dpm[a][alpha] = h[a][alpha];
  }

  std::vector<std::vector<Real> > dic_dintnl;
  dic_dintnl.resize(numberOfInternalParameters());
  for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a)
  {
    dic_dintnl[a].assign(numberOfInternalParameters(), 0);
    for (unsigned b = 0 ; b < numberOfInternalParameters() ; ++b)
      for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
        dic_dintnl[a][b] += pm[alpha]*dh_dintnl[a][alpha][b];
    dic_dintnl[a][a] += 1;
  }


  /**
   * now construct the Jacobian
   * It is:
   * ( depp_dstress depp_dpm depp_dintnl )
   * (  df_dstress       0      df_dintnl   )
   * ( dic_dstress    dic_dpm   dic_dintnl  )
   * For the "epp" terms, only the i>=j components are kept in the RHS, so only these terms are kept here too
   */

  unsigned int dim = 3;
  unsigned int system_size = 6 + numberOfYieldFunctions() + numberOfInternalParameters(); // "6" comes from symmeterizing epp
  jac.resize(system_size);
  for (unsigned i = 0 ; i < system_size ; ++i)
    jac[i].resize(system_size);

  unsigned int row_num = 0;
  unsigned int col_num = 0;
  for (unsigned i = 0 ; i < dim ; ++i)
    for (unsigned j = 0 ; j <= i ; ++j)
    {
      for (unsigned k = 0 ; k < dim ; ++k)
        for (unsigned l = 0 ; l <= k ; ++l)
          jac[col_num][row_num++] = depp_dstress(i, j, k, l) + (k != l ? depp_dstress(i, j, l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i)
      for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
        jac[col_num][row_num++] = depp_dpm[alpha](i, j);
      for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a)
        jac[col_num][row_num++] = depp_dintnl[a](i, j);
      row_num = 0;
      col_num++;
    }

  for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
  {
    for (unsigned k = 0 ; k < dim ; ++k)
      for (unsigned l = 0 ; l <= k ; ++l)
        jac[col_num][row_num++] = df_dstress[alpha](k, l) + (k != l ? df_dstress[alpha](l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i)
    for (unsigned beta = 0 ; beta < numberOfYieldFunctions() ; ++beta)
      jac[col_num][row_num++] = 0;
    for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a)
      jac[col_num][row_num++] = df_dintnl[alpha][a];
    row_num = 0;
    col_num++;
  }

  for (unsigned a = 0 ; a < numberOfInternalParameters() ; ++a)
  {
    for (unsigned k = 0 ; k < dim ; ++k)
      for (unsigned l = 0 ; l <= k ; ++l)
        jac[col_num][row_num++] = dic_dstress[a](k, l) + (k != l ? dic_dstress[a](l, k) : 0); // extra part is needed because i assume dstress(i, j) = dstress(j, i)
    for (unsigned alpha = 0 ; alpha < numberOfYieldFunctions() ; ++alpha)
      jac[col_num][row_num++] = dic_dpm[a][alpha];
    for (unsigned b = 0 ; b < numberOfInternalParameters() ; ++b)
      jac[col_num][row_num++] = dic_dintnl[a][b];
    row_num = 0;
    col_num++;
  }
}