void
ReferenceResidualProblem::updateReferenceResidual()
{
  NonlinearSystemBase & nonlinear_sys = getNonlinearSystemBase();
  AuxiliarySystem & aux_sys = getAuxiliarySystem();
  System & s = nonlinear_sys.system();
  TransientExplicitSystem & as = aux_sys.sys();

  for (unsigned int i = 0; i < _solnVars.size(); ++i)
    _resid[i] = s.calculate_norm(nonlinear_sys.RHS(), _solnVars[i], DISCRETE_L2);

  for (unsigned int i = 0; i < _refResidVars.size(); ++i)
  {
    const Real refResidual =
        as.calculate_norm(*as.current_local_solution, _refResidVars[i], DISCRETE_L2);
    _refResid[i] = refResidual * _scaling_factors[i];
  }
}
void
ReferenceResidualProblem::initialSetup()
{
  NonlinearSystemBase & nonlinear_sys = getNonlinearSystemBase();
  AuxiliarySystem & aux_sys = getAuxiliarySystem();
  System & s = nonlinear_sys.system();
  TransientExplicitSystem & as = aux_sys.sys();

  if (_solnVarNames.size() > 0 && _solnVarNames.size() != s.n_vars())
    mooseError("In ReferenceResidualProblem, size of solution_variables (",
               _solnVarNames.size(),
               ") != number of variables in system (",
               s.n_vars(),
               ")");

  _solnVars.clear();
  for (unsigned int i = 0; i < _solnVarNames.size(); ++i)
  {
    bool foundMatch = false;
    for (unsigned int var_num = 0; var_num < s.n_vars(); var_num++)
    {
      if (_solnVarNames[i] == s.variable_name(var_num))
      {
        _solnVars.push_back(var_num);
        _resid.push_back(0.0);
        foundMatch = true;
        break;
      }
    }
    if (!foundMatch)
      mooseError("Could not find solution variable '", _solnVarNames[i], "' in system");
  }

  _refResidVars.clear();
  for (unsigned int i = 0; i < _refResidVarNames.size(); ++i)
  {
    bool foundMatch = false;
    for (unsigned int var_num = 0; var_num < as.n_vars(); var_num++)
    {
      if (_refResidVarNames[i] == as.variable_name(var_num))
      {
        _refResidVars.push_back(var_num);
        _refResid.push_back(0.0);
        foundMatch = true;
        break;
      }
    }
    if (!foundMatch)
      mooseError("Could not find variable '", _refResidVarNames[i], "' in auxiliary system");
  }

  const unsigned int size_solnVars = _solnVars.size();
  _scaling_factors.resize(size_solnVars);
  for (unsigned int i = 0; i < size_solnVars; ++i)
    if (nonlinear_sys.isScalarVariable(_solnVars[i]))
      _scaling_factors[i] = nonlinear_sys.getScalarVariable(0, _solnVars[i]).scalingFactor();
    else
      _scaling_factors[i] = nonlinear_sys.getVariable(/*tid*/ 0, _solnVars[i]).scalingFactor();

  FEProblemBase::initialSetup();
}
MooseNonlinearConvergenceReason
ReferenceResidualProblem::checkNonlinearConvergence(std::string & msg,
                                                    const PetscInt it,
                                                    const Real xnorm,
                                                    const Real snorm,
                                                    const Real fnorm,
                                                    const Real rtol,
                                                    const Real stol,
                                                    const Real abstol,
                                                    const PetscInt nfuncs,
                                                    const PetscInt max_funcs,
                                                    const PetscBool force_iteration,
                                                    const Real ref_resid,
                                                    const Real /*div_threshold*/)
{
  updateReferenceResidual();

  if (_solnVars.size() > 0)
  {
    _console << "Solution, reference convergence variable norms:" << std::endl;
    unsigned int maxwsv = 0;
    unsigned int maxwrv = 0;
    for (unsigned int i = 0; i < _solnVars.size(); ++i)
    {
      if (_solnVarNames[i].size() > maxwsv)
        maxwsv = _solnVarNames[i].size();
      if (_refResidVarNames[i].size() > maxwrv)
        maxwrv = _refResidVarNames[i].size();
    }

    for (unsigned int i = 0; i < _solnVars.size(); ++i)
      _console << std::setw(maxwsv + 2) << std::left << _solnVarNames[i] + ":" << _resid[i] << "  "
               << std::setw(maxwrv + 2) << _refResidVarNames[i] + ":" << _refResid[i] << std::endl;
  }

  NonlinearSystemBase & system = getNonlinearSystemBase();
  MooseNonlinearConvergenceReason reason = MOOSE_NONLINEAR_ITERATING;
  std::stringstream oss;

  if (fnorm != fnorm)
  {
    oss << "Failed to converge, function norm is NaN\n";
    reason = MOOSE_DIVERGED_FNORM_NAN;
  }
  else if (fnorm < abstol && (it || !force_iteration))
  {
    oss << "Converged due to function norm " << fnorm << " < " << abstol << std::endl;
    reason = MOOSE_CONVERGED_FNORM_ABS;
  }
  else if (nfuncs >= max_funcs)
  {
    oss << "Exceeded maximum number of function evaluations: " << nfuncs << " > " << max_funcs
        << std::endl;
    reason = MOOSE_DIVERGED_FUNCTION_COUNT;
  }

  if (it && !reason)
  {
    if (checkConvergenceIndividVars(fnorm, abstol, rtol, ref_resid))
    {
      if (_resid.size() > 0)
        oss << "Converged due to function norm "
            << " < "
            << " (relative tolerance) or (absolute tolerance) for all solution variables"
            << std::endl;
      else
        oss << "Converged due to function norm " << fnorm << " < "
            << " (relative tolerance)" << std::endl;
      reason = MOOSE_CONVERGED_FNORM_RELATIVE;
    }
    else if (it >= _accept_iters &&
             checkConvergenceIndividVars(
                 fnorm, abstol * _accept_mult, rtol * _accept_mult, ref_resid))
    {
      if (_resid.size() > 0)
        oss << "Converged due to function norm "
            << " < "
            << " (acceptable relative tolerance) or (acceptable absolute tolerance) for all "
               "solution variables"
            << std::endl;
      else
        oss << "Converged due to function norm " << fnorm << " < "
            << " (acceptable relative tolerance)" << std::endl;
      _console << "ACCEPTABLE" << std::endl;
      reason = MOOSE_CONVERGED_FNORM_RELATIVE;
    }

    else if (snorm < stol * xnorm)
    {
      oss << "Converged due to small update length: " << snorm << " < " << stol << " * " << xnorm
          << std::endl;
      reason = MOOSE_CONVERGED_SNORM_RELATIVE;
    }
  }

  system._last_nl_rnorm = fnorm;
  system._current_nl_its = static_cast<unsigned int>(it);

  msg = oss.str();
  return reason;
}
MooseNonlinearConvergenceReason
AugmentedLagrangianContactProblem::checkNonlinearConvergence(std::string & msg,
                                                             const PetscInt it,
                                                             const Real xnorm,
                                                             const Real snorm,
                                                             const Real fnorm,
                                                             const Real rtol,
                                                             const Real stol,
                                                             const Real abstol,
                                                             const PetscInt nfuncs,
                                                             const PetscInt /*max_funcs*/,
                                                             const PetscBool force_iteration,
                                                             const Real ref_resid,
                                                             const Real /*div_threshold*/)
{

  Real my_max_funcs = std::numeric_limits<int>::max();
  Real my_div_threshold = std::numeric_limits<Real>::max();

  MooseNonlinearConvergenceReason reason =
      ReferenceResidualProblem::checkNonlinearConvergence(msg,
                                                          it,
                                                          xnorm,
                                                          snorm,
                                                          fnorm,
                                                          rtol,
                                                          stol,
                                                          abstol,
                                                          nfuncs,
                                                          my_max_funcs,
                                                          force_iteration,
                                                          ref_resid,
                                                          my_div_threshold);

  _console << "Augmented Lagrangian contact iteration " << _num_lagmul_iterations << "\n";

  bool _augLM_repeat_step;

  if (reason > 0)
  {
    if (_num_lagmul_iterations < _max_lagmul_iters)
    {
      NonlinearSystemBase & nonlinear_sys = getNonlinearSystemBase();
      nonlinear_sys.update();

      const ConstraintWarehouse & constraints = nonlinear_sys.getConstraintWarehouse();

      std::map<std::pair<unsigned int, unsigned int>, PenetrationLocator *> * penetration_locators =
          NULL;

      bool displaced = false;
      _augLM_repeat_step = false;
      if (getDisplacedProblem() == NULL)
      {
        GeometricSearchData & geom_search_data = geomSearchData();
        penetration_locators = &geom_search_data._penetration_locators;
      }
      else
      {
        GeometricSearchData & displaced_geom_search_data = getDisplacedProblem()->geomSearchData();
        penetration_locators = &displaced_geom_search_data._penetration_locators;
        displaced = true;
      }

      for (const auto & it : *penetration_locators)
      {
        PenetrationLocator & pen_loc = *(it.second);

        BoundaryID slave_boundary = pen_loc._slave_boundary;

        if (constraints.hasActiveNodeFaceConstraints(slave_boundary, displaced))
        {
          const auto & ncs = constraints.getActiveNodeFaceConstraints(slave_boundary, displaced);

          for (const auto & nc : ncs)
          {
            if (std::dynamic_pointer_cast<MechanicalContactConstraint>(nc) == NULL)
              mooseError("AugmentedLagrangianContactProblem: dynamic cast of "
                         "MechanicalContactConstraint object failed.");

            if (!(std::dynamic_pointer_cast<MechanicalContactConstraint>(nc))
                     ->AugmentedLagrangianContactConverged())
            {
              (std::dynamic_pointer_cast<MechanicalContactConstraint>(nc))
                  ->updateAugmentedLagrangianMultiplier(false);
              _augLM_repeat_step = true;
              break;
            }
          }
        }
      }

      if (_augLM_repeat_step)
      {
        // force it to keep iterating
        reason = MOOSE_NONLINEAR_ITERATING;
        _console << "Augmented Lagrangian Multiplier needs updating." << std::endl;
        _num_lagmul_iterations++;
      }
      else
        _console << "Augmented Lagrangian contact constraint enforcement is satisfied."
                 << std::endl;
    }
    else
    {
      // maxed out
      _console << "Maximum Augmented Lagrangian contact iterations have been reached." << std::endl;
      reason = MOOSE_DIVERGED_FUNCTION_COUNT;
    }
  }

  return reason;
}