cantera/src/numerics/NonlinearSolver.cpp
2012-02-17 20:30:03 +00:00

4191 lines
160 KiB
C++

/**
*
* @file NonlinearSolver.cpp
*
* Damped Newton solver for 0D and 1D problems
*/
/*
* $Date$
* $Revision$
*/
/*
* Copywrite 2004 Sandia Corporation. Under the terms of Contract
* DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
* retains certain rights in this software.
* See file License.txt for licensing information.
*/
#include <limits>
#include "cantera/numerics/SquareMatrix.h"
#include "cantera/numerics/GeneralMatrix.h"
#include "cantera/numerics/NonlinearSolver.h"
#include "cantera/numerics/ctlapack.h"
#include "cantera/base/clockWC.h"
#include "cantera/base/vec_functions.h"
#include "cantera/base/mdp_allo.h"
#include <cfloat>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cmath>
//@{
#ifndef MAX
#define MAX(x,y) (( (x) > (y) ) ? (x) : (y))
#define MIN(x,y) (( (x) < (y) ) ? (x) : (y))
#endif
#ifndef CONSTD_DATA_PTR
#define CONSTD_DATA_PTR(x) (( const doublereal *) (&x[0]))
#endif
//@}
using namespace std;
namespace Cantera
{
//====================================================================================================================
//-----------------------------------------------------------
// Constants
//-----------------------------------------------------------
//! Dampfactor is the factor by which the damping factor is reduced by when a reduction in step length is warranted
const doublereal DampFactor = 4.0;
//! Number of damping steps that are carried out before the solution is deemed a failure
const int NDAMP = 7;
//====================================================================================================================
//! Print a line of a single repeated character string
/*!
* @param str Character string
* @param n Iteration length
*/
static void print_line(const char* str, int n)
{
for (int i = 0; i < n; i++) {
printf("%s", str);
}
printf("\n");
}
bool NonlinearSolver::s_TurnOffTiming(false);
#ifdef DEBUG_NUMJAC
bool NonlinearSolver::s_print_NumJac(true);
#else
bool NonlinearSolver::s_print_NumJac(false);
#endif
// Turn off printing of dogleg information
bool NonlinearSolver::s_print_DogLeg(false);
// Turn off solving the system twice and comparing the answer.
/*
* Turn this on if you want to compare the Hessian and Newton solve results.
*/
bool NonlinearSolver::s_doBothSolvesAndCompare(false);
// This toggle turns off the use of the Hessian when it is warranted by the condition number.
/*
* This is a debugging option.
*/
bool NonlinearSolver::s_alwaysAssumeNewtonGood(false);
//====================================================================================================================
// Default constructor
/*
* @param func Residual and jacobian evaluator function object
*/
NonlinearSolver::NonlinearSolver(ResidJacEval* func) :
m_func(func),
solnType_(NSOLN_TYPE_STEADY_STATE),
neq_(0),
m_ewt(0),
m_manualDeltaStepSet(0),
m_deltaStepMinimum(0),
m_y_n_curr(0),
m_ydot_n_curr(0),
m_y_nm1(0),
m_y_n_1(0),
m_ydot_n_1(0),
m_colScales(0),
m_rowScales(0),
m_rowWtScales(0),
m_resid(0),
m_wksp(0),
m_wksp_2(0),
m_residWts(0),
m_normResid_0(0.0),
m_normResid_Bound(0.0),
m_normResid_1(0.0),
m_normDeltaSoln_Newton(0.0),
m_normDeltaSoln_CP(0.0),
m_normResidTrial(0.0),
m_resid_scaled(false),
m_y_high_bounds(0),
m_y_low_bounds(0),
m_dampBound(1.0),
m_dampRes(1.0),
delta_t_n(-1.0),
m_nfe(0),
m_colScaling(0),
m_rowScaling(0),
m_numTotalLinearSolves(0),
m_numTotalNewtIts(0),
m_min_newt_its(0),
maxNewtIts_(100),
m_jacFormMethod(NSOLN_JAC_NUM),
m_nJacEval(0),
time_n(0.0),
m_matrixConditioning(0),
m_order(1),
rtol_(1.0E-3),
atolBase_(1.0E-10),
m_ydot_nm1(0),
atolk_(0),
userResidAtol_(0),
userResidRtol_(1.0E-3),
checkUserResidualTols_(0),
m_print_flag(0),
m_ScaleSolnNormToResNorm(0.001),
jacCopyPtr_(0),
HessianPtr_(0),
deltaX_CP_(0),
deltaX_Newton_(0),
residNorm2Cauchy_(0.0),
dogLegID_(0),
dogLegAlpha_(1.0),
RJd_norm_(0.0),
lambdaStar_(0.0),
Jd_(0),
deltaX_trust_(0),
norm_deltaX_trust_(0.0),
trustDelta_(1.0),
trustRegionInitializationMethod_(2),
trustRegionInitializationFactor_(1.0),
Nuu_(0.0),
dist_R0_(0.0),
dist_R1_(0.0),
dist_R2_(0.0),
dist_Total_(0.0),
JdJd_norm_(0.0),
normTrust_Newton_(0.0),
normTrust_CP_(0.0),
doDogLeg_(0),
doAffineSolve_(0) ,
CurrentTrustFactor_(1.0),
NextTrustFactor_(1.0),
ResidWtsReevaluated_(false),
ResidDecreaseSDExp_(0.0),
ResidDecreaseSD_(0.0),
ResidDecreaseNewtExp_(0.0),
ResidDecreaseNewt_(0.0)
{
neq_ = m_func->nEquations();
m_ewt.resize(neq_, rtol_);
m_deltaStepMinimum.resize(neq_, 0.001);
m_deltaStepMaximum.resize(neq_, 1.0E10);
m_y_n_curr.resize(neq_, 0.0);
m_ydot_n_curr.resize(neq_, 0.0);
m_y_nm1.resize(neq_, 0.0);
m_y_n_1.resize(neq_, 0.0);
m_ydot_n_1.resize(neq_, 0.0);
m_colScales.resize(neq_, 1.0);
m_rowScales.resize(neq_, 1.0);
m_rowWtScales.resize(neq_, 1.0);
m_resid.resize(neq_, 0.0);
m_wksp.resize(neq_, 0.0);
m_wksp_2.resize(neq_, 0.0);
m_residWts.resize(neq_, 0.0);
atolk_.resize(neq_, atolBase_);
deltaX_Newton_.resize(neq_, 0.0);
m_step_1.resize(neq_, 0.0);
m_y_n_1.resize(neq_, 0.0);
doublereal hb = std::numeric_limits<double>::max();
m_y_high_bounds.resize(neq_, hb);
m_y_low_bounds.resize(neq_, -hb);
for (size_t i = 0; i < neq_; i++) {
atolk_[i] = atolBase_;
m_ewt[i] = atolk_[i];
}
// jacCopyPtr_->resize(neq_, 0.0);
deltaX_CP_.resize(neq_, 0.0);
Jd_.resize(neq_, 0.0);
deltaX_trust_.resize(neq_, 1.0);
}
//====================================================================================================================
NonlinearSolver::NonlinearSolver(const NonlinearSolver& right) :
m_func(right.m_func),
solnType_(NSOLN_TYPE_STEADY_STATE),
neq_(0),
m_ewt(0),
m_manualDeltaStepSet(0),
m_deltaStepMinimum(0),
m_y_n_curr(0),
m_ydot_n_curr(0),
m_y_nm1(0),
m_y_n_1(0),
m_ydot_n_1(0),
m_step_1(0),
m_colScales(0),
m_rowScales(0),
m_rowWtScales(0),
m_resid(0),
m_wksp(0),
m_wksp_2(0),
m_residWts(0),
m_normResid_0(0.0),
m_normResid_Bound(0.0),
m_normResid_1(0.0),
m_normDeltaSoln_Newton(0.0),
m_normDeltaSoln_CP(0.0),
m_normResidTrial(0.0),
m_resid_scaled(false),
m_y_high_bounds(0),
m_y_low_bounds(0),
m_dampBound(1.0),
m_dampRes(1.0),
delta_t_n(-1.0),
m_nfe(0),
m_colScaling(0),
m_rowScaling(0),
m_numTotalLinearSolves(0),
m_numTotalNewtIts(0),
m_min_newt_its(0),
maxNewtIts_(100),
m_jacFormMethod(NSOLN_JAC_NUM),
m_nJacEval(0),
time_n(0.0),
m_matrixConditioning(0),
m_order(1),
rtol_(1.0E-3),
atolBase_(1.0E-10),
m_ydot_nm1(0),
atolk_(0),
userResidAtol_(0),
userResidRtol_(1.0E-3),
checkUserResidualTols_(0),
m_print_flag(0),
m_ScaleSolnNormToResNorm(0.001),
jacCopyPtr_(0),
HessianPtr_(0),
deltaX_CP_(0),
deltaX_Newton_(0),
residNorm2Cauchy_(0.0),
dogLegID_(0),
dogLegAlpha_(1.0),
RJd_norm_(0.0),
lambdaStar_(0.0),
Jd_(0),
deltaX_trust_(0),
norm_deltaX_trust_(0.0),
trustDelta_(1.0),
trustRegionInitializationMethod_(2),
trustRegionInitializationFactor_(1.0),
Nuu_(0.0),
dist_R0_(0.0),
dist_R1_(0.0),
dist_R2_(0.0),
dist_Total_(0.0),
JdJd_norm_(0.0),
normTrust_Newton_(0.0),
normTrust_CP_(0.0),
doDogLeg_(0),
doAffineSolve_(0),
CurrentTrustFactor_(1.0),
NextTrustFactor_(1.0),
ResidWtsReevaluated_(false),
ResidDecreaseSDExp_(0.0),
ResidDecreaseSD_(0.0),
ResidDecreaseNewtExp_(0.0),
ResidDecreaseNewt_(0.0)
{
*this =operator=(right);
}
//====================================================================================================================
NonlinearSolver::~NonlinearSolver()
{
if (jacCopyPtr_) {
delete jacCopyPtr_;
}
if (HessianPtr_) {
delete HessianPtr_;
}
}
//====================================================================================================================
NonlinearSolver& NonlinearSolver::operator=(const NonlinearSolver& right)
{
if (this == &right) {
return *this;
}
// rely on the ResidJacEval duplMyselfAsresidJacEval() function to
// create a deep copy
m_func = right.m_func->duplMyselfAsResidJacEval();
solnType_ = right.solnType_;
neq_ = right.neq_;
m_ewt = right.m_ewt;
m_manualDeltaStepSet = right.m_manualDeltaStepSet;
m_deltaStepMinimum = right.m_deltaStepMinimum;
m_y_n_curr = right.m_y_n_curr;
m_ydot_n_curr = right.m_ydot_n_curr;
m_y_nm1 = right.m_y_nm1;
m_y_n_1 = right.m_y_n_1;
m_ydot_n_1 = right.m_ydot_n_1;
m_step_1 = right.m_step_1;
m_colScales = right.m_colScales;
m_rowScales = right.m_rowScales;
m_rowWtScales = right.m_rowWtScales;
m_resid = right.m_resid;
m_wksp = right.m_wksp;
m_wksp_2 = right.m_wksp_2;
m_residWts = right.m_residWts;
m_normResid_0 = right.m_normResid_0;
m_normResid_Bound = right.m_normResid_Bound;
m_normResid_1 = right.m_normResid_1;
m_normDeltaSoln_Newton = right.m_normDeltaSoln_Newton;
m_normDeltaSoln_CP = right.m_normDeltaSoln_CP;
m_normResidTrial = right.m_normResidTrial;
m_resid_scaled = right.m_resid_scaled;
m_y_high_bounds = right.m_y_high_bounds;
m_y_low_bounds = right.m_y_low_bounds;
m_dampBound = right.m_dampBound;
m_dampRes = right.m_dampRes;
delta_t_n = right.delta_t_n;
m_nfe = right.m_nfe;
m_colScaling = right.m_colScaling;
m_rowScaling = right.m_rowScaling;
m_numTotalLinearSolves = right.m_numTotalLinearSolves;
m_numTotalNewtIts = right.m_numTotalNewtIts;
m_min_newt_its = right.m_min_newt_its;
maxNewtIts_ = right.maxNewtIts_;
m_jacFormMethod = right.m_jacFormMethod;
m_nJacEval = right.m_nJacEval;
time_n = right.time_n;
m_matrixConditioning = right.m_matrixConditioning;
m_order = right.m_order;
rtol_ = right.rtol_;
atolBase_ = right.atolBase_;
atolk_ = right.atolk_;
userResidAtol_ = right.userResidAtol_;
userResidRtol_ = right.userResidRtol_;
checkUserResidualTols_ = right.checkUserResidualTols_;
m_print_flag = right.m_print_flag;
m_ScaleSolnNormToResNorm = right.m_ScaleSolnNormToResNorm;
if (jacCopyPtr_) {
delete(jacCopyPtr_);
}
jacCopyPtr_ = (right.jacCopyPtr_)->duplMyselfAsGeneralMatrix();
if (HessianPtr_) {
delete(HessianPtr_);
}
HessianPtr_ = (right.HessianPtr_)->duplMyselfAsGeneralMatrix();
deltaX_CP_ = right.deltaX_CP_;
deltaX_Newton_ = right.deltaX_Newton_;
residNorm2Cauchy_ = right.residNorm2Cauchy_;
dogLegID_ = right.dogLegID_;
dogLegAlpha_ = right.dogLegAlpha_;
RJd_norm_ = right.RJd_norm_;
lambdaStar_ = right.lambdaStar_;
Jd_ = right.Jd_;
deltaX_trust_ = right.deltaX_trust_;
norm_deltaX_trust_ = right.norm_deltaX_trust_;
trustDelta_ = right.trustDelta_;
trustRegionInitializationMethod_ = right.trustRegionInitializationMethod_;
trustRegionInitializationFactor_ = right.trustRegionInitializationFactor_;
Nuu_ = right.Nuu_;
dist_R0_ = right.dist_R0_;
dist_R1_ = right.dist_R1_;
dist_R2_ = right.dist_R2_;
dist_Total_ = right.dist_Total_;
JdJd_norm_ = right.JdJd_norm_;
normTrust_Newton_ = right.normTrust_Newton_;
normTrust_CP_ = right.normTrust_CP_;
doDogLeg_ = right.doDogLeg_;
doAffineSolve_ = right.doAffineSolve_;
CurrentTrustFactor_ = right.CurrentTrustFactor_;
NextTrustFactor_ = right.NextTrustFactor_;
ResidWtsReevaluated_ = right.ResidWtsReevaluated_;
ResidDecreaseSDExp_ = right.ResidDecreaseSDExp_;
ResidDecreaseSD_ = right.ResidDecreaseSD_;
ResidDecreaseNewtExp_ = right.ResidDecreaseNewtExp_;
ResidDecreaseNewt_ = right.ResidDecreaseNewt_;
return *this;
}
//====================================================================================================================
// Create solution weights for convergence criteria
/*
* We create soln weights from the following formula
*
* wt[i] = rtol * abs(y[i]) + atol[i]
*
* The program always assumes that atol is specific
* to the solution component
*
* @param y vector of the current solution values
*/
void NonlinearSolver::createSolnWeights(const doublereal* const y)
{
for (size_t i = 0; i < neq_; i++) {
m_ewt[i] = rtol_ * fabs(y[i]) + atolk_[i];
}
}
//====================================================================================================================
// set bounds constraints for all variables in the problem
/*
*
* @param y_low_bounds Vector of lower bounds
* @param y_high_bounds Vector of high bounds
*/
void NonlinearSolver::setBoundsConstraints(const doublereal* const y_low_bounds,
const doublereal* const y_high_bounds)
{
for (size_t i = 0; i < neq_; i++) {
m_y_low_bounds[i] = y_low_bounds[i];
m_y_high_bounds[i] = y_high_bounds[i];
}
}
//====================================================================================================================
void NonlinearSolver::setSolverScheme(int doDogLeg, int doAffineSolve)
{
doDogLeg_ = doDogLeg;
doAffineSolve_ = doAffineSolve;
}
//====================================================================================================================
std::vector<doublereal> & NonlinearSolver::lowBoundsConstraintVector()
{
return m_y_low_bounds;
}
//====================================================================================================================
std::vector<doublereal> & NonlinearSolver::highBoundsConstraintVector()
{
return m_y_high_bounds;
}
//====================================================================================================================
// L2 norm of the delta of the solution vector
/*
* calculate the norm of the solution vector. This will
* involve the column scaling of the matrix
*
* The third argument has a default of false. However,
* if true, then a table of the largest values is printed
* out to standard output.
*
* @param delta_y Vector to take the norm of
* @param title Optional title to be printed out
* @param printLargest int indicating how many specific lines should be printed out
* @param dampFactor Current value of the damping factor. Defaults to 1.
* only used for printout out a table.
*/
doublereal NonlinearSolver::solnErrorNorm(const doublereal* const delta_y, const char* title, int printLargest,
const doublereal dampFactor) const
{
doublereal sum_norm = 0.0, error;
for (size_t i = 0; i < neq_; i++) {
error = delta_y[i] / m_ewt[i];
sum_norm += (error * error);
}
sum_norm = sqrt(sum_norm / neq_);
if (printLargest) {
if ((printLargest == 1) || (m_print_flag >= 4 && m_print_flag <= 5)) {
printf("\t\t solnErrorNorm(): ");
if (title) {
printf("%s", title);
} else {
printf(" Delta soln norm ");
}
printf(" = %-11.4E\n", sum_norm);
} else if (m_print_flag >= 6) {
const int num_entries = printLargest;
printf("\t\t ");
print_line("-", 90);
printf("\t\t solnErrorNorm(): ");
if (title) {
printf("%s", title);
} else {
printf(" Delta soln norm ");
}
printf(" = %-11.4E\n", sum_norm);
doublereal dmax1, normContrib;
int j;
std::vector<size_t> imax(num_entries, npos);
printf("\t\t Printout of Largest Contributors: (damp = %g)\n", dampFactor);
printf("\t\t I weightdeltaY/sqtN| deltaY "
"ysolnOld ysolnNew Soln_Weights\n");
printf("\t\t ");
print_line("-", 88);
for (int jnum = 0; jnum < num_entries; jnum++) {
dmax1 = -1.0;
for (size_t i = 0; i < neq_; i++) {
bool used = false;
for (j = 0; j < jnum; j++) {
if (imax[j] == i) {
used = true;
}
}
if (!used) {
error = delta_y[i] / m_ewt[i];
normContrib = sqrt(error * error);
if (normContrib > dmax1) {
imax[jnum] = i;
dmax1 = normContrib;
}
}
}
size_t i = imax[jnum];
if (i != npos) {
error = delta_y[i] / m_ewt[i];
normContrib = sqrt(error * error);
printf("\t\t %4d %12.4e | %12.4e %12.4e %12.4e %12.4e\n", i, normContrib/sqrt((double)neq_),
delta_y[i], m_y_n_curr[i], m_y_n_curr[i] + dampFactor * delta_y[i], m_ewt[i]);
}
}
printf("\t\t ");
print_line("-", 90);
}
}
return sum_norm;
}
//====================================================================================================================
/*
* L2 Norm of the residual
*
* The second argument has a default of false. However,
* if true, then a table of the largest values is printed
* out to standard output.
*/
doublereal NonlinearSolver::residErrorNorm(const doublereal* const resid, const char* title, const int printLargest,
const doublereal* const y) const
{
doublereal sum_norm = 0.0, error;
for (size_t i = 0; i < neq_; i++) {
#ifdef DEBUG_HKM
mdp::checkFinite(resid[i]);
#endif
error = resid[i] / m_residWts[i];
#ifdef DEBUG_HKM
mdp::checkFinite(error);
#endif
sum_norm += (error * error);
}
sum_norm = sqrt(sum_norm / neq_);
#ifdef DEBUG_HKM
mdp::checkFinite(sum_norm);
#endif
if (printLargest) {
const int num_entries = printLargest;
doublereal dmax1, normContrib;
int j;
std::vector<size_t> imax(num_entries, npos);
if (m_print_flag >= 4 && m_print_flag <= 5) {
printf("\t\t residErrorNorm():");
if (title) {
printf(" %s ", title);
} else {
printf(" residual L2 norm ");
}
printf("= %12.4E\n", sum_norm);
}
if (m_print_flag >= 6) {
printf("\t\t ");
print_line("-", 90);
printf("\t\t residErrorNorm(): ");
if (title) {
printf(" %s ", title);
} else {
printf(" residual L2 norm ");
}
printf("= %12.4E\n", sum_norm);
printf("\t\t Printout of Largest Contributors to norm:\n");
printf("\t\t I |Resid/ResWt| UnsclRes ResWt | y_curr\n");
printf("\t\t ");
print_line("-", 88);
for (int jnum = 0; jnum < num_entries; jnum++) {
dmax1 = -1.0;
for (size_t i = 0; i < neq_; i++) {
bool used = false;
for (j = 0; j < jnum; j++) {
if (imax[j] == i) {
used = true;
}
}
if (!used) {
error = resid[i] / m_residWts[i];
normContrib = sqrt(error * error);
if (normContrib > dmax1) {
imax[jnum] = i;
dmax1 = normContrib;
}
}
}
size_t i = imax[jnum];
if (i != npos) {
error = resid[i] / m_residWts[i];
normContrib = sqrt(error * error);
printf("\t\t %4d %12.4e %12.4e %12.4e | %12.4e\n", i, normContrib, resid[i], m_residWts[i], y[i]);
}
}
printf("\t\t ");
print_line("-", 90);
}
}
return sum_norm;
}
//====================================================================================================================
// Set the column scaling that are used for the inversion of the matrix
/*
* There are three ways to do this.
*
* The first method is to set the bool useColScaling to true, leaving the scaling factors unset.
* Then, the column scales will be set to the solution error weighting factors. This has the
* effect of ensuring that all delta variables will have the same order of magnitude at convergence
* end.
*
* The second way is the explicity set the column factors in the second argument of this function call.
*
* The final way to input the scales is to override the ResidJacEval member function call,
*
* calcSolnScales(double time_n, const double *m_y_n_curr, const double *m_y_nm1, double *m_colScales)
*
* Overriding this function call will trump all other ways to specify the column scaling factors.
*
* @param useColScaling Turn this on if you want to use column scaling in the calculations
* @param scaleFactors A vector of doubles that specifies the column factors.
*/
void NonlinearSolver::setColumnScaling(bool useColScaling, const double* const scaleFactors)
{
if (useColScaling) {
if (scaleFactors) {
m_colScaling = 2;
for (size_t i = 0; i < neq_; i++) {
m_colScales[i] = scaleFactors[i];
if (m_colScales[i] <= 1.0E-200) {
throw CanteraError("NonlinearSolver::setColumnScaling() ERROR", "Bad column scale factor");
}
}
} else {
m_colScaling = 1;
}
} else {
m_colScaling = 0;
}
}
//====================================================================================================================
// Set the rowscaling that are used for the inversion of the matrix
/*
* Row scaling is set here. Right now the row scaling is set internally in the code.
*
* @param useRowScaling Turn row scaling on or off.
*/
void NonlinearSolver::setRowScaling(bool useRowScaling)
{
m_rowScaling = useRowScaling;
}
//====================================================================================================================
/*
* calcColumnScales():
*
* Set the column scaling vector at the current time
*/
void NonlinearSolver::calcColumnScales()
{
if (m_colScaling == 1) {
for (size_t i = 0; i < neq_; i++) {
m_colScales[i] = m_ewt[i];
}
} else {
for (size_t i = 0; i < neq_; i++) {
m_colScales[i] = 1.0;
}
}
if (m_colScaling) {
m_func->calcSolnScales(time_n, DATA_PTR(m_y_n_curr), DATA_PTR(m_y_nm1), DATA_PTR(m_colScales));
}
}
//====================================================================================================================
// Compute the current residual
/*
* @param time_curr Value of the time
* @param typeCalc Type of the calculation
* @param y_curr Current value of the solution vector
* @param ydot_curr Current value of the time derivative of the solution vector
*
* @return Returns a flag to indicate that operation is successful.
* 1 Means a successful operation
* -0 or neg value Means an unsuccessful operation
*/
int NonlinearSolver::doResidualCalc(const doublereal time_curr, const int typeCalc, const doublereal* const y_curr,
const doublereal* const ydot_curr, const ResidEval_Type_Enum evalType) const
{
int retn = m_func->evalResidNJ(time_curr, delta_t_n, y_curr, ydot_curr, DATA_PTR(m_resid), evalType);
m_nfe++;
m_resid_scaled = false;
return retn;
}
//====================================================================================================================
// Scale the matrix
/*
* @param jac Jacobian
* @param y_comm Current value of the solution vector
* @param ydot_comm Current value of the time derivative of the solution vector
* @param time_curr current value of the time
*/
void NonlinearSolver::scaleMatrix(GeneralMatrix& jac, doublereal* const y_comm, doublereal* const ydot_comm,
doublereal time_curr, int num_newt_its)
{
size_t irow, jcol;
size_t ku, kl;
size_t ivec[2];
jac.nRowsAndStruct(ivec);
double* colP_j;
/*
* Column scaling -> We scale the columns of the Jacobian
* by the nominal important change in the solution vector
*/
if (m_colScaling) {
if (!jac.factored()) {
if (jac.matrixType_ == 0) {
/*
* Go get new scales -> Took this out of this inner loop.
* Needs to be done at a larger scale.
*/
// setColumnScales();
/*
* Scale the new Jacobian
*/
doublereal* jptr = &(*(jac.begin()));
for (jcol = 0; jcol < neq_; jcol++) {
for (irow = 0; irow < neq_; irow++) {
*jptr *= m_colScales[jcol];
jptr++;
}
}
} else if (jac.matrixType_ == 1) {
kl = ivec[0];
ku = ivec[1];
for (jcol = 0; jcol < neq_; jcol++) {
colP_j = (doublereal*) jac.ptrColumn(jcol);
for (irow = jcol - ku; irow <= jcol + kl; irow++) {
if (irow >= 0 && irow < neq_) {
colP_j[kl + ku + irow - jcol] *= m_colScales[jcol];
}
}
}
}
}
}
/*
* row sum scaling -> Note, this is an unequivical success
* at keeping the small numbers well balanced and nonnegative.
*/
if (! jac.factored()) {
/*
* Ok, this is ugly. jac.begin() returns an vector<double> iterator
* to the first data location.
* Then &(*()) reverts it to a doublereal *.
*/
doublereal* jptr = &(*(jac.begin()));
for (irow = 0; irow < neq_; irow++) {
m_rowScales[irow] = 0.0;
m_rowWtScales[irow] = 0.0;
}
if (jac.matrixType_ == 0) {
for (jcol = 0; jcol < neq_; jcol++) {
for (irow = 0; irow < neq_; irow++) {
if (m_rowScaling) {
m_rowScales[irow] += fabs(*jptr);
}
if (m_colScaling) {
// This is needed in order to mitgate the change in J_ij carried out just above this loop.
// Alternatively, we could move this loop up to the top
m_rowWtScales[irow] += fabs(*jptr) * m_ewt[jcol] / m_colScales[jcol];
} else {
m_rowWtScales[irow] += fabs(*jptr) * m_ewt[jcol];
}
jptr++;
}
}
} else if (jac.matrixType_ == 1) {
kl = ivec[0];
ku = ivec[1];
for (jcol = 0; jcol < neq_; jcol++) {
colP_j = (doublereal*) jac.ptrColumn(jcol);
for (irow = jcol - ku; irow <= jcol + kl; irow++) {
if (irow >= 0 && irow < neq_) {
double vv = fabs(colP_j[kl + ku + irow - jcol]);
if (m_rowScaling) {
m_rowScales[irow] += vv;
}
if (m_colScaling) {
// This is needed in order to mitgate the change in J_ij carried out just above this loop.
// Alternatively, we could move this loop up to the top
m_rowWtScales[irow] += vv * m_ewt[jcol] / m_colScales[jcol];
} else {
m_rowWtScales[irow] += vv * m_ewt[jcol];
}
}
}
}
}
if (m_rowScaling) {
for (irow = 0; irow < neq_; irow++) {
m_rowScales[irow] = 1.0/m_rowScales[irow];
}
} else {
for (irow = 0; irow < neq_; irow++) {
m_rowScales[irow] = 1.0;
}
}
// What we have defined is a maximum value that the residual can be and still pass.
// This isn't sufficient.
if (m_rowScaling) {
if (jac.matrixType_ == 0) {
jptr = &(*(jac.begin()));
for (jcol = 0; jcol < neq_; jcol++) {
for (irow = 0; irow < neq_; irow++) {
*jptr *= m_rowScales[irow];
jptr++;
}
}
} else if (jac.matrixType_ == 1) {
kl = ivec[0];
ku = ivec[1];
for (jcol = 0; jcol < neq_; jcol++) {
colP_j = (doublereal*) jac.ptrColumn(jcol);
for (irow = jcol - ku; irow <= jcol + kl; irow++) {
if (irow >= 0 && irow < neq_) {
colP_j[kl + ku + irow - jcol] *= m_rowScales[irow];
}
}
}
}
}
if (num_newt_its % 5 == 1) {
computeResidWts();
}
}
}
//====================================================================================================================
// Calculate the scaling factor for translating residual norms into solution norms.
/*
* This routine calls computeResidWts() a couple of times in the calculation of m_ScaleSolnNormToResNorm.
* A more sophisticated routine may do more with signs to get a better value. Perhaps, a series of calculations
* with different signs attached may be in order. Then, m_ScaleSolnNormToResNorm would be calculated
* as the minimum of a series of calculations.
*/
void NonlinearSolver::calcSolnToResNormVector()
{
if (! jacCopyPtr_->factored()) {
if (checkUserResidualTols_ != 1) {
doublereal sum = 0.0;
for (size_t irow = 0; irow < neq_; irow++) {
m_residWts[irow] = m_rowWtScales[irow] / neq_;
sum += m_residWts[irow];
}
sum /= neq_;
for (size_t irow = 0; irow < neq_; irow++) {
m_residWts[irow] = (m_residWts[irow] + atolBase_ * atolBase_ * sum);
}
if (checkUserResidualTols_ == 2) {
for (size_t irow = 0; irow < neq_; irow++) {
m_residWts[irow] = MIN(m_residWts[irow], userResidAtol_[irow] + userResidRtol_ * m_rowWtScales[irow] / neq_);
}
}
} else {
for (size_t irow = 0; irow < neq_; irow++) {
m_residWts[irow] = userResidAtol_[irow] + userResidRtol_ * m_rowWtScales[irow] / neq_;
}
}
for (size_t irow = 0; irow < neq_; irow++) {
m_wksp[irow] = 0.0;
}
doublereal* jptr = &(jacCopyPtr_->operator()(0,0));
for (size_t jcol = 0; jcol < neq_; jcol++) {
for (size_t irow = 0; irow < neq_; irow++) {
m_wksp[irow] += (*jptr) * m_ewt[jcol];
jptr++;
}
}
doublereal resNormOld = 0.0;
doublereal error;
for (size_t irow = 0; irow < neq_; irow++) {
error = m_wksp[irow] / m_residWts[irow];
resNormOld += error * error;
}
resNormOld = sqrt(resNormOld / neq_);
if (resNormOld > 0.0) {
m_ScaleSolnNormToResNorm = resNormOld;
}
if (m_ScaleSolnNormToResNorm < 1.0E-8) {
m_ScaleSolnNormToResNorm = 1.0E-8;
}
// Recalculate the residual weights now that we know the value of m_ScaleSolnNormToResNorm
computeResidWts();
} else {
throw CanteraError("NonlinearSolver::calcSolnToResNormVector()" , "Logic error");
}
}
//====================================================================================================================
// Compute the undamped Newton step based on the current jacobian and an input rhs
/*
* Compute the undamped Newton step. The residual function is
* evaluated at the current time, t_n, at the current values of the
* solution vector, m_y_n_curr, and the solution time derivative, m_ydot_n.
* The Jacobian is not recomputed.
*
* A factored jacobian is reused, if available. If a factored jacobian
* is not available, then the jacobian is factored. Before factoring,
* the jacobian is row and column-scaled. Column scaling is not
* recomputed. The row scales are recomputed here, after column
* scaling has been implemented.
*/
int NonlinearSolver::doNewtonSolve(const doublereal time_curr, const doublereal* const y_curr,
const doublereal* const ydot_curr, doublereal* const delta_y,
GeneralMatrix& jac)
{
// multiply the residual by -1
if (m_rowScaling && !m_resid_scaled) {
for (size_t n = 0; n < neq_; n++) {
delta_y[n] = -m_rowScales[n] * m_resid[n];
}
m_resid_scaled = true;
} else {
for (size_t n = 0; n < neq_; n++) {
delta_y[n] = -m_resid[n];
}
}
/*
* Solve the system -> This also involves inverting the
* matrix
*/
int info = jac.solve(DATA_PTR(delta_y));
/*
* reverse the column scaling if there was any.
*/
if (m_colScaling) {
for (size_t irow = 0; irow < neq_; irow++) {
delta_y[irow] = delta_y[irow] * m_colScales[irow];
}
}
#ifdef DEBUG_JAC
if (printJacContributions) {
for (size_t iNum = 0; iNum < numRows; iNum++) {
if (iNum > 0) {
focusRow++;
}
doublereal dsum = 0.0;
vector_fp& Jdata = jacBack.data();
doublereal dRow = Jdata[neq_ * focusRow + focusRow];
printf("\n Details on delta_Y for row %d \n", focusRow);
printf(" Value before = %15.5e, delta = %15.5e,"
"value after = %15.5e\n", y_curr[focusRow],
delta_y[focusRow], y_curr[focusRow] + delta_y[focusRow]);
if (!freshJac) {
printf(" Old Jacobian\n");
}
printf(" col delta_y aij "
"contrib \n");
printf("--------------------------------------------------"
"---------------------------------------------\n");
printf(" Res(%d) %15.5e %15.5e %15.5e (Res = %g)\n",
focusRow, delta_y[focusRow],
dRow, RRow[iNum] / dRow, RRow[iNum]);
dsum += RRow[iNum] / dRow;
for (size_t ii = 0; ii < neq_; ii++) {
if (ii != focusRow) {
doublereal aij = Jdata[neq_ * ii + focusRow];
doublereal contrib = aij * delta_y[ii] * (-1.0) / dRow;
dsum += contrib;
if (fabs(contrib) > Pcutoff) {
printf("%6d %15.5e %15.5e %15.5e\n", ii,
delta_y[ii] , aij, contrib);
}
}
}
printf("--------------------------------------------------"
"---------------------------------------------\n");
printf(" %15.5e %15.5e\n",
delta_y[focusRow], dsum);
}
}
#endif
m_numTotalLinearSolves++;
m_numLocalLinearSolves++;
return info;
}
//====================================================================================================================
// Compute the newton step, either by direct newton's or by solving a close problem that is represented
// by a Hessian (
/*
* This is algorith A.6.5.1 in Dennis / Schnabel
*
* Compute the QR decomposition
*
* Notes on banded Hessian solve:
* The matrix for jT j has a larger band width. Both the top and bottom band widths
* are doubled, going from KU to KU+KL and KL to KU+KL in size. This is not an impossible increase in cost, but
* has to be considered.
*/
int NonlinearSolver::doAffineNewtonSolve(const doublereal* const y_curr, const doublereal* const ydot_curr,
doublereal* const delta_y, GeneralMatrix& jac)
{
bool newtonGood = true;
doublereal* delyNewton = 0;
// We can default to QR here ( or not )
jac.useFactorAlgorithm(1);
int useQR = jac.factorAlgorithm();
// multiplyl the residual by -1
// Scale the residual if there is row scaling. Note, the matrix has already been scaled
if (m_rowScaling && !m_resid_scaled) {
for (size_t n = 0; n < neq_; n++) {
delta_y[n] = -m_rowScales[n] * m_resid[n];
}
m_resid_scaled = true;
} else {
for (size_t n = 0; n < neq_; n++) {
delta_y[n] = -m_resid[n];
}
}
// Factor the matrix using a standard Newton solve
m_conditionNumber = 1.0E300;
int info = 0;
if (!jac.factored()) {
if (useQR) {
info = jac.factorQR();
} else {
info = jac.factor();
}
}
/*
* Find the condition number of the matrix
* If we have failed to factor, we will fall back to calculating and factoring a modified Hessian
*/
if (info == 0) {
doublereal rcond = 0.0;
if (useQR) {
rcond = jac.rcondQR();
} else {
doublereal a1norm = jac.oneNorm();
rcond = jac.rcond(a1norm);
}
if (rcond > 0.0) {
m_conditionNumber = 1.0 / rcond;
}
} else {
m_conditionNumber = 1.0E300;
newtonGood = false;
if (m_print_flag >= 1) {
printf("\t\t doAffineNewtonSolve: ");
if (useQR) {
printf("factorQR()");
} else {
printf("factor()");
}
printf(" returned with info = %d, indicating a zero row or column\n", info);
}
}
bool doHessian = false;
if (s_doBothSolvesAndCompare) {
doHessian = true;
}
if (m_conditionNumber < 1.0E7) {
if (m_print_flag >= 4) {
printf("\t\t doAffineNewtonSolve: Condition number = %g during regular solve\n", m_conditionNumber);
}
/*
* Solve the system -> This also involves inverting the matrix
*/
int info = jac.solve(DATA_PTR(delta_y));
if (info) {
if (m_print_flag >= 2) {
printf("\t\t doAffineNewtonSolve() ERROR: QRSolve returned INFO = %d. Switching to Hessian solve\n", info);
}
doHessian = true;
newtonGood = false;
}
/*
* reverse the column scaling if there was any on a successful solve
*/
if (m_colScaling) {
for (size_t irow = 0; irow < neq_; irow++) {
delta_y[irow] = delta_y[irow] * m_colScales[irow];
}
}
} else {
if (jac.matrixType_ == 1) {
newtonGood = true;
if (m_print_flag >= 3) {
printf("\t\t doAffineNewtonSolve() WARNING: Condition number too large, %g, But Banded Hessian solve "
"not implemented yet \n", m_conditionNumber);
}
} else {
doHessian = true;
newtonGood = false;
if (m_print_flag >= 3) {
printf("\t\t doAffineNewtonSolve() WARNING: Condition number too large, %g. Doing a Hessian solve \n", m_conditionNumber);
}
}
}
if (doHessian) {
// Store the old value for later comparison
delyNewton = mdp::mdp_alloc_dbl_1((int) neq_, MDP_DBL_NOINIT);
for (size_t irow = 0; irow < neq_; irow++) {
delyNewton[irow] = delta_y[irow];
}
// Get memory if not done before
if (HessianPtr_ == 0) {
HessianPtr_ = jac.duplMyselfAsGeneralMatrix();
}
/*
* Calculate the symmetric Hessian
*/
GeneralMatrix& hessian = *HessianPtr_;
GeneralMatrix& jacCopy = *jacCopyPtr_;
hessian.zero();
if (m_rowScaling) {
for (size_t i = 0; i < neq_; i++) {
for (size_t j = i; j < neq_; j++) {
for (size_t k = 0; k < neq_; k++) {
hessian(i,j) += jacCopy(k,i) * jacCopy(k,j) * m_rowScales[k] * m_rowScales[k];
}
hessian(j,i) = hessian(i,j);
}
}
} else {
for (size_t i = 0; i < neq_; i++) {
for (size_t j = i; j < neq_; j++) {
for (size_t k = 0; k < neq_; k++) {
hessian(i,j) += jacCopy(k,i) * jacCopy(k,j);
}
hessian(j,i) = hessian(i,j);
}
}
}
/*
* Calculate the matrix norm of the Hessian
*/
doublereal hnorm = 0.0;
doublereal hcol = 0.0;
if (m_colScaling) {
for (size_t i = 0; i < neq_; i++) {
for (size_t j = i; j < neq_; j++) {
hcol += fabs(hessian(j,i)) * m_colScales[j];
}
for (size_t j = i+1; j < neq_; j++) {
hcol += fabs(hessian(i,j)) * m_colScales[j];
}
hcol *= m_colScales[i];
if (hcol > hnorm) {
hnorm = hcol;
}
}
} else {
for (size_t i = 0; i < neq_; i++) {
for (size_t j = i; j < neq_; j++) {
hcol += fabs(hessian(j,i));
}
for (size_t j = i+1; j < neq_; j++) {
hcol += fabs(hessian(i,j));
}
if (hcol > hnorm) {
hnorm = hcol;
}
}
}
/*
* Add junk to the Hessian diagonal
* -> Note, testing indicates that this will get too big for ill-conditioned systems.
*/
hcol = sqrt(1.0*neq_) * 1.0E-7 * hnorm;
#ifdef DEBUG_HKM_NOT
if (hcol > 1.0) {
hcol = 1.0E1;
}
#endif
if (m_colScaling) {
for (size_t i = 0; i < neq_; i++) {
hessian(i,i) += hcol / (m_colScales[i] * m_colScales[i]);
}
} else {
for (size_t i = 0; i < neq_; i++) {
hessian(i,i) += hcol;
}
}
/*
* Factor the Hessian
*/
int info;
ct_dpotrf(ctlapack::UpperTriangular, neq_, &(*(HessianPtr_->begin())), neq_, info);
if (info) {
if (m_print_flag >= 2) {
printf("\t\t doAffineNewtonSolve() ERROR: Hessian isn't positive definate DPOTRF returned INFO = %d\n", info);
}
return info;
}
// doublereal *JTF = delta_y;
doublereal* delyH = mdp::mdp_alloc_dbl_1((int) neq_, MDP_DBL_NOINIT);
// First recalculate the scaled residual. It got wiped out doing the newton solve
if (m_rowScaling) {
for (size_t n = 0; n < neq_; n++) {
delyH[n] = -m_rowScales[n] * m_resid[n];
}
} else {
for (size_t n = 0; n < neq_; n++) {
delyH[n] = -m_resid[n];
}
}
if (m_rowScaling) {
for (size_t j = 0; j < neq_; j++) {
delta_y[j] = 0.0;
for (size_t i = 0; i < neq_; i++) {
delta_y[j] += delyH[i] * jacCopy(i,j) * m_rowScales[i];
}
}
} else {
for (size_t j = 0; j < neq_; j++) {
delta_y[j] = 0.0;
for (size_t i = 0; i < neq_; i++) {
delta_y[j] += delyH[i] * jacCopy(i,j);
}
}
}
/*
* Solve the factored Hessian System
*/
ct_dpotrs(ctlapack::UpperTriangular, neq_, 1,&(*(hessian.begin())), neq_, delta_y, neq_, info);
if (info) {
if (m_print_flag >= 2) {
printf("\t\t NonlinearSolver::doAffineNewtonSolve() ERROR: DPOTRS returned INFO = %d\n", info);
}
return info;
}
/*
* reverse the column scaling if there was any.
*/
if (m_colScaling) {
for (size_t irow = 0; irow < neq_; irow++) {
delta_y[irow] = delta_y[irow] * m_colScales[irow];
}
}
if (doDogLeg_ && m_print_flag > 7) {
double normNewt = solnErrorNorm(CONSTD_DATA_PTR(delyNewton));
double normHess = solnErrorNorm(CONSTD_DATA_PTR(delta_y));
printf("\t\t doAffineNewtonSolve(): Printout Comparison between Hessian deltaX and Newton deltaX\n");
printf("\t\t I Hessian+Junk Newton");
if (newtonGood || s_alwaysAssumeNewtonGood) {
printf(" (USING NEWTON DIRECTION)\n");
} else {
printf(" (USING HESSIAN DIRECTION)\n");
}
printf("\t\t Norm: %12.4E %12.4E\n", normHess, normNewt);
printf("\t\t --------------------------------------------------------\n");
for (size_t i =0; i < neq_; i++) {
printf("\t\t %3d %13.5E %13.5E\n", i, delta_y[i], delyNewton[i]);
}
printf("\t\t --------------------------------------------------------\n");
} else if (doDogLeg_ && m_print_flag >= 4) {
double normNewt = solnErrorNorm(CONSTD_DATA_PTR(delyNewton));
double normHess = solnErrorNorm(CONSTD_DATA_PTR(delta_y));
printf("\t\t doAffineNewtonSolve(): Hessian update norm = %12.4E \n"
"\t\t Newton update norm = %12.4E \n", normHess, normNewt);
if (newtonGood || s_alwaysAssumeNewtonGood) {
printf("\t\t (USING NEWTON DIRECTION)\n");
} else {
printf("\t\t (USING HESSIAN DIRECTION)\n");
}
}
/*
* Choose the delta_y to use
*/
if (newtonGood || s_alwaysAssumeNewtonGood) {
mdp::mdp_copy_dbl_1(DATA_PTR(delta_y), CONSTD_DATA_PTR(delyNewton), (int) neq_);
}
mdp::mdp_safe_free((void**) &delyH);
mdp::mdp_safe_free((void**) &delyNewton);
}
#ifdef DEBUG_JAC
if (printJacContributions) {
for (int iNum = 0; iNum < numRows; iNum++) {
if (iNum > 0) {
focusRow++;
}
doublereal dsum = 0.0;
vector_fp& Jdata = jacBack.data();
doublereal dRow = Jdata[neq_ * focusRow + focusRow];
printf("\n Details on delta_Y for row %d \n", focusRow);
printf(" Value before = %15.5e, delta = %15.5e,"
"value after = %15.5e\n", y_curr[focusRow],
delta_y[focusRow], y_curr[focusRow] + delta_y[focusRow]);
if (!freshJac) {
printf(" Old Jacobian\n");
}
printf(" col delta_y aij "
"contrib \n");
printf("-----------------------------------------------------------------------------------------------\n");
printf(" Res(%d) %15.5e %15.5e %15.5e (Res = %g)\n",
focusRow, delta_y[focusRow],
dRow, RRow[iNum] / dRow, RRow[iNum]);
dsum += RRow[iNum] / dRow;
for (int ii = 0; ii < neq_; ii++) {
if (ii != focusRow) {
doublereal aij = Jdata[neq_ * ii + focusRow];
doublereal contrib = aij * delta_y[ii] * (-1.0) / dRow;
dsum += contrib;
if (fabs(contrib) > Pcutoff) {
printf("%6d %15.5e %15.5e %15.5e\n", ii,
delta_y[ii] , aij, contrib);
}
}
}
printf("-----------------------------------------------------------------------------------------------\n");
printf(" %15.5e %15.5e\n",
delta_y[focusRow], dsum);
}
}
#endif
m_numTotalLinearSolves++;
m_numLocalLinearSolves++;
return info;
}
//====================================================================================================================
// Do a steepest descent calculation
/*
* This call must be made on the unfactored jacobian!
*/
doublereal NonlinearSolver::doCauchyPointSolve(GeneralMatrix& jac)
{
doublereal rowFac = 1.0;
doublereal colFac = 1.0;
doublereal normSoln;
// Calculate the descent direction
/*
* For confirmation of the scaling factors, see Dennis and Schnabel p, 152, p, 156 and my notes
*
* The colFac and rowFac values are used to eliminate the scaling of the matrix from the
* actual equation
*
* Here we calculate the steepest descent direction. This is equation (11) in the notes. It is
* storred in deltaX_CP_[].The value corresponds to d_descent[].
*/
for (size_t j = 0; j < neq_; j++) {
deltaX_CP_[j] = 0.0;
if (m_colScaling) {
colFac = 1.0 / m_colScales[j];
}
for (size_t i = 0; i < neq_; i++) {
if (m_rowScaling) {
rowFac = 1.0 / m_rowScales[i];
}
deltaX_CP_[j] -= m_resid[i] * jac(i,j) * colFac * rowFac * m_ewt[j] * m_ewt[j]
/ (m_residWts[i] * m_residWts[i]);
#ifdef DEBUG_MODE
mdp::checkFinite(deltaX_CP_[j]);
#endif
}
}
/*
* Calculate J_hat d_y_descent. This is formula 18 in the notes.
*/
for (size_t i = 0; i < neq_; i++) {
Jd_[i] = 0.0;
if (m_rowScaling) {
rowFac = 1.0 / m_rowScales[i];
} else {
rowFac = 1.0;
}
for (size_t j = 0; j < neq_; j++) {
if (m_colScaling) {
colFac = 1.0 / m_colScales[j];
}
Jd_[i] += deltaX_CP_[j] * jac(i,j) * rowFac * colFac / m_residWts[i];
}
}
/*
* Calculate the distance along the steepest descent until the Cauchy point
* This is Eqn. 17 in the notes.
*/
RJd_norm_ = 0.0;
JdJd_norm_ = 0.0;
for (size_t i = 0; i < neq_; i++) {
RJd_norm_ += m_resid[i] * Jd_[i] / m_residWts[i];
JdJd_norm_ += Jd_[i] * Jd_[i];
}
//if (RJd_norm_ > -1.0E-300) {
// printf("we are here: zero residual\n");
//}
if (fabs(JdJd_norm_) < 1.0E-290) {
if (fabs(RJd_norm_) < 1.0E-300) {
lambdaStar_ = 0.0;
} else {
throw CanteraError("NonlinearSolver::doCauchyPointSolve()", "Unexpected condition: norms are zero");
}
} else {
lambdaStar_ = - RJd_norm_ / (JdJd_norm_);
}
/*
* Now we modify the steepest descent vector such that its length is equal to the
* Cauchy distance. From now on, if we want to recreate the descent vector, we have
* to unnormalize it by dividing by lambdaStar_.
*/
for (size_t i = 0; i < neq_; i++) {
deltaX_CP_[i] *= lambdaStar_;
}
doublereal normResid02 = m_normResid_0 * m_normResid_0 * neq_;
/*
* Calculate the expected square of the risdual at the Cauchy point if the linear model is correct
*/
if (fabs(JdJd_norm_) < 1.0E-290) {
residNorm2Cauchy_ = normResid02;
} else {
residNorm2Cauchy_ = normResid02 - RJd_norm_ * RJd_norm_ / (JdJd_norm_);
}
// Extra printout section
if (m_print_flag > 2) {
// Calculate the expected residual at the Cauchy point if the linear model is correct
doublereal residCauchy = 0.0;
if (residNorm2Cauchy_ > 0.0) {
residCauchy = sqrt(residNorm2Cauchy_ / neq_);
} else {
if (fabs(JdJd_norm_) < 1.0E-290) {
residCauchy = m_normResid_0;
} else {
residCauchy = m_normResid_0 - sqrt(RJd_norm_ * RJd_norm_ / (JdJd_norm_));
}
}
// Compute the weighted norm of the undamped step size descentDir_[]
if ((s_print_DogLeg || doDogLeg_) && m_print_flag >= 6) {
normSoln = solnErrorNorm(DATA_PTR(deltaX_CP_), "SteepestDescentDir", 10);
} else {
normSoln = solnErrorNorm(DATA_PTR(deltaX_CP_), "SteepestDescentDir", 0);
}
if ((s_print_DogLeg || doDogLeg_) && m_print_flag >= 5) {
printf("\t\t doCauchyPointSolve: Steepest descent to Cauchy point: \n");
printf("\t\t\t R0 = %g \n", m_normResid_0);
printf("\t\t\t Rpred = %g\n", residCauchy);
printf("\t\t\t Rjd = %g\n", RJd_norm_);
printf("\t\t\t JdJd = %g\n", JdJd_norm_);
printf("\t\t\t deltaX = %g\n", normSoln);
printf("\t\t\t lambda = %g\n", lambdaStar_);
}
} else {
// Calculate the norm of the Cauchy solution update in any case
normSoln = solnErrorNorm(DATA_PTR(deltaX_CP_), "SteepestDescentDir", 0);
}
return normSoln;
}
//===================================================================================================================
void NonlinearSolver::descentComparison(doublereal time_curr, doublereal* ydot0, doublereal* ydot1, int& numTrials)
{
doublereal ff = 1.0E-5;
doublereal ffNewt = 1.0E-5;
doublereal* y_n_1 = DATA_PTR(m_wksp);
doublereal cauchyDistanceNorm = solnErrorNorm(DATA_PTR(deltaX_CP_));
if (cauchyDistanceNorm < 1.0E-2) {
ff = 1.0E-9 / cauchyDistanceNorm;
if (ff > 1.0E-2) {
ff = 1.0E-2;
}
}
for (size_t i = 0; i < neq_; i++) {
y_n_1[i] = m_y_n_curr[i] + ff * deltaX_CP_[i];
}
/*
* Calculate the residual that would result if y1[] were the new solution vector
* -> m_resid[] contains the result of the residual calculation
*/
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
doResidualCalc(time_curr, solnType_, y_n_1, ydot1, Base_LaggedSolutionComponents);
} else {
doResidualCalc(time_curr, solnType_, y_n_1, ydot0, Base_LaggedSolutionComponents);
}
doublereal normResid02 = m_normResid_0 * m_normResid_0 * neq_;
doublereal residSteep = residErrorNorm(DATA_PTR(m_resid));
doublereal residSteep2 = residSteep * residSteep * neq_;
doublereal funcDecreaseSD = 0.5 * (residSteep2 - normResid02) / (ff * cauchyDistanceNorm);
doublereal sNewt = solnErrorNorm(DATA_PTR(deltaX_Newton_));
if (sNewt > 1.0) {
ffNewt = ffNewt / sNewt;
}
for (size_t i = 0; i < neq_; i++) {
y_n_1[i] = m_y_n_curr[i] + ffNewt * deltaX_Newton_[i];
}
/*
* Calculate the residual that would result if y1[] were the new solution vector.
* Here we use the lagged solution components in the residual calculation as well. We are
* interested in the linear model and its agreement with the nonlinear model.
*
* -> m_resid[] contains the result of the residual calculation
*/
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
doResidualCalc(time_curr, solnType_, y_n_1, ydot1, Base_LaggedSolutionComponents);
} else {
doResidualCalc(time_curr, solnType_, y_n_1, ydot0, Base_LaggedSolutionComponents);
}
doublereal residNewt = residErrorNorm(DATA_PTR(m_resid));
doublereal residNewt2 = residNewt * residNewt * neq_;
doublereal funcDecreaseNewt2 = 0.5 * (residNewt2 - normResid02) / (ffNewt * sNewt);
// This is the expected inital rate of decrease in the Cauchy direction.
// -> This is Eqn. 29 = Rhat dot Jhat dy / || d ||
doublereal funcDecreaseSDExp = RJd_norm_ / cauchyDistanceNorm * lambdaStar_;
doublereal funcDecreaseNewtExp2 = - normResid02 / sNewt;
if (m_normResid_0 > 1.0E-100) {
ResidDecreaseSDExp_ = funcDecreaseSDExp / neq_ / m_normResid_0;
ResidDecreaseSD_ = funcDecreaseSD / neq_ / m_normResid_0;
ResidDecreaseNewtExp_ = funcDecreaseNewtExp2 / neq_ / m_normResid_0;
ResidDecreaseNewt_ = funcDecreaseNewt2 / neq_ / m_normResid_0;
} else {
ResidDecreaseSDExp_ = 0.0;
ResidDecreaseSD_ = funcDecreaseSD / neq_;
ResidDecreaseNewtExp_ = 0.0;
ResidDecreaseNewt_ = funcDecreaseNewt2 / neq_;
}
numTrials += 2;
/*
* HKM These have been shown to exactly match up.
* The steepest direction is always largest even when there are variable solution weights
*
* HKM When a hessian is used with junk on the diagonal, funcDecreaseNewtExp2 is no longer accurate as the
* direction gets signficantly shorter with increasing condition number. This suggests an algorithm where the
* newton step from the Hessian should be increased so as to match funcDecreaseNewtExp2 = funcDecreaseNewt2.
* This roughly equals the ratio of the norms of the hessian and newton steps. This increased Newton step can
* then be used with the trust region double dogleg algorithm.
*/
if ((s_print_DogLeg && m_print_flag >= 3) || (doDogLeg_ && m_print_flag >= 5)) {
printf("\t\t descentComparison: initial rate of decrease of func in cauchy dir (expected) = %g\n", funcDecreaseSDExp);
printf("\t\t descentComparison: initial rate of decrease of func in cauchy dir = %g\n", funcDecreaseSD);
printf("\t\t descentComparison: initial rate of decrease of func in newton dir (expected) = %g\n", funcDecreaseNewtExp2);
printf("\t\t descentComparison: initial rate of decrease of func in newton dir = %g\n", funcDecreaseNewt2);
}
if ((s_print_DogLeg && m_print_flag >= 3) || (doDogLeg_ && m_print_flag >= 4)) {
printf("\t\t descentComparison: initial rate of decrease of Resid in cauchy dir (expected) = %g\n", ResidDecreaseSDExp_);
printf("\t\t descentComparison: initial rate of decrease of Resid in cauchy dir = %g\n", ResidDecreaseSD_);
printf("\t\t descentComparison: initial rate of decrease of Resid in newton dir (expected) = %g\n", ResidDecreaseNewtExp_);
printf("\t\t descentComparison: initial rate of decrease of Resid in newton dir = %g\n", ResidDecreaseNewt_);
}
if ((s_print_DogLeg && m_print_flag >= 5) || (doDogLeg_ && m_print_flag >= 5)) {
if (funcDecreaseNewt2 >= 0.0) {
printf("\t\t %13.5E %22.16E\n", funcDecreaseNewtExp2, m_normResid_0);
double ff = ffNewt * 1.0E-5;
for (int ii = 0; ii < 13; ii++) {
ff *= 10.;
if (ii == 12) {
ff = ffNewt;
}
for (size_t i = 0; i < neq_; i++) {
y_n_1[i] = m_y_n_curr[i] + ff * deltaX_Newton_[i];
}
numTrials += 1;
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
doResidualCalc(time_curr, solnType_, y_n_1, ydot1, Base_LaggedSolutionComponents);
} else {
doResidualCalc(time_curr, solnType_, y_n_1, ydot0, Base_LaggedSolutionComponents);
}
residNewt = residErrorNorm(DATA_PTR(m_resid));
residNewt2 = residNewt * residNewt * neq_;
funcDecreaseNewt2 = 0.5 * (residNewt2 - normResid02) / (ff * sNewt);
printf("\t\t %10.3E %13.5E %22.16E\n", ff, funcDecreaseNewt2, residNewt);
}
}
}
}
//====================================================================================================================
// Setup the parameters for the double dog leg
/*
* The calls to the doCauchySolve() and doNewtonSolve() routines are done at the main level. This routine comes
* after those calls. We calculate the point Nuu_ here, the distances of the dog-legs,
* and the norms of the CP and Newton points in terms of the trust vectors.
*/
void NonlinearSolver::setupDoubleDogleg()
{
/*
* Gamma = ||grad f ||**4
* ---------------------------------------------
* (grad f)T H (grad f) (grad f)T H-1 (grad f)
*/
// doublereal sumG = 0.0;
// doublereal sumH = 0.0;
// for (int i = 0; i < neq_; i++) {
// sumG = deltax_cp_[i] * deltax_cp_[i];
// sumH = deltax_cp_[i] * newtDir[i];
// }
// double fac1 = sumG / lambdaStar_;
// double fac2 = sumH / lambdaStar_;
// double gamma = fac1 / fac2;
// doublereal gamma = m_normDeltaSoln_CP / m_normDeltaSoln_Newton;
/*
* This hasn't worked. so will do it heuristically. One issue is that the newton
* direction is not the inverse of the Hessian times the gradient. The Hession
* is the matrix squared. Until I have the inverse of the Hessian from QR factorization
* I may not be able to do it this way.
*/
/*
* Heuristic algorithm - Find out where on the Newton line the residual is the same
* as the residual at the cauchy point. Then, go halfway to
* the newton point and call that Nuu.
* Maybe we need to check that the linearized residual is
* monotonic along that line. However, we haven't needed to yet.
*/
doublereal residSteepLin = expectedResidLeg(0, 1.0);
doublereal Nres2CP = residSteepLin * residSteepLin * neq_;
doublereal Nres2_o = m_normResid_0 * m_normResid_0 * neq_;
doublereal a = Nres2CP / Nres2_o;
doublereal betaEqual = (2.0 - sqrt(4.0 - 4 * (1.0 - a))) / 2.0;
doublereal beta = (1.0 + betaEqual) / 2.0;
Nuu_ = beta;
dist_R0_ = m_normDeltaSoln_CP;
for (size_t i = 0; i < neq_; i++) {
m_wksp[i] = Nuu_ * deltaX_Newton_[i] - deltaX_CP_[i];
}
dist_R1_ = solnErrorNorm(DATA_PTR(m_wksp));
dist_R2_ = (1.0 - Nuu_) * m_normDeltaSoln_Newton;
dist_Total_ = dist_R0_ + dist_R1_ + dist_R2_;
/*
* Calculate the trust distances
*/
normTrust_Newton_ = calcTrustDistance(deltaX_Newton_);
normTrust_CP_ = calcTrustDistance(deltaX_CP_);
}
//====================================================================================================================
// Change the global lambda coordinate into the (leg,alpha) coordinate for the double dogleg
/*
* @param lambda Global value of the distance along the double dogleg
* @param alpha relative value along the particular leg
*
* @return Returns the leg number ( 0, 1, or 2).
*/
int NonlinearSolver::lambdaToLeg(const doublereal lambda, doublereal& alpha) const
{
if (lambda < dist_R0_ / dist_Total_) {
alpha = lambda * dist_Total_ / dist_R0_;
return 0;
} else if (lambda < ((dist_R0_ + dist_R1_)/ dist_Total_)) {
alpha = (lambda * dist_Total_ - dist_R0_) / dist_R1_;
return 1;
}
alpha = (lambda * dist_Total_ - dist_R0_ - dist_R1_) / dist_R2_;
return 2;
}
//====================================================================================================================
// Calculated the expected residual along the double dogleg curve.
/*
* @param leg 0, 1, or 2 representing the curves of the dogleg
* @param alpha Relative distance along the particular curve.
*
* @return Returns the expected value of the residual at that point according to the quadratic model.
* The residual at the newton point will always be zero.
*/
doublereal NonlinearSolver::expectedResidLeg(int leg, doublereal alpha) const
{
doublereal resD2, res2, resNorm;
doublereal normResid02 = m_normResid_0 * m_normResid_0 * neq_;
if (leg == 0) {
/*
* We are on the steepest descent line
* along that line
* R2 = R2 + 2 lambda R dot Jd + lambda**2 Jd dot Jd
*/
doublereal tmp = - 2.0 * alpha + alpha * alpha;
doublereal tmp2 = - RJd_norm_ * lambdaStar_;
resD2 = tmp2 * tmp;
} else if (leg == 1) {
/*
* Same formula as above for lambda=1.
*/
doublereal tmp2 = - RJd_norm_ * lambdaStar_;
doublereal RdotJS = - tmp2;
doublereal JsJs = tmp2;
doublereal res0_2 = m_normResid_0 * m_normResid_0 * neq_;
res2 = res0_2 + (1.0 - alpha) * 2 * RdotJS - 2 * alpha * Nuu_ * res0_2
+ (1.0 - alpha) * (1.0 - alpha) * JsJs
+ alpha * alpha * Nuu_ * Nuu_ * res0_2
- 2 * alpha * Nuu_ * (1.0 - alpha) * RdotJS;
resNorm = sqrt(res2 / neq_);
return resNorm;
} else {
doublereal beta = Nuu_ + alpha * (1.0 - Nuu_);
doublereal tmp2 = normResid02;
doublereal tmp = 1.0 - 2.0 * beta + 1.0 * beta * beta - 1.0;
resD2 = tmp * tmp2;
}
res2 = m_normResid_0 * m_normResid_0 * neq_ + resD2;
if (res2 < 0.0) {
resNorm = m_normResid_0 - sqrt(resD2/neq_);
} else {
resNorm = sqrt(res2 / neq_);
}
return resNorm;
}
//====================================================================================================================
// Here we print out the residual at various points along the double dogleg, comparing against the quadratic model
// in a table format
/*
* @param time_curr INPUT current time
* @param ydot0 INPUT Current value of the derivative of the solution vector for non-time dependent
* determinations
* @param legBest OUTPUT leg of the dogleg that gives the lowest residual
* @param alphaBest OUTPUT distance along dogleg for best result.
*/
void NonlinearSolver::residualComparisonLeg(const doublereal time_curr, const doublereal* const ydot0, int& legBest,
doublereal& alphaBest) const
{
doublereal* y1 = DATA_PTR(m_wksp);
doublereal* ydot1 = DATA_PTR(m_wksp_2);
doublereal sLen;
doublereal alpha;
doublereal residSteepBest = 1.0E300;
doublereal residSteepLinBest = 0.0;
if (s_print_DogLeg || (doDogLeg_ && m_print_flag > 6)) {
printf("\t\t residualComparisonLeg() \n");
printf("\t\t Point StepLen Residual_Actual Residual_Linear RelativeMatch\n");
}
// First compare at 1/4 along SD curve
std::vector<doublereal> alphaT;
alphaT.push_back(0.00);
alphaT.push_back(0.01);
alphaT.push_back(0.1);
alphaT.push_back(0.25);
alphaT.push_back(0.50);
alphaT.push_back(0.75);
alphaT.push_back(1.0);
for (size_t iteration = 0; iteration < alphaT.size(); iteration++) {
alpha = alphaT[iteration];
for (size_t i = 0; i < neq_; i++) {
y1[i] = m_y_n_curr[i] + alpha * deltaX_CP_[i];
}
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
calc_ydot(m_order, y1, ydot1);
}
sLen = alpha * solnErrorNorm(DATA_PTR(deltaX_CP_));
/*
* Calculate the residual that would result if y1[] were the new solution vector
* -> m_resid[] contains the result of the residual calculation
*/
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
doResidualCalc(time_curr, solnType_, y1, ydot1, Base_LaggedSolutionComponents);
} else {
doResidualCalc(time_curr, solnType_, y1, ydot0, Base_LaggedSolutionComponents);
}
doublereal residSteep = residErrorNorm(DATA_PTR(m_resid));
doublereal residSteepLin = expectedResidLeg(0, alpha);
if (residSteep < residSteepBest) {
legBest = 0;
alphaBest = alpha;
residSteepBest = residSteep;
residSteepLinBest = residSteepLin;
}
doublereal relFit = (residSteep - residSteepLin) / (fabs(residSteepLin) + 1.0E-10);
if (s_print_DogLeg || (doDogLeg_ && m_print_flag > 6)) {
printf("\t\t (%2d - % 10.3g) % 15.8E % 15.8E % 15.8E % 15.8E\n", 0, alpha, sLen, residSteep, residSteepLin , relFit);
}
}
for (size_t iteration = 0; iteration < alphaT.size(); iteration++) {
doublereal alpha = alphaT[iteration];
for (size_t i = 0; i < neq_; i++) {
y1[i] = m_y_n_curr[i] + (1.0 - alpha) * deltaX_CP_[i];
y1[i] += alpha * Nuu_ * deltaX_Newton_[i];
}
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
calc_ydot(m_order, y1, ydot1);
}
/*
* Calculate the residual that would result if y1[] were the new solution vector
* -> m_resid[] contains the result of the residual calculation
*/
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
doResidualCalc(time_curr, solnType_, y1, ydot1, Base_LaggedSolutionComponents);
} else {
doResidualCalc(time_curr, solnType_, y1, ydot0, Base_LaggedSolutionComponents);
}
for (size_t i = 0; i < neq_; i++) {
y1[i] -= m_y_n_curr[i];
}
sLen = solnErrorNorm(DATA_PTR(y1));
doublereal residSteep = residErrorNorm(DATA_PTR(m_resid));
doublereal residSteepLin = expectedResidLeg(1, alpha);
if (residSteep < residSteepBest) {
legBest = 1;
alphaBest = alpha;
residSteepBest = residSteep;
residSteepLinBest = residSteepLin;
}
doublereal relFit = (residSteep - residSteepLin) / (fabs(residSteepLin) + 1.0E-10);
if (s_print_DogLeg || (doDogLeg_ && m_print_flag > 6)) {
printf("\t\t (%2d - % 10.3g) % 15.8E % 15.8E % 15.8E % 15.8E\n", 1, alpha, sLen, residSteep, residSteepLin , relFit);
}
}
for (size_t iteration = 0; iteration < alphaT.size(); iteration++) {
doublereal alpha = alphaT[iteration];
for (size_t i = 0; i < neq_; i++) {
y1[i] = m_y_n_curr[i] + (Nuu_ + alpha * (1.0 - Nuu_))* deltaX_Newton_[i];
}
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
calc_ydot(m_order, y1, ydot1);
}
sLen = (Nuu_ + alpha * (1.0 - Nuu_)) * solnErrorNorm(DATA_PTR(deltaX_Newton_));
/*
* Calculate the residual that would result if y1[] were the new solution vector
* -> m_resid[] contains the result of the residual calculation
*/
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
doResidualCalc(time_curr, solnType_, y1, ydot1, Base_LaggedSolutionComponents);
} else {
doResidualCalc(time_curr, solnType_, y1, ydot0, Base_LaggedSolutionComponents);
}
doublereal residSteep = residErrorNorm(DATA_PTR(m_resid));
doublereal residSteepLin = expectedResidLeg(2, alpha);
if (residSteep < residSteepBest) {
legBest = 2;
alphaBest = alpha;
residSteepBest = residSteep;
residSteepLinBest = residSteepLin;
}
doublereal relFit = (residSteep - residSteepLin) / (fabs(residSteepLin) + 1.0E-10);
if (s_print_DogLeg || (doDogLeg_ && m_print_flag > 6)) {
printf("\t\t (%2d - % 10.3g) % 15.8E % 15.8E % 15.8E % 15.8E\n", 2, alpha, sLen, residSteep, residSteepLin , relFit);
}
}
if (s_print_DogLeg || (doDogLeg_ && m_print_flag > 6)) {
printf("\t\t Best Result: \n");
doublereal relFit = (residSteepBest - residSteepLinBest) / (fabs(residSteepLinBest) + 1.0E-10);
if (m_print_flag <= 6) {
printf("\t\t Leg %2d alpha %5g: NonlinResid = %g LinResid = %g, relfit = %g\n",
legBest, alphaBest, residSteepBest, residSteepLinBest, relFit);
} else {
if (legBest == 0) {
sLen = alpha * solnErrorNorm(DATA_PTR(deltaX_CP_));
} else if (legBest == 1) {
for (size_t i = 0; i < neq_; i++) {
y1[i] = (1.0 - alphaBest) * deltaX_CP_[i];
y1[i] += alphaBest * Nuu_ * deltaX_Newton_[i];
}
sLen = solnErrorNorm(DATA_PTR(y1));
} else {
sLen = (Nuu_ + alpha * (1.0 - Nuu_)) * solnErrorNorm(DATA_PTR(deltaX_Newton_));
}
printf("\t\t (%2d - % 10.3g) % 15.8E % 15.8E % 15.8E % 15.8E\n", legBest, alphaBest, sLen,
residSteepBest, residSteepLinBest , relFit);
}
}
}
//====================================================================================================================
// Calculate the length of the current trust region in terms of the solution error norm
/*
* We carry out a norm of deltaX_trust_ first. Then, we multiply that value
* by trustDelta_
*/
doublereal NonlinearSolver::trustRegionLength() const
{
norm_deltaX_trust_ = solnErrorNorm(DATA_PTR(deltaX_trust_));
return (trustDelta_ * norm_deltaX_trust_);
}
//====================================================================================================================
void NonlinearSolver::setDefaultDeltaBoundsMagnitudes()
{
for (size_t i = 0; i < neq_; i++) {
m_deltaStepMinimum[i] = 1000. * atolk_[i];
m_deltaStepMinimum[i] = MAX(m_deltaStepMinimum[i], 0.1 * fabs(m_y_n_curr[i]));
}
}
//====================================================================================================================
void NonlinearSolver::adjustUpStepMinimums()
{
for (size_t i = 0; i < neq_; i++) {
doublereal goodVal = deltaX_trust_[i] * trustDelta_;
if (deltaX_trust_[i] * trustDelta_ > m_deltaStepMinimum[i]) {
m_deltaStepMinimum[i] = 1.1 * goodVal;
}
}
}
//====================================================================================================================
void NonlinearSolver::setDeltaBoundsMagnitudes(const doublereal* const deltaStepMinimum)
{
for (size_t i = 0; i < neq_; i++) {
m_deltaStepMinimum[i] = deltaStepMinimum[i];
}
m_manualDeltaStepSet = 1;
}
//====================================================================================================================
/*
*
* Return the factor by which the undamped Newton step 'step0'
* must be multiplied in order to keep the update within the bounds of an accurate jacobian.
*
* The idea behind these is that the Jacobian couldn't possibly be representative, if the
* variable is changed by a lot. (true for nonlinear systems, false for linear systems)
* Maximum increase in variable in any one newton iteration:
* factor of 1.5
* Maximum decrease in variable in any one newton iteration:
* factor of 2
*
* @param y_n_curr Initial value of the solution vector
* @param step_1 initial proposed step size
*
* @return returns the damping factor
*/
double
NonlinearSolver::deltaBoundStep(const doublereal* const y_n_curr, const doublereal* const step_1)
{
size_t i_fbounds = 0;
int ifbd = 0;
int i_fbd = 0;
doublereal UPFAC = 2.0;
doublereal sameSign = 0.0;
doublereal ff;
doublereal f_delta_bounds = 1.0;
doublereal ff_alt;
for (size_t i = 0; i < neq_; i++) {
doublereal y_new = y_n_curr[i] + step_1[i];
sameSign = y_new * y_n_curr[i];
/*
* Now do a delta bounds
* Increase variables by a factor of UPFAC only
* decrease variables by a factor of 2 only
*/
ff = 1.0;
if (sameSign >= 0.0) {
if ((fabs(y_new) > UPFAC * fabs(y_n_curr[i])) &&
(fabs(y_new - y_n_curr[i]) > m_deltaStepMinimum[i])) {
ff = (UPFAC - 1.0) * fabs(y_n_curr[i]/(y_new - y_n_curr[i]));
ff_alt = fabs(m_deltaStepMinimum[i] / (y_new - y_n_curr[i]));
ff = MAX(ff, ff_alt);
ifbd = 1;
}
if ((fabs(2.0 * y_new) < fabs(y_n_curr[i])) &&
(fabs(y_new - y_n_curr[i]) > m_deltaStepMinimum[i])) {
ff = y_n_curr[i]/(y_new - y_n_curr[i]) * (1.0 - 2.0)/2.0;
ff_alt = fabs(m_deltaStepMinimum[i] / (y_new - y_n_curr[i]));
ff = MAX(ff, ff_alt);
ifbd = 0;
}
} else {
/*
* This handles the case where the value crosses the origin.
* - First we don't let it cross the origin until its shrunk to the size of m_deltaStepMinimum[i]
*/
if (fabs(y_n_curr[i]) > m_deltaStepMinimum[i]) {
ff = y_n_curr[i]/(y_new - y_n_curr[i]) * (1.0 - 2.0)/2.0;
ff_alt = fabs(m_deltaStepMinimum[i] / (y_new - y_n_curr[i]));
ff = MAX(ff, ff_alt);
if (y_n_curr[i] >= 0.0) {
ifbd = 0;
} else {
ifbd = 1;
}
}
/*
* Second when it does cross the origin, we make sure that its magnitude is only 50% of the previous value.
*/
else if (fabs(y_new) > 0.5 * fabs(y_n_curr[i])) {
ff = y_n_curr[i]/(y_new - y_n_curr[i]) * (-1.5);
ff_alt = fabs(m_deltaStepMinimum[i] / (y_new - y_n_curr[i]));
ff = MAX(ff, ff_alt);
ifbd = 0;
}
}
if (ff < f_delta_bounds) {
f_delta_bounds = ff;
i_fbounds = i;
i_fbd = ifbd;
}
}
/*
* Report on any corrections
*/
if (m_print_flag >= 3) {
if (f_delta_bounds < 1.0) {
if (i_fbd) {
printf("\t\tdeltaBoundStep: Increase of Variable %d causing "
"delta damping of %g: origVal = %10.3g, undampedNew = %10.3g, dampedNew = %10.3g\n",
i_fbounds, f_delta_bounds, y_n_curr[i_fbounds], y_n_curr[i_fbounds] + step_1[i_fbounds],
y_n_curr[i_fbounds] + f_delta_bounds * step_1[i_fbounds]);
} else {
printf("\t\tdeltaBoundStep: Decrease of variable %d causing"
"delta damping of %g: origVal = %10.3g, undampedNew = %10.3g, dampedNew = %10.3g\n",
i_fbounds, f_delta_bounds, y_n_curr[i_fbounds], y_n_curr[i_fbounds] + step_1[i_fbounds],
y_n_curr[i_fbounds] + f_delta_bounds * step_1[i_fbounds]);
}
}
}
return f_delta_bounds;
}
//====================================================================================================================
// Readjust the trust region vectors
/*
* The trust region is made up of the trust region vector calculation and the trustDelta_ value
* We periodically recalculate the trustVector_ values so that they renormalize to the
* correct length.
*/
void NonlinearSolver::readjustTrustVector()
{
doublereal trustDeltaOld = trustDelta_;
doublereal wtSum = 0.0;
for (size_t i = 0; i < neq_; i++) {
wtSum += m_ewt[i];
}
wtSum /= neq_;
doublereal trustNorm = solnErrorNorm(DATA_PTR(deltaX_trust_));
doublereal deltaXSizeOld = trustNorm;
doublereal trustNormGoal = trustNorm * trustDelta_;
// This is the size of each component.
// doublereal trustDeltaEach = trustDelta_ * trustNorm / neq_;
doublereal oldVal;
doublereal fabsy;
// we use the old value of the trust region as an indicator
for (size_t i = 0; i < neq_; i++) {
oldVal = deltaX_trust_[i];
fabsy = fabs(m_y_n_curr[i]);
// First off make sure that each trust region vector is 1/2 the size of each variable or smaller
// unless overridden by the deltaStepMininum value.
// doublereal newValue = trustDeltaEach * m_ewt[i] / wtSum;
doublereal newValue = trustNormGoal * m_ewt[i];
if (newValue > 0.5 * fabsy) {
if (fabsy * 0.5 > m_deltaStepMinimum[i]) {
deltaX_trust_[i] = 0.5 * fabsy;
} else {
deltaX_trust_[i] = m_deltaStepMinimum[i];
}
} else {
if (newValue > 4.0 * oldVal) {
newValue = 4.0 * oldVal;
} else if (newValue < 0.25 * oldVal) {
newValue = 0.25 * oldVal;
if (deltaX_trust_[i] < m_deltaStepMinimum[i]) {
newValue = m_deltaStepMinimum[i];
}
}
deltaX_trust_[i] = newValue;
if (deltaX_trust_[i] > 0.75 * m_deltaStepMaximum[i]) {
deltaX_trust_[i] = 0.75 * m_deltaStepMaximum[i];
}
}
}
// Final renormalization.
norm_deltaX_trust_ = solnErrorNorm(DATA_PTR(deltaX_trust_));
doublereal sum = trustNormGoal / trustNorm;
for (size_t i = 0; i < neq_; i++) {
deltaX_trust_[i] = deltaX_trust_[i] * sum;
}
norm_deltaX_trust_ = solnErrorNorm(DATA_PTR(deltaX_trust_));
trustDelta_ = trustNormGoal / norm_deltaX_trust_;
if (doDogLeg_ && m_print_flag >= 4) {
printf("\t\t reajustTrustVector(): Trust size = %11.3E: Old deltaX size = %11.3E trustDelta_ = %11.3E\n"
"\t\t new deltaX size = %11.3E trustdelta_ = %11.3E\n",
trustNormGoal, deltaXSizeOld, trustDeltaOld, norm_deltaX_trust_, trustDelta_);
}
}
//====================================================================================================================
//! Initialize the size of the trust vector.
/*!
* The algorithm we use is to set it equal to the length of the Distance to the Cauchy point.
*/
void NonlinearSolver::initializeTrustRegion()
{
if (trustRegionInitializationMethod_ == 0) {
return;
}
if (trustRegionInitializationMethod_ == 1) {
for (size_t i = 0; i < neq_; i++) {
deltaX_trust_[i] = m_ewt[i] * trustRegionInitializationFactor_;
}
trustDelta_ = 1.0;
}
if (trustRegionInitializationMethod_ == 2) {
for (size_t i = 0; i < neq_; i++) {
deltaX_trust_[i] = m_ewt[i] * m_normDeltaSoln_CP * trustRegionInitializationFactor_;
}
doublereal cpd = calcTrustDistance(deltaX_CP_);
if ((doDogLeg_ && m_print_flag >= 4)) {
printf("\t\t initializeTrustRegion(): Relative Distance of Cauchy Vector wrt Trust Vector = %g\n", cpd);
}
trustDelta_ = trustDelta_ * cpd * trustRegionInitializationFactor_;
readjustTrustVector();
cpd = calcTrustDistance(deltaX_CP_);
if ((doDogLeg_ && m_print_flag >= 4)) {
printf("\t\t initializeTrustRegion(): Relative Distance of Cauchy Vector wrt Trust Vector = %g\n", cpd);
}
}
if (trustRegionInitializationMethod_ == 3) {
for (size_t i = 0; i < neq_; i++) {
deltaX_trust_[i] = m_ewt[i] * m_normDeltaSoln_Newton * trustRegionInitializationFactor_;
}
doublereal cpd = calcTrustDistance(deltaX_Newton_);
if ((doDogLeg_ && m_print_flag >= 4)) {
printf("\t\t initializeTrustRegion(): Relative Distance of Newton Vector wrt Trust Vector = %g\n", cpd);
}
trustDelta_ = trustDelta_ * cpd;
readjustTrustVector();
cpd = calcTrustDistance(deltaX_Newton_);
if ((doDogLeg_ && m_print_flag >= 4)) {
printf("\t\t initializeTrustRegion(): Relative Distance of Newton Vector wrt Trust Vector = %g\n", cpd);
}
}
}
//====================================================================================================================
// Fill a dogleg solution step vector
/*
* Previously, we have filled up deltaX_Newton_[], deltaX_CP_[], and Nuu_, so that
* this routine is straightforward.
*
* @param leg Leg of the dog leg you are on (0, 1, or 2)
* @param alpha Relative length along the dog length that you are on.
* @param deltaX Vector to be filled up
*/
void NonlinearSolver::fillDogLegStep(int leg, doublereal alpha, std::vector<doublereal> & deltaX) const
{
if (leg == 0) {
for (size_t i = 0; i < neq_; i++) {
deltaX[i] = alpha * deltaX_CP_[i];
}
} else if (leg == 2) {
for (size_t i = 0; i < neq_; i++) {
deltaX[i] = (alpha + (1.0 - alpha) * Nuu_) * deltaX_Newton_[i];
}
} else {
for (size_t i = 0; i < neq_; i++) {
deltaX[i] = deltaX_CP_[i] * (1.0 - alpha) + alpha * Nuu_ * deltaX_Newton_[i];
}
}
}
//====================================================================================================================
// Calculate the trust distance of a step in the solution variables
/*
* The trust distance is defined as the length of the step according to the norm wrt to the trust region.
* We calculate the trust distance by the following method
*
* trustDist = || delta_x dot 1/trustDeltaX_ || / trustDelta_
*
* @param deltaX Current value of deltaX
*/
doublereal NonlinearSolver::calcTrustDistance(std::vector<doublereal> const& deltaX) const
{
doublereal sum = 0.0;
doublereal tmp = 0.0;
for (size_t i = 0; i < neq_; i++) {
tmp = deltaX[i] / deltaX_trust_[i];
sum += tmp * tmp;
}
sum = sqrt(sum / neq_) / trustDelta_;
return sum;
}
//====================================================================================================================
// Given a trust distance, this routine calculates the intersection of the this distance with the
// double dogleg curve
/*
* @param trustDelta (INPUT) Value of the trust distance
* @param lambda (OUTPUT) Returns the internal coordinate of the double dogleg
* @param alpha (OUTPUT) Returns the relative distance along the appropriate leg
* @return leg (OUTPUT) Returns the leg ID (0, 1, or 2)
*/
int NonlinearSolver::calcTrustIntersection(doublereal trustDelta, doublereal& lambda, doublereal& alpha) const
{
doublereal dist;
if (normTrust_Newton_ < trustDelta) {
lambda = 1.0;
alpha = 1.0;
return 2;
}
if (normTrust_Newton_ * Nuu_ < trustDelta) {
alpha = (trustDelta - normTrust_Newton_ * Nuu_) / (normTrust_Newton_ - normTrust_Newton_ * Nuu_);
dist = dist_R0_ + dist_R1_ + alpha * dist_R2_;
lambda = dist / dist_Total_;
return 2;
}
if (normTrust_CP_ > trustDelta) {
lambda = 1.0;
dist = dist_R0_ * trustDelta / normTrust_CP_;
lambda = dist / dist_Total_;
alpha = trustDelta / normTrust_CP_;
return 0;
}
doublereal sumv = 0.0;
for (size_t i = 0; i < neq_; i++) {
sumv += (deltaX_Newton_[i] / deltaX_trust_[i]) * (deltaX_CP_[i] / deltaX_trust_[i]);
}
doublereal a = normTrust_Newton_ * normTrust_Newton_ * Nuu_ * Nuu_;
doublereal b = 2.0 * Nuu_ * sumv;
doublereal c = normTrust_CP_ * normTrust_CP_ - trustDelta * trustDelta;
alpha =(-b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
dist = dist_R0_ + alpha * dist_R1_;
lambda = dist / dist_Total_;
return 1;
}
//====================================================================================================================
/*
*
* boundStep():
*
* Return the factor by which the undamped Newton step 'step0'
* must be multiplied in order to keep all solution components in
* all domains between their specified lower and upper bounds.
* Other bounds may be applied here as well.
*
* Currently the bounds are hard coded into this routine:
*
* Minimum value for all variables: - 0.01 * m_ewt[i]
* Maximum value = none.
*
* Thus, this means that all solution components are expected
* to be numerical greater than zero in the limit of time step
* truncation errors going to zero.
*
* Delta bounds: The idea behind these is that the Jacobian
* couldn't possibly be representative if the
* variable is changed by a lot. (true for
* nonlinear systems, false for linear systems)
* Maximum increase in variable in any one newton iteration:
* factor of 2
* Maximum decrease in variable in any one newton iteration:
* factor of 5
*/
doublereal NonlinearSolver::boundStep(const doublereal* const y, const doublereal* const step0)
{
size_t i_lower = npos;
doublereal fbound = 1.0, f_bounds = 1.0;
doublereal ff, y_new;
for (size_t i = 0; i < neq_; i++) {
y_new = y[i] + step0[i];
/*
* Force the step to only take 80% a step towards the lower bounds
*/
if (step0[i] < 0.0) {
if (y_new < (y[i] + 0.8 * (m_y_low_bounds[i] - y[i]))) {
doublereal legalDelta = 0.8*(m_y_low_bounds[i] - y[i]);
ff = legalDelta / step0[i];
if (ff < f_bounds) {
f_bounds = ff;
i_lower = i;
}
}
}
/*
* Force the step to only take 80% a step towards the high bounds
*/
if (step0[i] > 0.0) {
if (y_new > (y[i] + 0.8 * (m_y_high_bounds[i] - y[i]))) {
doublereal legalDelta = 0.8*(m_y_high_bounds[i] - y[i]);
ff = legalDelta / step0[i];
if (ff < f_bounds) {
f_bounds = ff;
i_lower = i;
}
}
}
}
/*
* Report on any corrections
*/
if (m_print_flag >= 3) {
if (f_bounds != 1.0) {
printf("\t\tboundStep: Variable %d causing bounds damping of %g\n", i_lower, f_bounds);
}
}
doublereal f_delta_bounds = deltaBoundStep(y, step0);
fbound = MIN(f_bounds, f_delta_bounds);
return fbound;
}
//===================================================================================================================
// Find a damping coefficient through a look-ahead mechanism
/*
*
* On entry, step0 must contain an undamped Newton step to the
* current solution y0. This method attempts to find a damping coefficient
* such that the next undamped step would have a norm smaller than
* that of step0. If successful, the new solution after taking the
* damped step is returned in y1, and the undamped step at y1 is
* returned in step1.
*
*
* @return 1 Successful step was taken: Next step was less than previous step.
* s1 is calculated
* 2 Successful step: Next step's norm is less than 0.8
* 3 Success: The final residual is less than 1.0
* A predicted deltaSoln1 is not produced however. s1 is estimated.
* 4 Success: The final residual is less than the residual
* from the previous step.
* A predicted deltaSoln1 is not produced however. s1 is estimated.
* 0 Uncertain Success: s1 is about the same as s0
* NSOLN_RETN_FAIL_DAMPSTEP
* Unsuccessful step. We can not find a damping factor that is suitable.
*/
int NonlinearSolver::dampStep(const doublereal time_curr, const doublereal* const y_n_curr,
const doublereal* const ydot_n_curr, doublereal* const step_1,
doublereal* const y_n_1, doublereal* const ydot_n_1, doublereal* const step_2,
doublereal& stepNorm_2, GeneralMatrix& jac, bool writetitle, int& num_backtracks)
{
int m;
int info = 0;
int retnTrial = NSOLN_RETN_FAIL_DAMPSTEP;
// Compute the weighted norm of the undamped step size step_1
doublereal stepNorm_1 = solnErrorNorm(step_1);
doublereal* step_1_orig = DATA_PTR(m_wksp);
for (size_t j = 0; j < neq_; j++) {
step_1_orig[j] = step_1[j];
}
// Compute the multiplier to keep all components in bounds.A value of one indicates that there is no limitation
// on the current step size in the nonlinear method due to bounds constraints (either negative values of delta
// bounds constraints.
m_dampBound = boundStep(y_n_curr, step_1);
// If fbound is very small, then y0 is already close to the boundary and step0 points out of the allowed domain. In
// this case, the Newton algorithm fails, so return an error condition.
if (m_dampBound < 1.e-30) {
if (m_print_flag > 1) {
printf("\t\t\tdampStep(): At limits.\n");
}
return -3;
}
//--------------------------------------------
// Attempt damped step
//--------------------------------------------
// damping coefficient starts at 1.0
m_dampRes = 1.0;
doublereal ff = m_dampBound;
num_backtracks = 0;
for (m = 0; m < NDAMP; m++) {
ff = m_dampBound * m_dampRes;
// step the solution by the damped step size
/*
* Whenever we update the solution, we must also always
* update the time derivative.
*/
for (size_t j = 0; j < neq_; j++) {
step_1[j] = ff * step_1_orig[j];
y_n_1[j] = y_n_curr[j] + step_1[j];
}
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
calc_ydot(m_order, y_n_1, ydot_n_1);
}
/*
* Calculate the residual that would result if y1[] were the new solution vector
* -> m_resid[] contains the result of the residual calculation
*/
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
info = doResidualCalc(time_curr, solnType_, y_n_1, ydot_n_1, Base_LaggedSolutionComponents);
} else {
info = doResidualCalc(time_curr, solnType_, y_n_1, ydot_n_curr, Base_LaggedSolutionComponents);
}
if (info != 1) {
if (m_print_flag > 0) {
printf("\t\t\tdampStep(): current trial step and damping led to Residual Calc ERROR %d. Bailing\n", info);
}
return -1;
}
m_normResidTrial = residErrorNorm(DATA_PTR(m_resid));
m_normResid_1 = m_normResidTrial;
if (m == 0) {
m_normResid_Bound = m_normResidTrial;
}
bool steepEnough = (m_normResidTrial < m_normResid_0 * (0.9 * (1.0 - ff) * (1.0 - ff)* (1.0 - ff) + 0.1));
if (m_normResidTrial < 1.0 || steepEnough) {
if (m_print_flag >= 5) {
if (m_normResidTrial < 1.0) {
printf("\t dampStep(): Current trial step and damping"
" coefficient accepted because residTrial test step < 1:\n");
printf("\t resid0 = %g, residTrial = %g\n", m_normResid_0, m_normResidTrial);
} else if (steepEnough) {
printf("\t dampStep(): Current trial step and damping"
" coefficient accepted because resid0 > residTrial and steep enough:\n");
printf("\t resid0 = %g, residTrial = %g\n", m_normResid_0, m_normResidTrial);
} else {
printf("\t dampStep(): Current trial step and damping"
" coefficient accepted because residual solution damping is turned off:\n");
printf("\t resid0 = %g, residTrial = %g\n", m_normResid_0, m_normResidTrial);
}
}
/*
* We aren't going to solve the system if we don't need to. Therefore, return an estimate
* of the next solution update based on the ratio of the residual reduction.
*/
if (m_normResid_0 > 0.0) {
stepNorm_2 = stepNorm_1 * m_normResidTrial / m_normResid_0;
} else {
stepNorm_2 = 0;
}
if (m_normResidTrial < 1.0) {
retnTrial = 3;
} else {
retnTrial = 4;
}
break;
}
// Compute the next undamped step, step1[], that would result if y1[] were accepted.
// We now have two steps that we have calculated step0[] and step1[]
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
info = doNewtonSolve(time_curr, y_n_1, ydot_n_1, step_2, jac);
} else {
info = doNewtonSolve(time_curr, y_n_1, ydot_n_curr, step_2, jac);
}
if (info) {
if (m_print_flag > 0) {
printf("\t\t\tdampStep: current trial step and damping led to LAPACK ERROR %d. Bailing\n", info);
}
return -1;
}
// compute the weighted norm of step1
stepNorm_2 = solnErrorNorm(step_2);
// write log information
if (m_print_flag >= 5) {
print_solnDelta_norm_contrib((const doublereal*) step_1_orig, "DeltaSoln",
(const doublereal*) step_2, "DeltaSolnTrial",
"dampNewt: Important Entries for Weighted Soln Updates:",
y_n_curr, y_n_1, ff, 5);
}
if (m_print_flag >= 4) {
printf("\t\t\tdampStep(): s1 = %g, s2 = %g, dampBound = %g,"
"dampRes = %g\n", stepNorm_1, stepNorm_2, m_dampBound, m_dampRes);
}
// if the norm of s1 is less than the norm of s0, then
// accept this damping coefficient. Also accept it if this
// step would result in a converged solution. Otherwise,
// decrease the damping coefficient and try again.
if (stepNorm_2 < 0.8 || stepNorm_2 < stepNorm_1) {
if (stepNorm_2 < 1.0) {
if (m_print_flag >= 3) {
if (stepNorm_2 < 1.0) {
printf("\t\t\tdampStep: current trial step and damping coefficient accepted because test step < 1\n");
printf("\t\t\t s2 = %g, s1 = %g\n", stepNorm_2, stepNorm_1);
}
}
retnTrial = 2;
} else {
retnTrial = 1;
}
break;
} else {
if (m_print_flag > 1) {
printf("\t\t\tdampStep: current step rejected: (s1 = %g > "
"s0 = %g)", stepNorm_2, stepNorm_1);
if (m < (NDAMP-1)) {
printf(" Decreasing damping factor and retrying");
} else {
printf(" Giving up!!!");
}
printf("\n");
}
}
num_backtracks++;
m_dampRes /= DampFactor;
}
// If a damping coefficient was found, return 1 if the
// solution after stepping by the damped step would represent
// a converged solution, and return 0 otherwise. If no damping
// coefficient could be found, return NSOLN_RETN_FAIL_DAMPSTEP.
if (m < NDAMP) {
if (m_print_flag >= 4) {
printf("\t dampStep(): current trial step accepted retnTrial = %d, its = %d, damp = %g\n", retnTrial, m+1, ff);
}
return retnTrial;
} else {
if (stepNorm_2 < 0.5 && (stepNorm_1 < 0.5)) {
if (m_print_flag >= 4) {
printf("\t dampStep(): current trial step accepted kindof retnTrial = %d, its = %d, damp = %g\n", 2, m+1, ff);
}
return 2;
}
if (stepNorm_2 < 1.0) {
if (m_print_flag >= 4) {
printf("\t dampStep(): current trial step accepted and soln converged retnTrial ="
"%d, its = %d, damp = %g\n", 0, m+1, ff);
}
return 0;
}
}
if (m_print_flag >= 4) {
printf("\t dampStep(): current direction is rejected! retnTrial = %d, its = %d, damp = %g\n",
NSOLN_RETN_FAIL_DAMPSTEP, m+1, ff);
}
return NSOLN_RETN_FAIL_DAMPSTEP;
}
//====================================================================================================================
// Damp using the dog leg approach
/*
*
* @param time_curr INPUT Current value of the time
* @param y_n_curr INPUT Current value of the solution vector
* @param ydot_n_curr INPUT Current value of the derivative of the solution vector
* @param step_1 INPUT First trial step for the first iteration
* @param y_n_1 INPUT First trial value of the solution vector
* @param ydot_n_1 INPUT First trial value of the derivative of the solution vector
* @param s1 OUTPUT Norm of the vector step_1
* @param jac INPUT jacobian
* @param numTrials OUTPUT number of trials taken in the current damping step
*
*
* @return 1 Success: Good step was taken. The predicted residual norm is less than one
* 2 Success: Good step: Next step's norm is less than 0.8
* 3 Success: The final residual is less than 1.0
* A predicted deltaSoln1 is not produced however. s1 is estimated.
* 4 Success: The final residual is less than the residual from the previous step.
* A predicted deltaSoln1 is not produced however. s1 is estimated.
* 0 Unknown Uncertain Success: s1 is about the same as s0
* NSOLN_RETN_FAIL_DAMPSTEP
* Unsuccessful step. Can not find a damping coefficient that is suitable
*/
int NonlinearSolver::dampDogLeg(const doublereal time_curr, const doublereal* y_n_curr,
const doublereal* ydot_n_curr, std::vector<doublereal> & step_1,
doublereal* const y_n_1, doublereal* const ydot_n_1,
doublereal& stepNorm_1, doublereal& stepNorm_2, GeneralMatrix& jac, int& numTrials)
{
doublereal lambda;
int info;
bool success = false;
bool haveASuccess = false;
doublereal trustDeltaOld = trustDelta_;
doublereal* stepLastGood = DATA_PTR(m_wksp);
//--------------------------------------------
// Attempt damped step
//--------------------------------------------
// damping coefficient starts at 1.0
m_dampRes = 1.0;
int m;
doublereal tlen;
for (m = 0; m < NDAMP; m++) {
numTrials++;
/*
* Find the initial value of lambda that satisfies the trust distance, trustDelta_
*/
dogLegID_ = calcTrustIntersection(trustDelta_, lambda, dogLegAlpha_);
if (m_print_flag >= 4) {
tlen = trustRegionLength();
printf("\t\t dampDogLeg: trust region with length %13.5E has intersection at leg = %d, alpha = %g, lambda = %g\n",
tlen, dogLegID_, dogLegAlpha_, lambda);
}
/*
* Figure out the new step vector, step_1, based on (leg, alpha). Here we are using the
* intersection of the trust oval with the dog-leg curve.
*/
fillDogLegStep(dogLegID_, dogLegAlpha_, step_1);
/*
* OK, now that we have step0, Bound the step
*/
m_dampBound = boundStep(y_n_curr, DATA_PTR(step_1));
/*
* Decrease the step length if we are bound
*/
if (m_dampBound < 1.0) {
for (size_t j = 0; j < neq_; j++) {
step_1[j] = step_1[j] * m_dampBound;
}
}
/*
* Calculate the new solution value y1[] given the step size
*/
for (size_t j = 0; j < neq_; j++) {
y_n_1[j] = y_n_curr[j] + step_1[j];
}
/*
* Calculate the new solution time derivative given the step size
*/
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
calc_ydot(m_order, y_n_1, ydot_n_1);
}
/*
* OK, we have the step0. Now, ask the question whether it satisfies the acceptance criteria
* as a good step. The overall outcome is returned in the variable info.
*/
info = decideStep(time_curr, dogLegID_, dogLegAlpha_, y_n_curr, ydot_n_curr, step_1,
y_n_1, ydot_n_1, trustDeltaOld);
m_normResid_Bound = m_normResid_1;
/*
* The algorithm failed to find a solution vector sufficiently different than the current point
*/
if (info == -1) {
if (m_print_flag >= 1) {
doublereal stepNorm = solnErrorNorm(DATA_PTR(step_1));
printf("\t\t dampDogLeg: Current direction rejected, update became too small %g\n", stepNorm);
success = false;
break;
}
}
if (info == -2) {
if (m_print_flag >= 1) {
printf("\t\t dampDogLeg: current trial step and damping led to LAPACK ERROR %d. Bailing\n", info);
success = false;
break;
}
}
if (info == 0) {
success = true;
break;
}
if (info == 3) {
haveASuccess = true;
// Store the good results in stepLastGood
mdp::mdp_copy_dbl_1(DATA_PTR(stepLastGood), CONSTD_DATA_PTR(step_1), (int) neq_);
// Within the program decideStep(), we have already increased the value of trustDelta_. We store the
// value of step0 in step1, recalculate a larger step0 in the next fillDogLegStep(),
// and then attempt to see if the larger step works in the next iteration
}
if (info == 2) {
// Step was a failure. If we had a previous success with a smaller stepsize, haveASuccess is true
// and we execute the next block and break. If we didn't have a previous success, trustDelta_ has
// already been decreased in the decideStep() routine. We go back and try another iteration with
// a smaller trust region.
if (haveASuccess) {
mdp::mdp_copy_dbl_1(DATA_PTR(step_1), CONSTD_DATA_PTR(stepLastGood), (int) neq_);
for (size_t j = 0; j < neq_; j++) {
y_n_1[j] = y_n_curr[j] + step_1[j];
}
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
calc_ydot(m_order, y_n_1, ydot_n_1);
}
success = true;
break;
} else {
}
}
}
/*
* Estimate s1, the norm after the next step
*/
stepNorm_1 = solnErrorNorm(DATA_PTR(step_1));
stepNorm_2 = stepNorm_1;
if (m_dampBound < 1.0) {
stepNorm_2 /= m_dampBound;
}
stepNorm_2 /= lambda;
stepNorm_2 *= m_normResidTrial / m_normResid_0;
if (success) {
if (m_normResidTrial < 1.0) {
if (normTrust_Newton_ < trustDelta_ && m_dampBound == 1.0) {
return 1;
} else {
return 0;
}
}
return 0;
}
return NSOLN_RETN_FAIL_DAMPSTEP;
}
//====================================================================================================================
// Decide whether the current step is acceptable and adjust the trust region size
/*
* This is an extension of algorithm 6.4.5 of Dennis and Schnabel.
*
* Here we decide whether to accept the current step
* At the end of the calculation a new estimate of the trust region is calculated
*
* @param time_curr INPUT Current value of the time
* @param leg INPUT Leg of the dogleg that we are on
* @param alpha INPUT Distance down that leg that we are on
* @param y0 INPUT Current value of the solution vector
* @param ydot0 INPUT Current value of the derivative of the solution vector
* @param step0 INPUT Trial step
* @param y1 OUTPUT Solution values at the conditions which are evalulated for success
* @param ydot1 OUTPUT Time derivates of solution at the conditions which are evalulated for success
* @param trustDeltaOld INPUT Value of the trust length at the old conditions
*
*
* @return This function returns a code which indicates whether the step will be accepted or not.
* 3 Step passed with flying colors. Try redoing the calculation with a bigger trust region.
* 2 Step didn't pass deltaF requirement. Decrease the size of the next trust region for a retry and return
* 0 The step passed.
* -1 The step size is now too small (||d || < 0.1). A really small step isn't decreasing the function.
* This is an error condition.
* -2 Current value of the solution vector caused a residual error in its evaluation.
* Step is a failure, and the step size must be reduced in order to proceed further.
*/
int NonlinearSolver::decideStep(const doublereal time_curr, int leg, doublereal alpha,
const doublereal* const y_n_curr,
const doublereal* const ydot_n_curr, const std::vector<doublereal> & step_1,
const doublereal* const y_n_1, const doublereal* const ydot_n_1,
doublereal trustDeltaOld)
{
int retn = 2;
int info;
doublereal ll;
// Calculate the solution step length
doublereal stepNorm = solnErrorNorm(DATA_PTR(step_1));
// Calculate the initial (R**2 * neq) value for the old function
doublereal normResid0_2 = m_normResid_0 * m_normResid_0 * neq_;
// Calculate the distance to the cauchy point
doublereal cauchyDistanceNorm = solnErrorNorm(DATA_PTR(deltaX_CP_));
// This is the expected inital rate of decrease in the cauchy direction.
// -> This is Eqn. 29 = Rhat dot Jhat dy / || d ||
doublereal funcDecreaseSDExp = RJd_norm_ / cauchyDistanceNorm * lambdaStar_;
if (funcDecreaseSDExp > 0.0) {
if (m_print_flag >= 5) {
printf("\t\tdecideStep(): Unexpected condition -> cauchy slope is positive\n");
}
}
/*
* Calculate the residual that would result if y1[] were the new solution vector.
* The Lagged solution components are kept lagged here. Unfortunately, it just doesn't work in some cases to use a
* Jacobian from a lagged state and then use a residual from an unlagged condition. The linear model doesn't
* agree with the nonlinear model.
* -> m_resid[] contains the result of the residual calculation
*/
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
info = doResidualCalc(time_curr, solnType_, y_n_1, ydot_n_1, Base_LaggedSolutionComponents);
} else {
info = doResidualCalc(time_curr, solnType_, y_n_1, ydot_n_curr, Base_LaggedSolutionComponents);
}
if (info != 1) {
if (m_print_flag >= 2) {
printf("\t\tdecideStep: current trial step and damping led to Residual Calc ERROR %d. Bailing\n", info);
}
return -2;
}
/*
* Ok we have a successful new residual. Calculate the normalized residual value and store it in
* m_normResidTrial
*/
m_normResidTrial = residErrorNorm(DATA_PTR(m_resid));
doublereal normResidTrial_2 = neq_ * m_normResidTrial * m_normResidTrial;
/*
* We have a minimal acceptance test for passage. deltaf < 1.0E-4 (CauchySlope) (deltS)
* This is the condition that D&S use in 6.4.5
*/
doublereal funcDecrease = 0.5 * (normResidTrial_2 - normResid0_2);
doublereal acceptableDelF = funcDecreaseSDExp * stepNorm * 1.0E-4;
if (funcDecrease < acceptableDelF) {
m_normResid_1 = m_normResidTrial;
m_normResid_1 = m_normResidTrial;
retn = 0;
if (m_print_flag >= 4) {
printf("\t\t decideStep: Norm Residual(leg=%1d, alpha=%10.2E) = %11.4E passes\n",
dogLegID_, dogLegAlpha_, m_normResidTrial);
}
} else {
if (m_print_flag >= 4) {
printf("\t\t decideStep: Norm Residual(leg=%1d, alpha=%10.2E) = %11.4E failes\n",
dogLegID_, dogLegAlpha_, m_normResidTrial);
}
trustDelta_ *= 0.33;
CurrentTrustFactor_ *= 0.33;
retn = 2;
// error condition if step is getting too small
if (rtol_ * stepNorm < 1.0E-6) {
retn = -1;
}
return retn;
}
/*
* Figure out the next trust region. We are here iff retn = 0
*
* If we had to bounds delta the update, decrease the trust region
*/
if (m_dampBound < 1.0) {
// trustDelta_ *= 0.5;
// NextTrustFactor_ *= 0.5;
// ll = trustRegionLength();
// if (m_print_flag >= 5) {
// printf("\t\tdecideStep(): Trust region decreased from %g to %g due to bounds constraint\n", ll*2, ll);
//}
} else {
retn = 0;
/*
* Calculate the expected residual from the quadratic model
*/
doublereal expectedNormRes = expectedResidLeg(leg, alpha);
doublereal expectedFuncDecrease = 0.5 * (neq_ * expectedNormRes * expectedNormRes - normResid0_2);
if (funcDecrease > 0.1 * expectedFuncDecrease) {
if ((m_normResidTrial > 0.5 * m_normResid_0) && (m_normResidTrial > 0.1)) {
trustDelta_ *= 0.5;
NextTrustFactor_ *= 0.5;
ll = trustRegionLength();
if (m_print_flag >= 4) {
printf("\t\t decideStep: Trust region decreased from %g to %g due to bad quad approximation\n",
ll*2, ll);
}
}
} else {
/*
* If we are doing well, consider increasing the trust region and recalculating
*/
if (funcDecrease < 0.8 * expectedFuncDecrease || (m_normResidTrial < 0.33 * m_normResid_0)) {
if (trustDelta_ <= trustDeltaOld && (leg != 2 || alpha < 0.75)) {
trustDelta_ *= 2.0;
CurrentTrustFactor_ *= 2;
adjustUpStepMinimums();
ll = trustRegionLength();
if (m_print_flag >= 4) {
if (m_normResidTrial < 0.33 * m_normResid_0) {
printf("\t\t decideStep: Redo line search with trust region increased from %g to %g due to good nonlinear behavior\n",
ll*0.5, ll);
} else {
printf("\t\t decideStep: Redi line search with trust region increased from %g to %g due to good linear model approximation\n",
ll*0.5, ll);
}
}
retn = 3;
} else {
/*
* Increase the size of the trust region for the next calculation
*/
if (m_normResidTrial < 0.99 * expectedNormRes || (m_normResidTrial < 0.20 * m_normResid_0) ||
(funcDecrease < -1.0E-50 && (funcDecrease < 0.9 *expectedFuncDecrease))) {
if (leg == 2 && alpha == 1.0) {
ll = trustRegionLength();
if (ll < 2.0 * m_normDeltaSoln_Newton) {
trustDelta_ *= 2.0;
NextTrustFactor_ *= 2.0;
adjustUpStepMinimums();
ll = trustRegionLength();
if (m_print_flag >= 4) {
printf("\t\t decideStep: Trust region further increased from %g to %g next step due to good linear model behavior\n",
ll*0.5, ll);
}
}
} else {
ll = trustRegionLength();
trustDelta_ *= 2.0;
NextTrustFactor_ *= 2.0;
adjustUpStepMinimums();
ll = trustRegionLength();
if (m_print_flag >= 4) {
printf("\t\t decideStep: Trust region further increased from %g to %g next step due to good linear model behavior\n",
ll*0.5, ll);
}
}
}
}
}
}
}
return retn;
}
//====================================================================================================================
/*
* solve_nonlinear_problem():
*
* Find the solution to F(X) = 0 by damped Newton iteration. On
* entry, x0 contains an initial estimate of the solution. On
* successful return, x1 contains the converged solution.
*
* SolnType = TRANSIENT -> we will assume we are relaxing a transient
* equation system for now. Will make it more general later,
* if an application comes up.
*
* @return A positive value indicates a successful convergence
* -1 Failed convergence
*/
int NonlinearSolver::solve_nonlinear_problem(int SolnType, doublereal* const y_comm, doublereal* const ydot_comm,
doublereal CJ, doublereal time_curr, GeneralMatrix& jac,
int& num_newt_its, int& num_linear_solves,
int& num_backtracks, int loglevelInput)
{
clockWC wc;
int convRes = 0;
solnType_ = SolnType;
int info = 0;
num_linear_solves -= m_numTotalLinearSolves;
int retnDamp = 0;
int retnCode = 0;
bool forceNewJac = false;
if (jacCopyPtr_) {
delete jacCopyPtr_;
}
jacCopyPtr_ = jac.duplMyselfAsGeneralMatrix();
doublereal stepNorm_1;
doublereal stepNorm_2;
#ifdef DEBUG_MODE
int legBest;
doublereal alphaBest;
#endif
bool trInit = false;
mdp::mdp_copy_dbl_1(DATA_PTR(m_y_n_curr), DATA_PTR(y_comm), (int) neq_);
if (SolnType != NSOLN_TYPE_STEADY_STATE || ydot_comm) {
mdp::mdp_copy_dbl_1(DATA_PTR(m_ydot_n_curr), ydot_comm, (int) neq_);
mdp::mdp_copy_dbl_1(DATA_PTR(m_ydot_n_1), ydot_comm, (int) neq_);
}
// Redo the solution weights every time we enter the function
createSolnWeights(DATA_PTR(m_y_n_curr));
m_normDeltaSoln_Newton = 1.0E1;
bool frst = true;
num_newt_its = 0;
num_backtracks = 0;
int i_numTrials;
m_print_flag = loglevelInput;
if (trustRegionInitializationMethod_ == 0) {
trInit = true;
} else if (trustRegionInitializationMethod_ == 1) {
trInit = true;
initializeTrustRegion();
} else {
mdp::mdp_init_dbl_1(DATA_PTR(deltaX_trust_), 1.0, (int) neq_);
trustDelta_ = 1.0;
}
if (m_print_flag == 2 || m_print_flag == 3) {
printf("\tsolve_nonlinear_problem():\n\n");
if (doDogLeg_) {
printf("\tWt Iter Resid NewJac log(CN)| dRdS_CDexp dRdS_CD dRdS_Newtexp dRdS_Newt |"
"DS_Cauchy DS_Newton DS_Trust | legID legAlpha Fbound | CTF NTF | nTr|"
"DS_Final ResidLag ResidFull\n");
printf("\t---------------------------------------------------------------------------------------------------"
"--------------------------------------------------------------------------------\n");
} else {
printf("\t Wt Iter Resid NewJac | Fbound ResidBound | DampIts Fdamp DS_Step1 DS_Step2"
"ResidLag | DS_Damp DS_Newton ResidFull\n");
printf("\t--------------------------------------------------------------------------------------------------"
"----------------------------------\n");
}
}
while (1 > 0) {
CurrentTrustFactor_ = 1.0;
NextTrustFactor_ = 1.0;
ResidWtsReevaluated_ = false;
i_numTrials = 0;
/*
* Increment Newton Solve counter
*/
m_numTotalNewtIts++;
num_newt_its++;
m_numLocalLinearSolves = 0;
if (m_print_flag > 3) {
printf("\t");
print_line("=", 119);
printf("\tsolve_nonlinear_problem(): iteration %d:\n",
num_newt_its);
}
/*
* If we are far enough away from the solution, redo the solution weights and the trust vectors.
*/
if (m_normDeltaSoln_Newton > 1.0E2) {
createSolnWeights(DATA_PTR(m_y_n_curr));
#ifdef DEBUG_MODE
if (trInit) {
readjustTrustVector();
}
#else
if (doDogLeg_ && trInit) {
readjustTrustVector();
}
#endif
} else {
// Do this stuff every 5 iterations
if ((num_newt_its % 5) == 1) {
createSolnWeights(DATA_PTR(m_y_n_curr));
#ifdef DEBUG_MODE
if (trInit) {
readjustTrustVector();
}
#else
if (doDogLeg_ && trInit) {
readjustTrustVector();
}
#endif
}
}
/*
* Set default values of Delta bounds constraints
*/
if (!m_manualDeltaStepSet) {
setDefaultDeltaBoundsMagnitudes();
}
// Check whether the Jacobian should be re-evaluated.
forceNewJac = true;
if (forceNewJac) {
if (m_print_flag > 3) {
printf("\t solve_nonlinear_problem(): Getting a new Jacobian\n");
}
info = beuler_jac(jac, DATA_PTR(m_resid), time_curr, CJ, DATA_PTR(m_y_n_curr),
DATA_PTR(m_ydot_n_curr), num_newt_its);
if (info != 1) {
if (m_print_flag > 0) {
printf("\t solve_nonlinear_problem(): Jacobian Formation Error: %d Bailing\n", info);
}
retnDamp = NSOLN_RETN_JACOBIANFORMATIONERROR ;
goto done;
}
} else {
if (m_print_flag > 1) {
printf("\t solve_nonlinear_problem(): Solving system with old jacobian\n");
}
}
/*
* Go get new scales
*/
calcColumnScales();
/*
* Calculate the base residual
*/
if (m_print_flag >= 6) {
printf("\t solve_nonlinear_problem(): Calculate the base residual\n");
}
info = doResidualCalc(time_curr, NSOLN_TYPE_STEADY_STATE, DATA_PTR(m_y_n_curr), DATA_PTR(m_ydot_n_curr));
if (info != 1) {
if (m_print_flag > 0) {
printf("\t solve_nonlinear_problem(): Residual Calc ERROR %d. Bailing\n", info);
}
retnDamp = NSOLN_RETN_RESIDUALFORMATIONERROR;
goto done;
}
/*
* Scale the matrix and the rhs, if they aren't already scaled
* Figure out and store the residual scaling factors.
*/
scaleMatrix(jac, DATA_PTR(m_y_n_curr), DATA_PTR(m_ydot_n_curr), time_curr, num_newt_its);
/*
* Optional print out the initial residual
*/
if (m_print_flag >= 6) {
m_normResid_0 = residErrorNorm(DATA_PTR(m_resid), "Initial norm of the residual", 10, DATA_PTR(m_y_n_curr));
} else {
m_normResid_0 = residErrorNorm(DATA_PTR(m_resid), "Initial norm of the residual", 0, DATA_PTR(m_y_n_curr));
if (m_print_flag == 4 || m_print_flag == 5) {
printf("\t solve_nonlinear_problem(): Initial Residual Norm = %13.4E\n", m_normResid_0);
}
}
#ifdef DEBUG_MODE
if (m_print_flag > 3) {
printf("\t solve_nonlinear_problem(): Calculate the steepest descent direction and Cauchy Point\n");
}
m_normDeltaSoln_CP = doCauchyPointSolve(jac);
#else
if (doDogLeg_) {
if (m_print_flag > 3) {
printf("\t solve_nonlinear_problem(): Calculate the steepest descent direction and Cauchy Point\n");
}
m_normDeltaSoln_CP = doCauchyPointSolve(jac);
}
#endif
// compute the undamped Newton step
if (doAffineSolve_) {
if (m_print_flag >= 4) {
printf("\t solve_nonlinear_problem(): Calculate the Newton direction via an Affine solve\n");
}
info = doAffineNewtonSolve(DATA_PTR(m_y_n_curr), DATA_PTR(m_ydot_n_curr), DATA_PTR(deltaX_Newton_), jac);
} else {
if (m_print_flag >= 4) {
printf("\t solve_nonlinear_problem(): Calculate the Newton direction via a Newton solve\n");
}
info = doNewtonSolve(time_curr, DATA_PTR(m_y_n_curr), DATA_PTR(m_ydot_n_curr), DATA_PTR(deltaX_Newton_), jac);
}
if (info) {
retnDamp = NSOLN_RETN_MATRIXINVERSIONERROR;
if (m_print_flag > 0) {
printf("\t solve_nonlinear_problem(): Matrix Inversion Error: %d Bailing\n", info);
}
goto done;
}
mdp::mdp_copy_dbl_1(DATA_PTR(m_step_1), CONSTD_DATA_PTR(deltaX_Newton_), (int) neq_);
if (m_print_flag >= 6) {
m_normDeltaSoln_Newton = solnErrorNorm(DATA_PTR(deltaX_Newton_), "Initial Undamped Newton Step of the iteration", 10);
} else {
m_normDeltaSoln_Newton = solnErrorNorm(DATA_PTR(deltaX_Newton_), "Initial Undamped Newton Step of the iteration", 0);
}
if (m_numTotalNewtIts == 1) {
if (trustRegionInitializationMethod_ == 2 || trustRegionInitializationMethod_ == 3) {
if (m_print_flag > 3) {
if (trustRegionInitializationMethod_ == 2) {
printf("\t solve_nonlinear_problem(): Initialize the trust region size as the length of the Cauchy Vector times %f\n",
trustRegionInitializationFactor_);
} else {
printf("\t solve_nonlinear_problem(): Initialize the trust region size as the length of the Newton Vector times %f\n",
trustRegionInitializationFactor_);
}
}
initializeTrustRegion();
trInit = true;
}
}
if (doDogLeg_) {
#ifdef DEBUG_MODE
doublereal trustD = calcTrustDistance(m_step_1);
if (m_print_flag >= 4) {
if (trustD > trustDelta_) {
printf("\t\t Newton's method step size, %g trustVectorUnits, larger than trust region, %g trustVectorUnits\n",
trustD, trustDelta_);
printf("\t\t Newton's method step size, %g trustVectorUnits, larger than trust region, %g trustVectorUnits\n",
trustD, trustDelta_);
} else {
printf("\t\t Newton's method step size, %g trustVectorUnits, smaller than trust region, %g trustVectorUnits\n",
trustD, trustDelta_);
}
}
#endif
}
/*
* Filter out bad directions
*/
filterNewStep(time_curr, DATA_PTR(m_y_n_curr), DATA_PTR(m_step_1));
if (s_print_DogLeg && m_print_flag >= 4) {
printf("\t solve_nonlinear_problem(): Compare descent rates for Cauchy and Newton directions\n");
descentComparison(time_curr, DATA_PTR(m_ydot_n_curr), DATA_PTR(m_ydot_n_1), i_numTrials);
} else {
if (doDogLeg_) {
descentComparison(time_curr, DATA_PTR(m_ydot_n_curr), DATA_PTR(m_ydot_n_1), i_numTrials);
}
}
if (doDogLeg_) {
setupDoubleDogleg();
#ifdef DEBUG_MODE
if (s_print_DogLeg && m_print_flag >= 5) {
printf("\t solve_nonlinear_problem(): Compare Linear and nonlinear residuals along double dog-leg path\n");
residualComparisonLeg(time_curr, DATA_PTR(m_ydot_n_curr), legBest, alphaBest);
}
#endif
if (m_print_flag >= 4) {
printf("\t solve_nonlinear_problem(): Calculate damping along dog-leg path to ensure residual decrease\n");
}
retnDamp = dampDogLeg(time_curr, DATA_PTR(m_y_n_curr), DATA_PTR(m_ydot_n_curr),
m_step_1, DATA_PTR(m_y_n_1), DATA_PTR(m_ydot_n_1), stepNorm_1, stepNorm_2, jac, i_numTrials);
}
#ifdef DEBUG_MODE
else {
if (s_print_DogLeg && m_print_flag >= 5) {
printf("\t solve_nonlinear_problem(): Compare Linear and nonlinear residuals along double dog-leg path\n");
residualComparisonLeg(time_curr, DATA_PTR(m_ydot_n_curr), legBest, alphaBest);
}
}
#endif
// Damp the Newton step
/*
* On return the recommended new solution and derivatisve is located in:
* y_new
* y_dot_new
* The update delta vector is located in
* stp1
* The estimate of the solution update norm for the next step is located in
* s1
*/
if (!doDogLeg_) {
retnDamp = dampStep(time_curr, DATA_PTR(m_y_n_curr), DATA_PTR(m_ydot_n_curr),
DATA_PTR(m_step_1), DATA_PTR(m_y_n_1), DATA_PTR(m_ydot_n_1),
DATA_PTR(m_wksp_2), stepNorm_2, jac, frst, i_numTrials);
frst = false;
num_backtracks += i_numTrials;
stepNorm_1 = solnErrorNorm(DATA_PTR(m_step_1));
}
/*
* Impose the minimum number of newton iterations critera
*/
if (num_newt_its < m_min_newt_its) {
if (retnDamp > NSOLN_RETN_CONTINUE) {
if (m_print_flag > 2) {
printf("\t solve_nonlinear_problem(): Damped Newton successful (m=%d) but minimum newton"
"iterations not attained. Resolving ...\n", retnDamp);
}
retnDamp = NSOLN_RETN_CONTINUE;
}
}
/*
* Impose max newton iteration
*/
if (num_newt_its > maxNewtIts_) {
retnDamp = NSOLN_RETN_MAXIMUMITERATIONSEXCEEDED;
if (m_print_flag > 1) {
printf("\t solve_nonlinear_problem(): Damped newton unsuccessful (max newts exceeded) sfinal = %g\n",
stepNorm_1);
}
}
/*
* Do a full residual calculation with the unlagged solution components.
* Then get the norm of the residual
*/
info = doResidualCalc(time_curr, NSOLN_TYPE_STEADY_STATE, DATA_PTR(m_y_n_1), DATA_PTR(m_ydot_n_1));
if (info != 1) {
if (m_print_flag > 0) {
printf("\t solve_nonlinear_problem(): current trial step and damping led to Residual Calc "
"ERROR %d. Bailing\n", info);
}
retnDamp = NSOLN_RETN_RESIDUALFORMATIONERROR;
goto done;
}
if (m_print_flag >= 4) {
m_normResid_full = residErrorNorm(DATA_PTR(m_resid), " Resulting full residual norm", 10, DATA_PTR(m_y_n_1));
if (fabs(m_normResid_full - m_normResid_1) > 1.0E-3 * (m_normResid_1 + m_normResid_full + 1.0E-4)) {
if (m_print_flag >= 4) {
printf("\t solve_nonlinear_problem(): Full residual norm changed from %g to %g due to "
"lagging of components\n", m_normResid_1, m_normResid_full);
}
}
} else {
m_normResid_full = residErrorNorm(DATA_PTR(m_resid));
}
/*
* Check the convergence criteria
*/
convRes = 0;
if (retnDamp > NSOLN_RETN_CONTINUE) {
convRes = convergenceCheck(retnDamp, stepNorm_1);
}
bool m_filterIntermediate = false;
if (m_filterIntermediate) {
if (retnDamp == NSOLN_RETN_CONTINUE) {
(void) filterNewSolution(time_n, DATA_PTR(m_y_n_1), DATA_PTR(m_ydot_n_1));
}
}
// Exchange new for curr solutions
if (retnDamp >= NSOLN_RETN_CONTINUE) {
mdp::mdp_copy_dbl_1(DATA_PTR(m_y_n_curr), CONSTD_DATA_PTR(m_y_n_1), (int) neq_);
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
calc_ydot(m_order, DATA_PTR(m_y_n_curr), DATA_PTR(m_ydot_n_curr));
}
}
if (m_print_flag == 2 || m_print_flag == 3) {
// printf("\t Iter Resid NewJac | Fbound | ResidBound | Fdamp DampIts | DeltaSolnNewton ResidFinal \n");
if (ResidWtsReevaluated_) {
printf("\t*");
} else {
printf("\t ");
}
printf(" %3d %11.3E", num_newt_its, m_normResid_0);
bool m_jacAge = false;
if (!m_jacAge) {
printf(" Y ");
} else {
printf(" N ");
}
if (doDogLeg_) {
printf("%5.1F |", log10(m_conditionNumber));
// printf("\t Iter Resid NewJac | DS_Cauchy DS_Newton DS_Trust | legID legAlpha Fbound | | DS_F ResidFinal \n");
printf("%10.3E %10.3E %10.3E %10.3E|", ResidDecreaseSDExp_, ResidDecreaseSD_,
ResidDecreaseNewtExp_, ResidDecreaseNewt_);
printf("%10.3E %10.3E %10.3E|", m_normDeltaSoln_CP , m_normDeltaSoln_Newton, norm_deltaX_trust_ * trustDelta_);
printf("%2d %10.2E %10.2E", dogLegID_ , dogLegAlpha_, m_dampBound);
printf("| %3.2f %3.2f |", CurrentTrustFactor_, NextTrustFactor_);
printf(" %2d ", i_numTrials);
printf("| %10.3E %10.3E %10.3E", stepNorm_1, m_normResid_1, m_normResid_full);
} else {
printf(" |");
printf("%10.2E %10.3E |", m_dampBound, m_normResid_Bound);
printf("%2d %10.2E %10.3E %10.3E %10.3E", i_numTrials + 1, m_dampRes,
stepNorm_1 / (m_dampRes * m_dampBound), stepNorm_2, m_normResid_1);
printf("| %10.3E %10.3E %10.3E", stepNorm_1, m_normDeltaSoln_Newton, m_normResid_full);
}
printf("\n");
}
if (m_print_flag >= 4) {
if (doDogLeg_) {
if (convRes > 0) {
printf("\t solve_nonlinear_problem(): Problem Converged, stepNorm = %11.3E, reduction of res from %11.3E to %11.3E\n",
stepNorm_1, m_normResid_0, m_normResid_full);
printf("\t");
print_line("=", 119);
} else {
printf("\t solve_nonlinear_problem(): Successfull step taken with stepNorm = %11.3E, reduction of res from %11.3E to %11.3E\n",
stepNorm_1, m_normResid_0, m_normResid_full);
}
} else {
if (convRes > 0) {
printf("\t solve_nonlinear_problem(): Damped Newton iteration successful, nonlin "
"converged, final estimate of the next solution update norm = %-12.4E\n", stepNorm_2);
printf("\t");
print_line("=", 119);
} else if (retnDamp >= NSOLN_RETN_CONTINUE) {
printf("\t solve_nonlinear_problem(): Damped Newton iteration successful, "
"estimate of the next solution update norm = %-12.4E\n", stepNorm_2);
} else {
printf("\t solve_nonlinear_problem(): Damped Newton unsuccessful, final estimate "
"of the next solution update norm = %-12.4E\n", stepNorm_2);
}
}
}
// convergence
if (convRes) {
goto done;
}
// If dampStep fails, first try a new Jacobian if an old
// one was being used. If it was a new Jacobian, then
// return -1 to signify failure.
else if (retnDamp < NSOLN_RETN_CONTINUE) {
goto done;
}
}
done:
if (m_print_flag == 2 || m_print_flag == 3) {
if (convRes > 0) {
if (doDogLeg_) {
if (convRes == 3) {
printf("\t | | "
" | | converged = 3 |(%11.3E) \n", stepNorm_2);
} else {
printf("\t | | "
" | | converged = %1d | %10.3E %10.3E\n", convRes,
stepNorm_2, m_normResidTrial);
}
printf("\t-----------------------------------------------------------------------------------------------------"
"------------------------------------------------------------------------------\n");
} else {
if (convRes == 3) {
printf("\t | "
" | converged = 3 | (%11.3E) \n", stepNorm_2);
} else {
printf("\t | "
" | converged = %1d | %10.3E %10.3E\n", convRes,
stepNorm_2, m_normResidTrial);
}
printf("\t------------------------------------------------------------------------------------"
"-----------------------------------------------\n");
}
}
}
mdp::mdp_copy_dbl_1(y_comm, CONSTD_DATA_PTR(m_y_n_curr), (int) neq_);
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
mdp::mdp_copy_dbl_1(ydot_comm, CONSTD_DATA_PTR(m_ydot_n_curr), (int) neq_);
}
num_linear_solves += m_numTotalLinearSolves;
doublereal time_elapsed = wc.secondsWC();
if (m_print_flag > 1) {
if (retnDamp > 0) {
if (NonlinearSolver::s_TurnOffTiming) {
printf("\tNonlinear problem solved successfully in %d its\n",
num_newt_its);
} else {
printf("\tNonlinear problem solved successfully in %d its, time elapsed = %g sec\n",
num_newt_its, time_elapsed);
}
} else {
printf("\tNonlinear problem failed to solve after %d its\n", num_newt_its);
}
}
retnCode = retnDamp;
if (retnDamp > 0) {
retnCode = NSOLN_RETN_SUCCESS;
}
return retnCode;
}
//====================================================================================================================
// Print solution norm contribution
/*
* Prints out the most important entries to the update to the solution vector for the current step
*
* @param step_1 Raw update vector for the current nonlinear step
* @param stepNorm_1 Norm of the vector step_1
* @param step_2 Raw update vector for the next solution value based on the old matrix
* @param stepNorm_2 Norm of the vector step_2
* @param title title of the printout
* @param y_n_curr Old value of the solution
* @param y_n_1 New value of the solution after damping corrections
* @param damp Value of the damping factor
* @param num_entries Number of entries to print out
*/
void NonlinearSolver::
print_solnDelta_norm_contrib(const doublereal* const step_1,
const char* const stepNorm_1,
const doublereal* const step_2,
const char* const stepNorm_2,
const char* const title,
const doublereal* const y_n_curr,
const doublereal* const y_n_1,
doublereal damp,
size_t num_entries)
{
bool used;
doublereal dmax0, dmax1, error, rel_norm;
printf("\t\t%s currentDamp = %g\n", title, damp);
printf("\t\t I ysolnOld %13s ysolnNewRaw | ysolnNewTrial "
"%10s ysolnNewTrialRaw | solnWeight wtDelSoln wtDelSolnTrial\n", stepNorm_1, stepNorm_2);
std::vector<size_t> imax(num_entries, npos);
printf("\t\t ");
print_line("-", 125);
for (size_t jnum = 0; jnum < num_entries; jnum++) {
dmax1 = -1.0;
for (size_t i = 0; i < neq_; i++) {
used = false;
for (size_t j = 0; j < jnum; j++) {
if (imax[j] == i) {
used = true;
}
}
if (!used) {
error = step_1[i] / m_ewt[i];
rel_norm = sqrt(error * error);
error = step_2[i] / m_ewt[i];
rel_norm += sqrt(error * error);
if (rel_norm > dmax1) {
imax[jnum] = i;
dmax1 = rel_norm;
}
}
}
if (imax[jnum] != npos) {
size_t i = imax[jnum];
error = step_1[i] / m_ewt[i];
dmax0 = sqrt(error * error);
error = step_2[i] / m_ewt[i];
dmax1 = sqrt(error * error);
printf("\t\t %4d %12.4e %12.4e %12.4e | %12.4e %12.4e %12.4e |%12.4e %12.4e %12.4e\n",
i, y_n_curr[i], step_1[i], y_n_curr[i] + step_1[i], y_n_1[i],
step_2[i], y_n_1[i]+ step_2[i], m_ewt[i], dmax0, dmax1);
}
}
printf("\t\t ");
print_line("-", 125);
}
//====================================================================================================================
//! This routine subtracts two numbers for one another
/*!
* This routine subtracts 2 numbers. If the difference is less
* than 1.0E-14 times the magnitude of the smallest number, then diff returns an exact zero.
* It also returns an exact zero if the difference is less than
* 1.0E-300.
*
* returns: a - b
*
* This routine is used in numerical differencing schemes in order
* to avoid roundoff errors resulting in creating Jacobian terms.
* Note: This is a slow routine. However, jacobian errors may cause
* loss of convergence. Therefore, in practice this routine has proved cost-effective.
*
* @param a Value of a
* @param b value of b
*
* @return returns the difference between a and b
*/
static inline doublereal subtractRD(doublereal a, doublereal b)
{
doublereal diff = a - b;
doublereal d = MIN(fabs(a), fabs(b));
d *= 1.0E-14;
doublereal ad = fabs(diff);
if (ad < 1.0E-300) {
diff = 0.0;
}
if (ad < d) {
diff = 0.0;
}
return diff;
}
//====================================================================================================================
/*
*
* Function called by BEuler to evaluate the Jacobian matrix and the
* current residual at the current time step.
* @param N = The size of the equation system
* @param J = Jacobian matrix to be filled in
* @param f = Right hand side. This routine returns the current
* value of the rhs (output), so that it does
* not have to be computed again.
*
* @return Returns a flag to indicate that operation is successful.
* 1 Means a successful operation
* 0 Means an unsuccessful operation
*/
int NonlinearSolver::beuler_jac(GeneralMatrix& J, doublereal* const f,
doublereal time_curr, doublereal CJ,
doublereal* const y, doublereal* const ydot,
int num_newt_its)
{
double* col_j;
int info;
doublereal ysave, ydotsave, dy;
int retn = 1;
/*
* Clear the factor flag
*/
J.clearFactorFlag();
if (m_jacFormMethod == NSOLN_JAC_ANAL) {
/********************************************************************
* Call the function to get a jacobian.
*/
info = m_func->evalJacobian(time_curr, delta_t_n, CJ, y, ydot, J, f);
m_nJacEval++;
m_nfe++;
if (info != 1) {
return info;
}
} else {
if (J.matrixType_ == 0) {
/*******************************************************************
* Generic algorithm to calculate a numerical Jacobian
*/
/*
* Calculate the current value of the rhs given the
* current conditions.
*/
info = m_func->evalResidNJ(time_curr, delta_t_n, y, ydot, f, JacBase_ResidEval);
m_nfe++;
if (info != 1) {
return info;
}
m_nJacEval++;
/*
* Malloc a vector and call the function object to return a set of
* deltaY's that are appropriate for calculating the numerical
* derivative.
*/
doublereal* dyVector = mdp::mdp_alloc_dbl_1((int) neq_, MDP_DBL_NOINIT);
retn = m_func->calcDeltaSolnVariables(time_curr, y, ydot, dyVector, DATA_PTR(m_ewt));
if (s_print_NumJac) {
if (m_print_flag >= 7) {
if (neq_ < 20) {
printf("\t\tUnk m_ewt y dyVector ResN\n");
for (size_t iii = 0; iii < neq_; iii++) {
printf("\t\t %4d %16.8e %16.8e %16.8e %16.8e \n",
iii, m_ewt[iii], y[iii], dyVector[iii], f[iii]);
}
}
}
}
/*
* Loop over the variables, formulating a numerical derivative
* of the dense matrix.
* For the delta in the variable, we will use a variety of approaches
* The original approach was to use the error tolerance amount.
* This may not be the best approach, as it could be overly large in
* some instances and overly small in others.
* We will first protect from being overly small, by using the usual
* sqrt of machine precision approach, i.e., 1.0E-7,
* to bound the lower limit of the delta.
*/
for (size_t j = 0; j < neq_; j++) {
/*
* Get a pointer into the column of the matrix
*/
col_j = (doublereal*) J.ptrColumn(j);
ysave = y[j];
dy = dyVector[j];
//dy = fmaxx(1.0E-6 * m_ewt[j], fabs(ysave)*1.0E-7);
y[j] = ysave + dy;
dy = y[j] - ysave;
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
ydotsave = ydot[j];
ydot[j] += dy * CJ;
}
/*
* Call the function
*/
info = m_func->evalResidNJ(time_curr, delta_t_n, y, ydot, DATA_PTR(m_wksp),
JacDelta_ResidEval, j, dy);
m_nfe++;
if (info != 1) {
mdp::mdp_safe_free((void**) &dyVector);
return info;
}
doublereal diff;
for (size_t i = 0; i < neq_; i++) {
diff = subtractRD(m_wksp[i], f[i]);
col_j[i] = diff / dy;
}
y[j] = ysave;
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
ydot[j] = ydotsave;
}
}
/*
* Release memory
*/
mdp::mdp_safe_free((void**) &dyVector);
} else if (J.matrixType_ == 1) {
size_t ku, kl;
size_t ivec[2];
size_t n = J.nRowsAndStruct(ivec);
kl = ivec[0];
ku = ivec[1];
if (n != neq_) {
printf("we have probs\n");
exit(-1);
}
// --------------------------------- BANDED MATRIX BRAIN DEAD ---------------------------------------------------
info = m_func->evalResidNJ(time_curr, delta_t_n, y, ydot, f, JacBase_ResidEval);
m_nfe++;
if (info != 1) {
return info;
}
m_nJacEval++;
doublereal* dyVector = mdp::mdp_alloc_dbl_1((int) neq_, MDP_DBL_NOINIT);
retn = m_func->calcDeltaSolnVariables(time_curr, y, ydot, dyVector, DATA_PTR(m_ewt));
if (s_print_NumJac) {
if (m_print_flag >= 7) {
if (neq_ < 20) {
printf("\t\tUnk m_ewt y dyVector ResN\n");
for (size_t iii = 0; iii < neq_; iii++) {
printf("\t\t %4d %16.8e %16.8e %16.8e %16.8e \n",
iii, m_ewt[iii], y[iii], dyVector[iii], f[iii]);
}
}
}
}
for (size_t j = 0; j < neq_; j++) {
col_j = (doublereal*) J.ptrColumn(j);
ysave = y[j];
dy = dyVector[j];
y[j] = ysave + dy;
dy = y[j] - ysave;
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
ydotsave = ydot[j];
ydot[j] += dy * CJ;
}
info = m_func->evalResidNJ(time_curr, delta_t_n, y, ydot, DATA_PTR(m_wksp), JacDelta_ResidEval, j, dy);
m_nfe++;
if (info != 1) {
mdp::mdp_safe_free((void**) &dyVector);
return info;
}
doublereal diff;
for (size_t i = j - ku; i <= j + kl; i++) {
if (i >= 0 && i < neq_) {
diff = subtractRD(m_wksp[i], f[i]);
col_j[kl + ku + i - j] = diff / dy;
}
}
y[j] = ysave;
if (solnType_ != NSOLN_TYPE_STEADY_STATE) {
ydot[j] = ydotsave;
}
}
mdp::mdp_safe_free((void**) &dyVector);
double vSmall;
size_t ismall = J.checkRows(vSmall);
if (vSmall < 1.0E-100) {
printf("WE have a zero row, %d\n", ismall);
exit(-1);
}
ismall = J.checkColumns(vSmall);
if (vSmall < 1.0E-100) {
printf("WE have a zero column, %d\n", ismall);
exit(-1);
}
// ---------------------BANDED MATRIX BRAIN DEAD -----------------------
}
}
if (m_print_flag >= 7 && s_print_NumJac) {
if (neq_ < 30) {
printf("\t\tCurrent Matrix and Residual:\n");
printf("\t\t I,J | ");
for (size_t j = 0; j < neq_; j++) {
printf(" %5d ", j);
}
printf("| Residual \n");
printf("\t\t --");
for (size_t j = 0; j < neq_; j++) {
printf("------------");
}
printf("| -----------\n");
for (size_t i = 0; i < neq_; i++) {
printf("\t\t %4d |", i);
for (size_t j = 0; j < neq_; j++) {
printf(" % 11.4E", J(i,j));
}
printf(" | % 11.4E\n", f[i]);
}
printf("\t\t --");
for (size_t j = 0; j < neq_; j++) {
printf("------------");
}
printf("--------------\n");
}
}
/*
* Make a copy of the data. Note, this jacobian copy occurs before any matrix scaling operations.
* It's the raw matrix producted by this routine.
*/
jacCopyPtr_->copyData(J);
return retn;
}
//====================================================================================================================
// Internal function to calculate the time derivative of the solution at the new step
/*
* Previously, the user must have supplied information about the previous time step for this routine to
* work as intended.
*
* @param order of the BDF method
* @param y_curr current value of the solution
* @param ydot_curr Calculated value of the solution derivative that is consistent with y_curr
*/
void NonlinearSolver::
calc_ydot(const int order, const doublereal* const y_curr, doublereal* const ydot_curr) const
{
if (!ydot_curr) {
return;
}
doublereal c1;
switch (order) {
case 0:
case 1: /* First order forward Euler/backward Euler */
c1 = 1.0 / delta_t_n;
for (size_t i = 0; i < neq_; i++) {
ydot_curr[i] = c1 * (y_curr[i] - m_y_nm1[i]);
}
return;
case 2: /* Second order Adams-Bashforth / Trapezoidal Rule */
c1 = 2.0 / delta_t_n;
for (size_t i = 0; i < neq_; i++) {
ydot_curr[i] = c1 * (y_curr[i] - m_y_nm1[i]) - m_ydot_nm1[i];
}
return;
default:
throw CanteraError("calc_ydot()", "Case not covered");
}
}
//====================================================================================================================
// Apply a filtering process to the new step
/*
* @param timeCurrent Current value of the time
* @param y_current current value of the solution
* @param ydot_current Current value of the solution derivative.
*
* @return Returns the norm of the value of the amount filtered
*/
doublereal NonlinearSolver::filterNewStep(const doublereal timeCurrent,
const doublereal* const ybase, doublereal* const step0)
{
doublereal tmp = m_func->filterNewStep(timeCurrent, ybase, step0);
return tmp;
}
//====================================================================================================================
// Apply a filtering process to the new solution
/*
* @param timeCurrent Current value of the time
* @param y_current current value of the solution
* @param ydot_current Current value of the solution derivative.
*
* @return Returns the norm of the value of the amount filtered
*/
doublereal NonlinearSolver::filterNewSolution(const doublereal timeCurrent,
doublereal* const y_current, doublereal* const ydot_current)
{
doublereal tmp = m_func->filterSolnPrediction(timeCurrent, y_current);
return tmp;
}
//====================================================================================================================
// Compute the Residual Weights
/*
* The residual weights are defined here to be equal to the inverse of the row scaling factors used to
* row scale the matrix, after column scaling is used. They are multiplied by rtol and an atol factor
* is added as well so that if the residual is less than 1, then the calculation is deemed to be converged.
*
* The basic idea is that a change in the solution vector on the order of the convergence tolerance
* multiplied by [RJC] which is of order one after row scaling should give you the relative weight
* of the row. Values of the residual for that row can then be normalized by the value of this weight.
* When the tolerance in delta x is achieved, the tolerance in the residual should also be achieved
* and should be checked.
*/
void
NonlinearSolver::computeResidWts()
{
ResidWtsReevaluated_ = true;
if (checkUserResidualTols_ == 1) {
for (size_t i = 0; i < neq_; i++) {
m_residWts[i] = userResidAtol_[i] + userResidRtol_ * m_rowWtScales[i] / neq_;
}
} else {
doublereal sum = 0.0;
for (size_t i = 0; i < neq_; i++) {
m_residWts[i] = m_rowWtScales[i] / neq_;
sum += m_residWts[i];
}
sum /= neq_;
for (size_t i = 0; i < neq_; i++) {
m_residWts[i] = m_ScaleSolnNormToResNorm * (m_residWts[i] + atolBase_ * atolBase_ * sum);
}
if (checkUserResidualTols_ == 2) {
for (size_t i = 0; i < neq_; i++) {
double uR = userResidAtol_[i] + userResidRtol_ * m_rowWtScales[i] / neq_;
m_residWts[i] = MIN(m_residWts[i], uR);
}
}
}
}
//=====================================================================================================================
// return the residual weights
/*
* @param residWts Vector of length neq_
*/
void
NonlinearSolver::getResidWts(doublereal* const residWts) const
{
for (size_t i = 0; i < neq_; i++) {
residWts[i] = (m_residWts)[i];
}
}
//=====================================================================================================================
// Check to see if the nonlinear problem has converged
/*
*
* @return integer is returned. If positive, then the problem has converged
* 1 Successful step was taken: Next step's norm is less than 1.0.
* The final residual norm is less than 1.0.
* 2 Successful step: Next step's norm is less than 0.8.
* This step's norm is less than 1.0.
* The residual norm can be anything.
* 3 Success: The final residual is less than 1.0E-2
* The predicted deltaSoln is below 1.0E-2.
* 0 Not converged yet
*/
int
NonlinearSolver::convergenceCheck(int dampCode, doublereal s1)
{
int retn = 0;
if (m_dampBound < 0.9999) {
return retn;
}
if (m_dampRes < 0.9999) {
return retn;
}
if (dampCode <= 0) {
return retn;
}
if (dampCode == 3) {
if (s1 < 1.0E-2) {
if (m_normResidTrial < 1.0E-6) {
return 3;
}
}
if (s1 < 0.8) {
if (m_normDeltaSoln_Newton < 1.0) {
return 2;
}
}
}
if (dampCode == 4) {
if (s1 < 1.0E-2) {
if (m_normResidTrial < 1.0E-6) {
return 3;
}
}
}
if (s1 < 0.8) {
if (m_normDeltaSoln_Newton < 1.0) {
return 2;
}
}
if (dampCode == 1 || dampCode == 2) {
if (s1 < 1.0) {
if (m_normResidTrial < 1.0) {
return 1;
}
}
}
return retn;
}
//=====================================================================================================================
// Set the absolute tolerances for the solution variables
/*
* Set the absolute tolerances used in the calculation
*
* @param atol Vector of length neq_ that contains the tolerances to be used for the solution variables
*/
void NonlinearSolver::setAtol(const doublereal* const atol)
{
for (size_t i = 0; i < neq_; i++) {
atolk_[i]= atol[i];
}
}
//=====================================================================================================================
// Set the relative tolerances for the solution variables
/*
* Set the relative tolerances used in the calculation for the solution variables.
*
* @param rtol single double
*/
void NonlinearSolver::setRtol(const doublereal rtol)
{
rtol_ = rtol;
}
//=====================================================================================================================
// Set the relative and absolute tolerances for the Residual norm comparisons, if used
/*
*
* residWeightNorm[i] = residAtol[i] + residRtol * m_rowWtScales[i] / neq
*
* @param residNormHandling Parameter that sets the default handling of the residual norms
* 0 The residual weighting vector is calculated to make sure that the solution
* norms are roughly 1 when the residual norm is roughly 1.
* This is the default if this routine is not called.
* 1 Use the user residual norm specified by the parameters in this routine
* 2 Use the minimum value of the residual weights calculcated by method 1 and 2.
* This is the default if this routine is called and this parameter isn't specified.
*/
void NonlinearSolver::setResidualTols(double residRtol, double* residATol, int residNormHandling)
{
if (residNormHandling < 0 || residNormHandling > 2) {
throw CanteraError("NonlinearSolver::setResidualTols()",
"Unknown int for residNormHandling");
}
checkUserResidualTols_ = residNormHandling;
userResidRtol_ = residRtol;
if (residATol) {
userResidAtol_.resize(neq_);
for (size_t i = 0; i < neq_; i++) {
userResidAtol_[i] = residATol[i];
}
} else {
if (residNormHandling ==1 || residNormHandling == 2) {
throw CanteraError("NonlinearSolver::setResidualTols()",
"Must set residATol vector");
}
}
}
//=====================================================================================================================
void NonlinearSolver::setPrintLvl(int printLvl)
{
m_print_flag = printLvl;
}
//=====================================================================================================================
}