コード例 #1
0
void 
BroydenOperator::runPostIterate( const NOX::Solver::Generic & solver)
{
  // Get and update using the solver object.

  if( solver.getNumIterations() > 0 )
  {
    // To consistently compute changes to the step and the yield, eg effects of
    // linesearch or other globalizations, we need to get and subtract the old and
    // new solution states and corresponding residuals 

    const Abstract::Group & oldSolnGrp = solver.getPreviousSolutionGroup();
    const Abstract::Group & solnGrp    = solver.getSolutionGroup();

    // Set the Step vector
    *workVec = solnGrp.getX();
    workVec->update(-1.0, oldSolnGrp.getX(), 1.0);

    setStepVector( *workVec );

    // Set the Yield vector
    *workVec = solnGrp.getF();
    workVec->update(-1.0, oldSolnGrp.getF(), 1.0);

    setYieldVector( *workVec );

    // Use of these updated vectors occurs when computeJacobian or computePreconditioner 
    // gets called
  }

  prePostOperator.runPostIterate( solver );

  return;
}
コード例 #2
0
NOX::StatusTest::StatusType 
N_NLS_NOX::Stagnation::checkStatus(const NOX::Solver::Generic& problem)
{
  status = NOX::StatusTest::Unconverged;

  // First time through we don't do anything but reset the counters
  int niters = problem.getNumIterations(); 
  if (niters == 0) {
    numSteps = 0;
    lastIteration = 0;
    convRate = 1.0;
    //minConvRate = 1.0;  // Don't reset this.  Xyce solver never does.
    normFInit = problem.getSolutionGroup().getNormF();
    return NOX::StatusTest::Unconverged;
  } 

  // Make sure we have not already counted the last nonlinear iteration.
  // This protects against multiple calls to checkStatus() in between 
  // nonlinear iterations.
  bool isCounted = false;
  if (niters == lastIteration) {
    isCounted = true;
  }
  else
    lastIteration = niters;

  // Compute the convergenc rate and set counter appropriately
  if (!isCounted) {

    convRate = problem.getSolutionGroup().getNormF() / 
               problem.getPreviousSolutionGroup().getNormF();
    
    if (fabs(convRate - 1.0) <= tolerance) {
      
      if ((numSteps == 0) || (convRate < minConvRate)) 
	minConvRate = convRate;
      
      ++numSteps ;
    }
    else
      numSteps = 0;
   
  }

  if (numSteps >= maxSteps) {
  
    double initConvRate = problem.getSolutionGroup().getNormF()/normFInit;

    if ((initConvRate <= 0.9) && (minConvRate <= 1.0)) {
      status = NOX::StatusTest::Converged;
    }
    else
      status = NOX::StatusTest::Failed;

  }

  return status;
}
コード例 #3
0
bool
NOX::Solver::TensorBased::implementGlobalStrategy(NOX::Abstract::Group& newGrp,
                      double& in_stepSize,
                      const NOX::Solver::Generic& s)
{
  bool ok;
  counter.incrementNumLineSearches();
  isNewtonDirection = false;
  NOX::Abstract::Vector& searchDirection = *tensorVecPtr;

  if ((counter.getNumLineSearches() == 1)  ||  (lsType == Newton))
  {
    isNewtonDirection = true;
    searchDirection = *newtonVecPtr;
  }

  // Do line search and compute new soln.
  if ((lsType != Dual) || (isNewtonDirection))
    ok = performLinesearch(newGrp, in_stepSize, searchDirection, s);
  else if (lsType == Dual)
  {
    double fTensor = 0.0;
    double fNew = 0.0;
    double tensorStep = 1.0;
    bool isTensorDescent = false;

    const Abstract::Group& oldGrp = s.getPreviousSolutionGroup();
    double fprime = slopeObj.computeSlope(searchDirection, oldGrp);

    // Backtrack along tensor direction if it is descent direction.
    if (fprime < 0)
    {
      ok = performLinesearch(newGrp, in_stepSize, searchDirection, s);
      assert(ok);
      fTensor = 0.5 * newGrp.getNormF() * newGrp.getNormF();
      tensorStep = in_stepSize;
      isTensorDescent = true;
    }

    // Backtrack along the Newton direction.
    ok = performLinesearch(newGrp, in_stepSize, *newtonVecPtr, s);
    fNew = 0.5 * newGrp.getNormF() * newGrp.getNormF();

    // If backtracking on the tensor step produced a better step, then use it.
    if (isTensorDescent  &&  (fTensor <= fNew))
    {
      newGrp.computeX(oldGrp, *tensorVecPtr, tensorStep);
      newGrp.computeF();
    }
  }

  return ok;
}
コード例 #4
0
bool NOX::Direction::Newton::compute(NOX::Abstract::Vector& dir, 
				     NOX::Abstract::Group& soln, 
				     const NOX::Solver::Generic& solver)
{
  NOX::Abstract::Group::ReturnType status;

  // Compute F at current solution.
  status = soln.computeF();
  if (status != NOX::Abstract::Group::Ok) 
    NOX::Direction::Newton::throwError("compute", "Unable to compute F");

  // Reset the linear solver tolerance.
  if (useAdjustableForcingTerm) {
    resetForcingTerm(soln, solver.getPreviousSolutionGroup(), 
		     solver.getNumIterations(), solver);
  }
  else { 
    if (utils->isPrintType(Utils::Details)) {
      utils->out() << "       CALCULATING FORCING TERM" << endl;
      utils->out() << "       Method: Constant" << endl;
      utils->out() << "       Forcing Term: " << eta_k << endl;
    }
  }

  // Compute Jacobian at current solution.
  status = soln.computeJacobian();
  if (status != NOX::Abstract::Group::Ok) 
    NOX::Direction::Newton::throwError("compute", "Unable to compute Jacobian");
  
  // Compute the Newton direction
  status = soln.computeNewton(paramsPtr->sublist("Newton").sublist("Linear Solver"));
  
  // It didn't converge, but maybe we can recover. 
  if ((status != NOX::Abstract::Group::Ok) &&
      (doRescue == false)) {
    NOX::Direction::Newton::throwError("compute", 
				       "Unable to solve Newton system");
  }
  else if ((status != NOX::Abstract::Group::Ok) &&
	   (doRescue == true)) {
    if (utils->isPrintType(NOX::Utils::Warning))
      utils->out() << "WARNING: NOX::Direction::Newton::compute() - Linear solve "
	   << "failed to achieve convergence - using the step anyway " 
	   << "since \"Rescue Bad Newton Solve\" is true " << endl;
  }

  // Set search direction.
  dir = soln.getNewton();

  return true;
}
NOX::StatusTest::StatusType
LOCA::Bifurcation::PitchforkBord::StatusTest::ParameterUpdateNorm::checkStatus(
                     const NOX::Solver::Generic& problem)
{
  // Get solution groups from solver
  const NOX::Abstract::Group& soln = problem.getSolutionGroup();
  const NOX::Abstract::Group& oldsoln = problem.getPreviousSolutionGroup();

  // Cast soln group to pitchfork group
  const LOCA::Bifurcation::PitchforkBord::ExtendedGroup* pfGroupPtr =
    dynamic_cast<const LOCA::Bifurcation::PitchforkBord::ExtendedGroup*>(&soln);

  // Check that group is a pitchfork group, return converged if not
  if (pfGroupPtr == NULL) {
    paramUpdateNorm = 0.0;
    return NOX::StatusTest::Converged;
  }

  // Get solution vectors
  const LOCA::Bifurcation::PitchforkBord::ExtendedVector& x =
    dynamic_cast<const LOCA::Bifurcation::PitchforkBord::ExtendedVector&>(soln.getX());
  const LOCA::Bifurcation::PitchforkBord::ExtendedVector& xold =
    dynamic_cast<const LOCA::Bifurcation::PitchforkBord::ExtendedVector&>(oldsoln.getX());

  // On the first iteration, the old and current solution are the same so
  // we should return the test as unconverged until there is a valid
  // old solution (i.e. the number of iterations is greater than zero).
  int niters = problem.getNumIterations();
  if (niters == 0)
  {
    paramUpdateNorm = 1.0e+12;
    status = NOX::StatusTest::Unconverged;
    return status;
  }

  paramUpdateNorm =
    fabs(x.getBifParam() - xold.getBifParam()) / (rtol*fabs(x.getBifParam()) + atol);

  if (paramUpdateNorm < tol)
    status = NOX::StatusTest::Converged;
  else
    status = NOX::StatusTest::Unconverged;

  return status;
}
コード例 #6
0
bool NOX::LineSearch::Backtrack::
compute(NOX::Abstract::Group& grp, double& step,
    const NOX::Abstract::Vector& dir,
    const NOX::Solver::Generic& s)
{
  const Abstract::Group& oldGrp = s.getPreviousSolutionGroup();
  double oldF = meritFunctionPtr->computef(oldGrp);
  double newF;
  bool isFailed = false;

  step = defaultStep;
  grp.computeX(oldGrp, dir, step);

  NOX::Abstract::Group::ReturnType rtype;

  rtype = grp.computeF();
  if (rtype != NOX::Abstract::Group::Ok)
  {
    utils->err() << "NOX::LineSearch::BackTrack::compute - Unable to compute F"
        << std::endl;
    throw "NOX Error";
  }

  newF = meritFunctionPtr->computef(grp);
  int nIters = 1;

  if (utils->isPrintType(Utils::InnerIteration))
  {
   utils->out() << "\n" << Utils::fill(72) << "\n"
           << "-- Backtrack Line Search -- \n";
  }

  NOX::StatusTest::FiniteValue checkNAN;

  while ( ((newF >= oldF) || (checkNAN.finiteNumberTest(newF) !=0))
     && (!isFailed))
  {

    if (utils->isPrintType(Utils::InnerIteration))
    {
      utils->out() << std::setw(3) << nIters << ":";
      utils->out() << " step = " << utils->sciformat(step);
      utils->out() << " old f = " << utils->sciformat(oldF);
      utils->out() << " new f = " << utils->sciformat(newF);
      utils->out() << std::endl;
    }

    nIters ++;
    step = step * reductionFactor;

    if ((step < minStep) || (nIters > maxIters))
    {
      isFailed = true;
      step = recoveryStep;
    }

    grp.computeX(oldGrp, dir, step);

    rtype = grp.computeF();
    if (rtype != NOX::Abstract::Group::Ok)
    {
      utils->err() << "NOX::LineSearch::BackTrack::compute - Unable to compute F" << std::endl;
      throw "NOX Error";
    }

    newF = meritFunctionPtr->computef(grp);
  }

  if (utils->isPrintType(Utils::InnerIteration))
  {
    utils->out() << std::setw(3) << nIters << ":";
    utils->out() << " step = " << utils->sciformat(step);
    utils->out() << " old f = " << utils->sciformat(oldF);
    utils->out() << " new f = " << utils->sciformat(newF);
    if (isFailed)
      utils->out() << " (USING RECOVERY STEP!)" << std::endl;
    else
      utils->out() << " (STEP ACCEPTED!)" << std::endl;
    utils->out() << Utils::fill(72) << "\n" << std::endl;
  }

  return (!isFailed);
}
コード例 #7
0
StatusType NormWRMS::
checkStatus(const NOX::Solver::Generic& problem,
        NOX::StatusTest::CheckType checkType)
{
  if (checkType == NOX::StatusTest::None) {
    status = Unevaluated;
    value = 1.0e+12;
    return status;
  }

  status = Unconverged;

  const Abstract::Group& soln = problem.getSolutionGroup();
  const Abstract::Group& oldsoln = problem.getPreviousSolutionGroup();
  const Abstract::Vector& x = soln.getScaledX();

  // On the first iteration, the old and current solution are the same so
  // we should return the test as unconverged until there is a valid
  // old solution (i.e. the number of iterations is greater than zero).
  int niters = problem.getNumIterations();
  if (niters == 0)
  {
    status = Unconverged;
    value = 1.0e+12;
    return status;
  }

  // **** Begin check for convergence criteria #1 ****

  // Create the working vectors if this is the first time this
  // operator is called.
  if (Teuchos::is_null(u))
    u = x.clone(NOX::ShapeCopy);
  if (Teuchos::is_null(v))
    v = x.clone(NOX::ShapeCopy);

  // Create the weighting vector u = RTOL |x| + ATOL
  // |x| is evaluated at the old time step
  v->abs(oldsoln.getScaledX());
  if (atolIsScalar)
  {
    u->init(1.0);
    u->update(rtol, *v, atol);
  }
  else
  {
    u->update(rtol, *v, 1.0, *atolVec, 0.0);
  }

  // v = 1/u (elementwise)
  v->reciprocal(*u);

  // u = x - oldx (i.e., the update)
  u->update(1.0, x, -1.0, oldsoln.getScaledX(), 0.0);

  // u = Cp * u @ v (where @ represents an elementwise multiply)
  u->scale(*v);

  // Turn off implicit scaling of norm if the vector supports it
  Teuchos::RCP<NOX::Abstract::ImplicitWeighting> iw_u;
  iw_u = Teuchos::rcp_dynamic_cast<NOX::Abstract::ImplicitWeighting>(u,false);
  bool saved_status = false;
  if (nonnull(iw_u) && m_disable_implicit_weighting) {
    saved_status = iw_u->getImplicitWeighting();
    iw_u->setImplicitWeighting(false);
  }

  // tmp = factor * sqrt (u * u / N)
  value = u->norm() * factor / sqrt(static_cast<double>(u->length()));

  // Set the implicit scaling back to original value
  if (nonnull(iw_u) && m_disable_implicit_weighting)
    iw_u->setImplicitWeighting(saved_status);

  StatusType status1 = Unconverged;
  if (value < tolerance)
    status1 = Converged;


  // **** Begin check for convergence criteria #2 ****
  StatusType status2 = Unconverged;

  // Determine if the Generic solver is a LineSearchBased solver
  // If it is not then return a "Converged" status
  const Solver::Generic* test = 0;
  test = dynamic_cast<const Solver::LineSearchBased*>(&problem);
  if (test == 0)
  {
    status2 = Converged;
  }
  else
  {
    printCriteria2Info = true;
    computedStepSize =
      (dynamic_cast<const Solver::LineSearchBased*>(&problem))->getStepSize();

    if (computedStepSize >= alpha)
      status2 = Converged;
  }

  // **** Begin check for convergence criteria #3 ****

  // First time through, make sure the output parameter list exists.
  // Since the list is const, a sublist call to a non-existent sublist
  // throws an error.  Therefore we have to check the existence of each
  // sublist before we call it.
  const Teuchos::ParameterList& p = problem.getList();
  if (niters == 1) {
    if (p.isSublist("Direction")) {
      if (p.sublist("Direction").isSublist("Newton")) {
    if (p.sublist("Direction").sublist("Newton").isSublist("Linear Solver")) {
      if (p.sublist("Direction").sublist("Newton").sublist("Linear Solver").isSublist("Output")) {

        const Teuchos::ParameterList& list = p.sublist("Direction").sublist("Newton").sublist("Linear Solver").sublist("Output");

        if (Teuchos::isParameterType<double>(list, "Achieved Tolerance")) {

          printCriteria3Info = true;


        }
      }
    }
      }
    }
  }

  StatusType status3 = Converged;
  if (printCriteria3Info) {
    achievedTol = const_cast<Teuchos::ParameterList&>(problem.getList()).
      sublist("Direction").sublist("Newton").sublist("Linear Solver").
      sublist("Output").get("Achieved Tolerance", -1.0);
    status3 = (achievedTol <= beta) ? Converged : Unconverged;
  }


  // Determine status of test
  if ((status1 == Converged) &&
      (status2 == Converged) &&
      (status3 == Converged))
    status = Converged;

  return status;
}
コード例 #8
0
NOX::StatusTest::StatusType 
LOCA::Bifurcation::TPBord::StatusTest::NullVectorNormWRMS::checkStatus(
					 const NOX::Solver::Generic& problem)
{
  // Get solution groups from solver
  const NOX::Abstract::Group& soln = problem.getSolutionGroup();
  const NOX::Abstract::Group& oldsoln = problem.getPreviousSolutionGroup();

  // Cast soln group to turning point group
  const LOCA::Bifurcation::TPBord::ExtendedGroup* tpGroupPtr = 
    dynamic_cast<const LOCA::Bifurcation::TPBord::ExtendedGroup*>(&soln);

  // Check that group is a turning point group, return converged if not
  if (tpGroupPtr == NULL) {
    normWRMS = 0.0;
    return NOX::StatusTest::Converged;
  }

  // Get solution vectors
  const LOCA::Bifurcation::TPBord::ExtendedVector& x = 
    dynamic_cast<const LOCA::Bifurcation::TPBord::ExtendedVector&>(soln.getX());
  const LOCA::Bifurcation::TPBord::ExtendedVector& xold = 
    dynamic_cast<const LOCA::Bifurcation::TPBord::ExtendedVector&>(oldsoln.getX());

  // Get null vectors
  const NOX::Abstract::Vector& y = x.getNullVec();
  const NOX::Abstract::Vector& yold = xold.getNullVec();

  // temporary vectors
  NOX::Abstract::Vector *u = y.clone(NOX::ShapeCopy);
  NOX::Abstract::Vector *v = yold.clone(NOX::ShapeCopy);
  
  // On the first iteration, the old and current solution are the same so
  // we should return the test as unconverged until there is a valid 
  // old solution (i.e. the number of iterations is greater than zero).
  int niters = problem.getNumIterations();
  if (niters == 0) 
  {
    normWRMS = 1.0e+12;
    status = NOX::StatusTest::Unconverged;
    return status;
  } 
  
  // Fill vector with 1's
  u->init(1.0);

  // Compute |y|
  v->abs(y);
  
  // Overwrite u with rtol*|y| + atol
  u->update(rtol, *v, atol);

  // Overwrite v with 1/(rtol*|y| + atol)
  v->reciprocal(*u);

  // Overwrite u with y-yold
  u->update(1.0, y, -1.0, yold, 0.0);

  // Overwrite u with (y-yold)/(rtol*|y| + atol)
  u->scale(*v);

  // Compute sqrt( (y-yold)/(rtol*|y| + atol) ) / sqrt(N)
  normWRMS = u->norm() / sqrt(static_cast<double>(u->length()));

  if (normWRMS < tol) 
    status = NOX::StatusTest::Converged;
  else
    status = NOX::StatusTest::Unconverged;

  delete u;
  delete v;
  
  return status;
}
コード例 #9
0
bool
NOX::Solver::TensorBased::performLinesearch(NOX::Abstract::Group& newSoln,
                        double& in_stepSize,
                        const NOX::Abstract::Vector& lsDir,
                        const NOX::Solver::Generic& s)
{
  if (print.isPrintType(NOX::Utils::InnerIteration))
  {
    utilsPtr->out() << "\n" << NOX::Utils::fill(72) << "\n";
    utilsPtr->out() << "-- Tensor Line Search (";
    if (lsType == Curvilinear)
      utilsPtr->out() << "Curvilinear";
    else if (lsType == Standard)
      utilsPtr->out() << "Standard";
    else if (lsType == FullStep)
      utilsPtr->out() << "Full Step";
    else if (lsType == Dual)
      utilsPtr->out() << "Dual";
    utilsPtr->out() << ") -- " << std::endl;
  }

  // Local variables
  bool isFailed = false;
  bool isAcceptable = false;
  bool isFirstPass = true;
  std::string message = "(STEP ACCEPTED!)";

  // Set counters
  int lsIterations = 1;

  // Get Old f
  const Abstract::Group& oldSoln = s.getPreviousSolutionGroup();
  double fOld = 0.5 * oldSoln.getNormF() * oldSoln.getNormF();

  // Compute first trial point and its function value
  in_stepSize = defaultStep;
  newSoln.computeX(oldSoln, lsDir, in_stepSize);
  newSoln.computeF();
  double fNew = 0.5 * newSoln.getNormF() * newSoln.getNormF();

  // Stop here if only using the full step
  if (lsType == FullStep)
  {
      print.printStep(lsIterations, in_stepSize, fOld, fNew, message);
      return (!isFailed);
  }

  // Compute directional derivative
  double fprime;
  if ((lsType == Curvilinear)  &&  !(isNewtonDirection))
    fprime = slopeObj.computeSlope(*newtonVecPtr, oldSoln);
  else
    fprime = slopeObj.computeSlope(lsDir, oldSoln);
  numJvMults++;  // computeSlope() has J*v inside of it

  // Compute the convergence criteria for the line search
  double threshold = fOld + alpha*in_stepSize*fprime;
  isAcceptable = (fNew < threshold);

  // Update counter and temporarily hold direction if a linesearch is needed
  if (!isAcceptable)
  {
    counter.incrementNumNonTrivialLineSearches();
    *tmpVecPtr = lsDir;
  }

  // Iterate until the trial point is accepted....
  while (!isAcceptable)
  {
    // Check for linesearch failure
    if (lsIterations > maxIters)
    {
      isFailed = true;
      message = "(FAILED - Max Iters)";
      break;
    }

    print.printStep(lsIterations, in_stepSize, fOld, fNew);

    // Is the full tensor step a descent direction?  If not, switch to Newton
    if (isFirstPass &&
    (!isNewtonDirection) &&
    (fprime >= 0) &&
    (lsType != Curvilinear) )
    {
      *tmpVecPtr = *newtonVecPtr;
      fprime = slopeObj.computeSlope(*tmpVecPtr, oldSoln);
      numJvMults++;

      if (utilsPtr->isPrintType(NOX::Utils::Details))
    utilsPtr->out() << "  Switching to Newton step.  New fprime = "
         << utilsPtr->sciformat(fprime, 6) << std::endl;
    }
    else
    {
      in_stepSize = selectLambda(fNew, fOld, fprime, in_stepSize);
    }

    isFirstPass = false;

    // Check for linesearch failure
    if (in_stepSize < minStep)
    {
      isFailed = true;
      message = "(FAILED - Min Step)";
      break;
    }

    // Update the number of linesearch iterations
    counter.incrementNumIterations();
    lsIterations ++;

    // Compute new trial point and its function value
    if ((lsType == Curvilinear) && !(isNewtonDirection))
    {
      computeCurvilinearStep(*tmpVecPtr, oldSoln, s, in_stepSize);
      // Note: oldSoln is needed above to get correct preconditioner
      newSoln.computeX(oldSoln, *tmpVecPtr, 1.0);
    }
    else
    {
      newSoln.computeX(oldSoln, *tmpVecPtr, in_stepSize);
    }
    newSoln.computeF();
    fNew = 0.5 * newSoln.getNormF() * newSoln.getNormF();

    // Recompute convergence criteria based on new step
    threshold = fOld + alpha*in_stepSize*fprime;
    isAcceptable = (fNew < threshold);
  }


  if (isFailed)
  {
    counter.incrementNumFailedLineSearches();

    if (recoveryStepType == Constant)
    {
      in_stepSize = recoveryStep;
      if (in_stepSize == 0.0)
      {
    newSoln = oldSoln;
    newSoln.computeF();
    fNew = fOld;
      }
      else
      {
    // Update the group using recovery step
    if ((lsType == Curvilinear) && !(isNewtonDirection))
    {
      computeCurvilinearStep(*tmpVecPtr, oldSoln, s, in_stepSize);
      // Note: oldSoln is needed above to get correct preconditioner
      newSoln.computeX(oldSoln, *tmpVecPtr, 1.0);
    }
    else
    {
      newSoln.computeX(oldSoln, *tmpVecPtr, in_stepSize);
    }
    //newSoln.computeX(oldSoln, lsDir, in_stepSize);
    newSoln.computeF();
    fNew = 0.5 * newSoln.getNormF() * newSoln.getNormF();
    message = "(USING RECOVERY STEP!)";
      }
    }
    else
      message = "(USING LAST STEP!)";
  }

  print.printStep(lsIterations, in_stepSize, fOld, fNew, message);
  counter.setValues(paramsPtr->sublist("Line Search"));

  return (!isFailed);
}
コード例 #10
0
bool
NOX::Solver::TensorBased::computeTensorDirection(NOX::Abstract::Group& soln,
                     const NOX::Solver::Generic& solver)
{
  NOX::Abstract::Group::ReturnType dir_status;

  Teuchos::ParameterList& linearParams = paramsPtr->sublist("Direction").
    sublist(paramsPtr->sublist("Direction").
        get("Method","Tensor")).
    sublist("Linear Solver");

  // Compute F at current solution.
  dir_status = soln.computeF();
  if (dir_status != NOX::Abstract::Group::Ok)
    throwError("computeTensorDirection", "Unable to compute F");

  // Compute Jacobian at current solution.
  dir_status = soln.computeJacobian();
  if (dir_status != NOX::Abstract::Group::Ok)
    throwError("computeTensorDirection", "Unable to compute Jacobian");

  // Begin processing for the tensor step, if necessary.
  double sDotS = 0.0;
  int tempVal1 = 0;
  if ((nIter > 0)  &&  (requestedBaseStep == TensorStep))
  {
    // Compute the tensor term s = x_{k-1} - x_k
    *sVecPtr = soln.getX();
    sVecPtr->update(1.0, solver.getPreviousSolutionGroup().getX(), -1.0);
    double normS = sVecPtr->norm();
    sDotS = normS * normS;

    // Form the tensor term a = (F_{k-1} - F_k - J*s) / (s^T s)^2
    soln.applyJacobian(*sVecPtr, *aVecPtr);
    numJvMults++;
    aVecPtr->update(1.0, solver.getPreviousSolutionGroup().getF(), -1.0);
    aVecPtr->update(-1.0, soln.getF(), 1.0);
    if (sDotS != 0)
      aVecPtr->scale(1.0 / (sDotS * sDotS));

    // Save old Newton step as initial guess to second system
    *tmpVecPtr = *newtonVecPtr;
    tmpVecPtr->scale(-1.0);   // Rewrite to avoid this?

    // Compute residual of linear system using initial guess...
    soln.applyJacobian(*tmpVecPtr, *residualVecPtr);
    numJvMults++;
    residualVecPtr->update(1.0, solver.getPreviousSolutionGroup().getF(),-1.0);
    double residualNorm = residualVecPtr->norm();

#if DEBUG_LEVEL > 0
    double tmpVecNorm = tmpVecPtr->norm();
    double residualNormRel = residualNorm /
      solver.getPreviousSolutionGroup().getNormF();
    if (utilsPtr->isPrintType(NOX::Utils::Details))
    {
      utilsPtr->out() << "  Norm of initial guess: " << utilsPtr->sciformat(tmpVecNorm, 6)
       << std::endl;
      utilsPtr->out() << "  initg norm of model residual =   "
       << utilsPtr->sciformat(residualNorm, 6) << " (abs)     "
       << utilsPtr->sciformat(residualNormRel, 6) << " (rel)" << std::endl;
    }
#endif

    // Save some parameters and use them later...
    double tol = linearParams.get("Tolerance", 1e-4);
    double relativeResidual = residualNorm /
      solver.getPreviousSolutionGroup().getNormF();

    // Decide whether to use initial guess...
    bool isInitialGuessGood = false;
#ifdef USE_INITIAL_GUESS_LOGIC
    if (relativeResidual < 1.0)
    {
      if (utilsPtr->isPrintType(NOX::Utils::Details))
    utilsPtr->out() << "  Initial guess is good..." << std::endl;
      isInitialGuessGood = true;
      // RPP - Brett please make sure the line below is correct.
      *tensorVecPtr = *tmpVecPtr;
      double newTol = tol / relativeResidual;
      if (newTol > 0.99)
    newTol = 0.99;  // force at least one iteration
      linearParams.set("Tolerance",  newTol);
      if (utilsPtr->isPrintType(NOX::Utils::Details))
    utilsPtr->out() << "  Setting tolerance to " << utilsPtr->sciformat(newTol,6) << std::endl;
    }
    else
#endif // USE_INITIAL_GUESS_LOGIC
    {
      //utilsPtr->out() << "  Initial guess is BAD... do not use!\n";
      isInitialGuessGood = false;
      *residualVecPtr = solver.getPreviousSolutionGroup().getF();
    }

    // Compute the term inv(J)*Fp....
    tmpVecPtr->init(0.0);
    dir_status = soln.applyJacobianInverse(linearParams, *residualVecPtr,
                       *tmpVecPtr);

    // If it didn't converge, maybe we can recover.
    if (dir_status != NOX::Abstract::Group::Ok)
    {
      if (doRescue == false)
    throwError("computeTensorDirection", "Unable to apply Jacobian inverse");
      else if ((doRescue == true) &&
           (utilsPtr->isPrintType(NOX::Utils::Warning)))
    utilsPtr->out() << "WARNING: NOX::Solver::TensorBased::computeTensorDirection() - "
         << "Linear solve failed to achieve convergence - "
         << "using the step anyway "
         << "since \"Rescue Bad Newton Solve\" is true." << std::endl;
    }

    // Continue processing
#ifdef USE_INITIAL_GUESS_LOGIC
    if (isInitialGuessGood)
    {
      tmpVecPtr->update(1.0, *tensorVecPtr, 1.0);
      linearParams.set("Tolerance",  tol);
    }
#endif

    // Save iteration count for comparison later
    if (linearParams.sublist("Output").
    isParameter("Number of Linear Iterations"))
      tempVal1 = linearParams.sublist("Output").
    get("Number of Linear Iterations",0);

#if DEBUG_LEVEL > 0
    // Compute residual of linear system with initial guess...
    soln.applyJacobian(*tmpVecPtr, *residualVecPtr);
    numJvMults++;
    residualVec.update(-1.0, solver.getPreviousSolutionGroup().getF(),1.0);
    double residualNorm2 = residualVec.norm();
    double residualNorm2Rel = residualNorm2 /
      solver.getPreviousSolutionGroup().getNormF();
    if (utilsPtr->isPrintType(NOX::Utils::Details))
      utilsPtr->out() << " jifp norm of model residual =   "
       << utilsPtr->sciformat(residualNorm2, 6) << " (abs)     "
       << utilsPtr->sciformat(residualNorm2Rel, 6) << " (rel)" << std::endl;
#endif
  }

  // Compute the Newton direction
  dir_status = soln.computeNewton(linearParams);

  // If it didn't converge, maybe we can recover.
  if (dir_status != NOX::Abstract::Group::Ok)
  {
    if (doRescue == false)
      throwError("computeTensorDirection", "Unable to apply Jacobian inverse");
    else if ((doRescue == true) &&
         (utilsPtr->isPrintType(NOX::Utils::Warning)))
      utilsPtr->out() << "WARNING: NOX::Solver::TensorBased::computeTensorDirection() - "
       << "Linear solve failed to achieve convergence - "
       << "using the step anyway "
       << "since \"Rescue Bad Newton Solve\" is true." << std::endl;
  }

  // Set Newton direction
  *newtonVecPtr = soln.getNewton();

  // Update counter
  int tempVal2 = 0;
  if (linearParams.sublist("Output").
      isParameter("Number of Linear Iterations"))
    tempVal2 = linearParams.sublist("Output").
      get("Number of Linear Iterations",0);
  numJ2vMults += (tempVal1 > tempVal2) ? tempVal1 : tempVal2;

#ifdef CHECK_RESIDUALS
  printDirectionInfo("newtonVec", *newtonVecPtr, soln, false);
#endif // CHECK_RESIDUALS

  // Continue processing the tensor step, if necessary
  if ((nIter > 0)  &&  (requestedBaseStep == TensorStep))
  {
    // Form the term inv(J)*a...  (note that a is not multiplied by 2)
    // The next line does not work in some implementations for some reason
    //tmpVec.update(1.0, newtonVec, -1.0, sVec, 1.0);
    tmpVecPtr->update(1.0, *newtonVecPtr, 1.0);
    tmpVecPtr->update(-1.0, *sVecPtr, 1.0);
    if (sDotS != 0.0)
      tmpVecPtr->scale( 1.0 / (sDotS * sDotS));

    // Calculate value of beta
    sTinvJF = -sVecPtr->innerProduct(*newtonVecPtr);
    sTinvJa = sVecPtr->innerProduct(*tmpVecPtr);
    double qval = 0;
    double lambdaBar = 1;
    beta = calculateBeta(sTinvJa, 1.0, sTinvJF, qval, lambdaBar);

    double sVecNorm = sVecPtr->norm();
    double aVecNorm = aVecPtr->norm();
    if (utilsPtr->isPrintType(NOX::Utils::Details))
    {
      utilsPtr->out() << " sTinvJF = " << utilsPtr->sciformat(sTinvJF, 6)
       << "  sTinvJa = " << utilsPtr->sciformat(sTinvJa, 6) << std::endl;
      utilsPtr->out() << " norm(s) = " << utilsPtr->sciformat(sVecNorm, 6)
       << "  norm(a) = " << utilsPtr->sciformat(aVecNorm, 6) << std::endl;
    }

    if (useModifiedMethod)
    {
      double alpha2 = lambdaBar;
      if (utilsPtr->isPrintType(NOX::Utils::Details))
    utilsPtr->out() << " Beta = " << utilsPtr->sciformat(beta, 6)
         << "  Alpha2 = " << utilsPtr->sciformat(alpha2, 6) << std::endl;
      if (alpha2 != 1.0)
      {
    if (utilsPtr->isPrintType(NOX::Utils::Details))
      utilsPtr->out() << "   *** Scaling tensor term a ***" << std::endl;
    aVecPtr->scale(alpha2);
    tmpVecPtr->scale(alpha2);
    sTinvJa *= alpha2;
    beta /= alpha2;
    lambdaBar = 1.0;
    qval = 0;
      }
    }

    // Form the tensor step
    tensorVecPtr->update(1.0, *newtonVecPtr, -beta*beta, *tmpVecPtr, 0.0);

#ifdef CHECK_RESIDUALS
    printDirectionInfo("tensorVec", *tensorVecPtr, soln, true);
#endif // CHECK_RESIDUALS
#if DEBUG_LEVEL > 0
    double sDotT = tensorVecPtr->innerProduct(sVec);
    if (utilsPtr->isPrintType(NOX::Utils::Details))
      utilsPtr->out() << "  Beta = " << utilsPtr->sciformat(beta, 6)
       << "  std = " << utilsPtr->sciformat(sDotT, 6)
       << "  qval = " << utilsPtr->sciformat(qval, 2)
       << "  lambdaBar = " << lambdaBar << std::endl;
#endif
  }
  else
    *tensorVecPtr = *newtonVecPtr;

  return true;
}