/* * @file: RootFind.cpp root finder for 1D problems */ /* * Copyright 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 "cantera/base/ct_defs.h" #include "cantera/numerics/RootFind.h" // turn on debugging for now #ifndef DEBUG_MODE #define DEBUG_MODE #endif #include "cantera/base/global.h" #include "cantera/base/utilities.h" #include "cantera/base/stringUtils.h" /* Standard include files */ #include #include #include #include using namespace std; namespace Cantera { #ifndef SQUARE # define SQUARE(x) ( (x) * (x) ) #endif #ifndef DSIGN #define DSIGN(x) (( (x) == (0.0) ) ? (0.0) : ( ((x) > 0.0) ? 1.0 : -1.0 )) #endif #ifdef DEBUG_MODE //! Print out a form for the current function evaluation /*! * @param fp Pointer to the FILE object * @param xval Current value of x * @param fval Current value of f * @param its Current iteration value */ static void print_funcEval(FILE* fp, doublereal xval, doublereal fval, int its) { fprintf(fp,"\n"); fprintf(fp,"...............................................................\n"); fprintf(fp,".................. RootFind Function Evaluation ...............\n"); fprintf(fp,".................. iteration = %5d ........................\n", its); fprintf(fp,".................. value = %12.5g ......................\n", xval); fprintf(fp,".................. funct = %12.5g ......................\n", fval); fprintf(fp,"...............................................................\n"); fprintf(fp,"\n"); } #endif RootFind::RootFind(ResidEval* resid) : m_residFunc(resid), m_funcTargetValue(0.0), m_atolf(1.0E-11), m_atolx(1.0E-11), m_rtolf(1.0E-5), m_rtolx(1.0E-5), m_maxstep(1000), printLvl(0), writeLogAllowed_(false), DeltaXnorm_(0.01), specifiedDeltaXnorm_(0), DeltaXMax_(1.0E6), specifiedDeltaXMax_(0), FuncIsGenerallyIncreasing_(false), FuncIsGenerallyDecreasing_(false), deltaXConverged_(0.0), x_maxTried_(-1.0E300), fx_maxTried_(0.0), x_minTried_(1.0E300), fx_minTried_(0.0) { } RootFind::RootFind(const RootFind& r) : m_residFunc(r.m_residFunc), m_funcTargetValue(0.0), m_atolf(1.0E-11), m_atolx(1.0E-11), m_rtolf(1.0E-5), m_rtolx(1.0E-5), m_maxstep(1000), printLvl(0), writeLogAllowed_(false), DeltaXnorm_(0.01), specifiedDeltaXnorm_(0), DeltaXMax_(1.0E6), specifiedDeltaXMax_(0), FuncIsGenerallyIncreasing_(false), FuncIsGenerallyDecreasing_(false), deltaXConverged_(0.0), x_maxTried_(-1.0E300), fx_maxTried_(0.0), x_minTried_(1.0E300), fx_minTried_(0.0) { *this = r; } RootFind::~RootFind() { } RootFind& RootFind::operator=(const RootFind& right) { if (this == &right) { return *this; } m_residFunc = right.m_residFunc; m_funcTargetValue = right.m_funcTargetValue; m_atolf = right.m_atolf; m_atolx = right.m_atolx; m_rtolf = right.m_rtolf; m_rtolx = right.m_rtolx; m_maxstep = right.m_maxstep; printLvl = right.printLvl; writeLogAllowed_ = right.writeLogAllowed_; DeltaXnorm_ = right.DeltaXnorm_; specifiedDeltaXnorm_ = right.specifiedDeltaXnorm_; DeltaXMax_ = right.DeltaXMax_; specifiedDeltaXMax_ = right.specifiedDeltaXMax_; FuncIsGenerallyIncreasing_ = right.FuncIsGenerallyIncreasing_; FuncIsGenerallyDecreasing_ = right.FuncIsGenerallyDecreasing_; deltaXConverged_ = right.deltaXConverged_; x_maxTried_ = right.x_maxTried_; fx_maxTried_ = right.fx_maxTried_; x_minTried_ = right.x_minTried_; fx_minTried_ = right.fx_minTried_; return *this; } doublereal RootFind::delXNonzero(doublereal x1) const { doublereal deltaX = 1.0E-14 * fabs(x1); doublereal delmin = DeltaXnorm_ * 1.0E-14; if (delmin > deltaX) { return delmin; } return deltaX; } doublereal RootFind::delXMeaningful(doublereal x1) const { doublereal del = delXNonzero(x1); if (deltaXConverged_ > del) { return deltaXConverged_; } return del; } double RootFind::deltaXControlled(doublereal x2, doublereal x1) const { doublereal sgnn = 1.0; if (x1 > x2) { sgnn = -1.0; } doublereal deltaX = x2 - x1; doublereal x = fabs(x2) + fabs(x1); doublereal deltaXm = delXNonzero(x); if (fabs(deltaX) < deltaXm) { deltaX = sgnn * deltaXm; } return deltaX; } bool RootFind::theSame(doublereal x2, doublereal x1, doublereal factor) const { doublereal x = fabs(x2) + fabs(x1); doublereal deltaX = delXMeaningful(x); doublereal deltaXSmall = factor * deltaX; deltaXSmall = std::max(deltaXSmall , x * 1.0E-15); if (fabs(x2 - x1) < deltaXSmall) { return true; } return false; } int RootFind::solve(doublereal xmin, doublereal xmax, int itmax, doublereal& funcTargetValue, doublereal* xbest) { /* * We store the function target and then actually calculate a modified functional * * func = eval(x1) - m_funcTargetValue = 0 */ m_funcTargetValue = funcTargetValue; static int callNum = 0; const char* stre = "RootFind ERROR: "; const char* strw = "RootFind WARNING: "; int converged = 0; int bottomBump = 0; int topBump = 0; #ifdef DEBUG_MODE char fileName[80]; FILE* fp = 0; #endif int doFinalFuncCall = 0; doublereal x1, x2, xnew, f1, f2, fnew, slope; doublereal deltaX2 = 0.0, deltaXnew = 0.0; int posStraddle = 0; int retn = ROOTFIND_FAILEDCONVERGENCE; int foundPosF = 0; int foundNegF = 0; int foundStraddle = 0; doublereal xPosF = 0.0; doublereal fPosF = 1.0E300; doublereal xNegF = 0.0; doublereal fNegF = -1.0E300; doublereal fnorm; /* A valid norm for the making the function value dimensionless */ doublereal xDelMin; doublereal sgn; doublereal fnoise = 0.0; rfHistory_.clear(); rfTable rfT; rfT.clear(); rfT.reasoning = "First Point: "; callNum++; #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { sprintf(fileName, "RootFind_%d.log", callNum); fp = fopen(fileName, "w"); fprintf(fp, " Iter TP_its xval Func_val | Reasoning\n"); fprintf(fp, "-----------------------------------------------------" "-------------------------------\n"); } #else if (printLvl >= 3) { writelog("WARNING: RootFind: printlvl >= 3, but debug mode not turned on\n"); } #endif if (xmax <= xmin) { writelogf("%sxmin and xmax are bad: %g %g\n", stre, xmin, xmax); funcTargetValue = func(*xbest); return ROOTFIND_BADINPUT; } /* * If the maximum step size has not been specified, set it here to 1/5 of the * domain range of x. */ if (!specifiedDeltaXMax_) { DeltaXMax_ = 0.2 *(xmax - xmin); } if (!specifiedDeltaXnorm_) { DeltaXnorm_ = 0.2 * DeltaXMax_; } else { if (DeltaXnorm_ > DeltaXMax_) { if (specifiedDeltaXnorm_) { DeltaXMax_ = DeltaXnorm_; } else { DeltaXnorm_ = 0.5 * DeltaXMax_; } } } /* * Calculate an initial value of deltaXConverged_ */ deltaXConverged_ = m_rtolx * (*xbest) + m_atolx; if (DeltaXnorm_ < deltaXConverged_) { writelogf("%s DeltaXnorm_, %g, is too small compared to tols, increasing to %g\n", stre, DeltaXnorm_, deltaXConverged_); DeltaXnorm_ = deltaXConverged_; } /* * Find the first function value f1 = func(x1), by using the value entered into xbest. * Process it */ x1 = *xbest; if (x1 < xmin || x1 > xmax) { x1 = (xmin + xmax) / 2.0; rfT.reasoning += " x1 set middle between xmin and xmax because entrance is outside bounds."; } else { rfT.reasoning += " x1 set to entrance x."; } x_maxTried_ = x1; x_minTried_ = x1; int its = 1; f1 = func(x1); #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { print_funcEval(fp, x1, f1, its); fprintf(fp, "%-5d %-5d %-15.5E %-15.5E\n", -2, 0, x1, f1); } #endif if (f1 == 0.0) { *xbest = x1; return 0; } else if (f1 > fnoise) { foundPosF = 1; xPosF = x1; fPosF = f1; } else if (f1 < -fnoise) { foundNegF = 1; xNegF = x1; fNegF = f1; } rfT.its = its; rfT.TP_its = 0; rfT.xval = x1; rfT.fval = f1; rfT.foundPos = foundPosF; rfT.foundNeg = foundNegF; rfT.deltaXConverged = m_rtolx * (fabs(x1) + 0.001); rfT.deltaFConverged = fabs(f1) * m_rtolf; rfT.delX = xmax - xmin; rfHistory_.push_back(rfT); rfT.clear(); /* * Now, this is actually a tricky part of the algorithm - Find the x value for * the second point. It's tricky because we don't have a valid idea of the scale of x yet * */ rfT.reasoning = "Second Point: "; if (x1 == 0.0) { x2 = x1 + 0.01 * DeltaXnorm_; rfT.reasoning += "Set by DeltaXnorm_"; } else { x2 = x1 * 1.0001; rfT.reasoning += "Set slightly higher."; } if (x2 > xmax) { x2 = x1 - 0.01 * DeltaXnorm_; rfT.reasoning += " - But adjusted to be within bounds"; } /* * Find the second function value f2 = func(x2), Process it */ deltaX2 = x2 - x1; its++; f2 = func(x2); #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { print_funcEval(fp, x2, f2, its); fprintf(fp, "%-5d %-5d %-15.5E %-15.5E", -1, 0, x2, f2); } #endif /* * Calculate the norm of the function, this is the nominal value of f. We try * to reduce the nominal value of f by rtolf, this is the main convergence requirement. */ if (m_funcTargetValue != 0.0) { fnorm = m_atolf + fabs(m_funcTargetValue); } else { fnorm = 0.5*(fabs(f1) + fabs(f2)) + fabs(m_funcTargetValue) + m_atolf; } fnoise = 1.0E-100; if (f2 > fnoise) { if (!foundPosF) { foundPosF = 1; xPosF = x2; fPosF = f2; } } else if (f2 < - fnoise) { if (!foundNegF) { foundNegF = 1; xNegF = x2; fNegF = f2; } } else if (f2 == 0.0) { *xbest = x2; return ROOTFIND_SUCCESS; } rfT.its = its; rfT.TP_its = 0; rfT.xval = x2; rfT.fval = f2; rfT.foundPos = foundPosF; rfT.foundNeg = foundNegF; /* * See if we have already achieved a straddle */ foundStraddle = foundPosF && foundNegF; if (foundStraddle) { if (xPosF > xNegF) { posStraddle = 1; } else { posStraddle = 0; } } bool useNextStrat = false; bool slopePointingToHigher = true; // --------------------------------------------------------------------------------------------- // MAIN LOOP // --------------------------------------------------------------------------------------------- do { /* * Find an estimate of the next point, xnew, to try based on * a linear approximation from the last two points. */ #ifdef DEBUG_MODE if (fabs(x2 - x1) < 1.0E-14) { printf(" RootFind: we are here x2 = %g x1 = %g\n", x2, x1); } #endif doublereal delXtmp = deltaXControlled(x2, x1); slope = (f2 - f1) / delXtmp; rfT.slope = slope; rfHistory_.push_back(rfT); rfT.clear(); rfT.reasoning = ""; if (fabs(slope) <= 1.0E-100) { if (printLvl >= 2) { writelogf("%s functions evals produced the same result, %g, at %g and %g\n", strw, f2, x1, x2); } xnew = x2 + DeltaXnorm_; slopePointingToHigher = true; useNextStrat = true; rfT.reasoning += "Slope is close to zero. "; } else { useNextStrat = false; xnew = x2 - f2 / slope; if (xnew > x2) { slopePointingToHigher = true; } else { slopePointingToHigher = false; } rfT.reasoning += "Slope is good. "; } #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | xlin = %-11.5E", xnew); } #endif deltaXnew = xnew - x2; /* * If the suggested step size is too big, throw out step */ if (!foundStraddle) { if (fabs(xnew - x2) > DeltaXMax_) { useNextStrat = true; rfT.reasoning += "Too large change in xnew from slope. "; } if (fabs(deltaXnew) < fabs(deltaX2)) { deltaXnew = 1.2 * deltaXnew; xnew = x2 + deltaXnew; } } /* * If the slope can't be trusted using a different strategy for picking the next point */ if (useNextStrat) { rfT.reasoning += "Using DeltaXnorm, " + fp2str(DeltaXnorm_) + " and FuncIsGenerallyIncreasing hints. "; if (f2 < 0.0) { if (FuncIsGenerallyIncreasing_) { if (slopePointingToHigher) { xnew = std::min(x2 + 3.0*DeltaXnorm_, xnew); } else { xnew = x2 + DeltaXnorm_; } } else if (FuncIsGenerallyDecreasing_) { if (!slopePointingToHigher) { xnew = std::max(x2 - 3.0*DeltaXnorm_, xnew); } else { xnew = x2 - DeltaXnorm_; } } else { if (slopePointingToHigher) { xnew = x2 + DeltaXnorm_; } else { xnew = x2 - DeltaXnorm_; } } } else { if (FuncIsGenerallyDecreasing_) { if (!slopePointingToHigher) { xnew = std::max(x2 + 3.0*DeltaXnorm_, xnew); } else { xnew = x2 + DeltaXnorm_; } } else if (FuncIsGenerallyIncreasing_) { if (! slopePointingToHigher) { xnew = std::min(x2 - 3.0*DeltaXnorm_, xnew); } else { xnew = x2 - DeltaXnorm_; } } else { if (slopePointingToHigher) { xnew = x2 + DeltaXnorm_; } else { xnew = x2 - DeltaXnorm_; } } } } /* * Here, if we have a straddle, we purposefully overshoot the smaller side by 5%. Yes it does lead to * more iterations. However, we're interested in bounding x, and not just doing Newton's method. */ if (foundStraddle) { double delta = fabs(x2 - x1); if (fabs(xnew - x1) < .01 * delta) { xnew = x1 + 0.01 * (x2 - x1); } else if (fabs(xnew - x2) < .01 * delta) { xnew = x1 + 0.01 * (x2 - x1); } else if ((xnew > x1 && xnew < x2) || (xnew < x1 && xnew > x2)) { if (fabs(xnew - x1) < fabs(x2 - xnew)) { xnew = x1 + 20./19. * (xnew - x1); } else { xnew = x2 + 20./19. * (xnew - x2); } } } /* * OK, we have an estimate xnew. * * * Put heuristic bounds on the step jump */ if ((xnew > x1 && xnew < x2) || (xnew < x1 && xnew > x2)) { /* * If we are doing a jump in between the two previous points, make sure * the new trial is no closer that 10% of the distances between x2-x1 to * any of the original points. This is an important part of finding a good bound. */ xDelMin = fabs(x2 - x1) / 10.; if (fabs(xnew - x1) < xDelMin) { xnew = x1 + DSIGN(xnew-x1) * xDelMin; #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | x10%% = %-11.5E", xnew); } #endif } if (fabs(xnew - x2) < 0.1 * xDelMin) { xnew = x2 + DSIGN(xnew-x2) * 0.1 * xDelMin; #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | x10%% = %-11.5E", xnew); } #endif } } else { /* * If we are venturing into new ground, only allow the step jump * to increase by 50% at each iteration, unless the step jump is less than * the user has said that it is ok to take */ doublereal xDelMax = 1.5 * fabs(x2 - x1); if (specifiedDeltaXnorm_) { if (0.5 * DeltaXnorm_ > xDelMax) { xDelMax = 0.5 *DeltaXnorm_ ; } } if (fabs(xDelMax) < fabs(xnew - x2)) { xnew = x2 + DSIGN(xnew-x2) * xDelMax; #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | xlimitsize = %-11.5E", xnew); } #endif } /* * If we are doing a jump outside the two previous points, make sure * the new trial is no closer that 10% of the distances between x2-x1 to * any of the original points. This is an important part of finding a good bound. */ xDelMin = 0.1 * fabs(x2 - x1); if (fabs(xnew - x2) < xDelMin) { xnew = x2 + DSIGN(xnew - x2) * xDelMin; #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | x10%% = %-11.5E", xnew); } #endif } if (fabs(xnew - x1) < xDelMin) { xnew = x1 + DSIGN(xnew - x1) * xDelMin; #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | x10%% = %-11.5E", xnew); } #endif } } /* * HKM -> Not sure this section is needed */ if (foundStraddle) { #ifdef DEBUG_MODE double xorig = xnew; #endif if (posStraddle) { if (f2 > 0.0) { if (xnew > x2) { xnew = (xNegF + x2)/2; } if (xnew < xNegF) { xnew = (xNegF + x2)/2; } } else { if (xnew < x2) { xnew = (xPosF + x2)/2; } if (xnew > xPosF) { xnew = (xPosF + x2)/2; } } } else { if (f2 > 0.0) { if (xnew < x2) { xnew = (xNegF + x2)/2; } if (xnew > xNegF) { xnew = (xNegF + x2)/2; } } else { if (xnew > x2) { xnew = (xPosF + x2)/2; } if (xnew < xPosF) { xnew = (xPosF + x2)/2; } } } #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { if (xorig != xnew) { fprintf(fp, " | xstraddle = %-11.5E", xnew); } } #endif } /* * Enforce a minimum stepsize if we haven't found a straddle. */ deltaXnew = xnew - x2; if (fabs(deltaXnew) < 1.2 * delXMeaningful(xnew)) { if (!foundStraddle) { sgn = 1.0; if (x2 > xnew) { sgn = -1.0; } deltaXnew = 1.2 * delXMeaningful(xnew) * sgn; rfT.reasoning += "Enforcing minimum stepsize from " + fp2str(xnew - x2) + " to " + fp2str(deltaXnew); xnew = x2 + deltaXnew; } } /* * Guard against going above xmax or below xmin */ if (xnew > xmax) { topBump++; if (topBump < 3) { xnew = x2 + (xmax - x2) / 2.0; rfT.reasoning += ("xval reduced to " + fp2str(xnew) + " because predicted xnew was above max value of " + fp2str(xmax)); } else { if (x2 == xmax || x1 == xmax) { // we are here when we are bumping against the top limit. // No further action is possible retn = ROOTFIND_SOLNHIGHERTHANXMAX; *xbest = xnew; rfT.slope = slope; rfT.reasoning += "Giving up because we're at xmax and xnew point higher: " + fp2str(xnew); goto done; } else { rfT.reasoning += "xval reduced from " + fp2str(xnew) + " to the max value, " + fp2str(xmax); xnew = xmax; } } #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | xlimitmax = %-11.5E", xnew); } #endif } if (xnew < xmin) { bottomBump++; if (bottomBump < 3) { rfT.reasoning += ("xnew increased from " + fp2str(xnew) +" to " + fp2str(x2 - (x2 - xmin) / 2.0) + " because above min value of " + fp2str(xmin)); xnew = x2 - (x2 - xmin) / 2.0; } else { if (x2 == xmin || x1 == xmin) { // we are here when we are bumping against the bottom limit. // No further action is possible retn = ROOTFIND_SOLNLOWERTHANXMIN; *xbest = xnew; rfT.slope = slope; rfT.reasoning = "Giving up because we're already at xmin and xnew points lower: " + fp2str(xnew); goto done; } else { rfT.reasoning += "xval increased from " + fp2str(xnew) + " to the min value, " + fp2str(xmin); xnew = xmin; } } #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | xlimitmin = %-11.5E", xnew); } #endif } its++; fnew = func(xnew); #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp,"\n"); print_funcEval(fp, xnew, fnew, its); fprintf(fp, "%-5d %-5d %-15.5E %-15.5E", its, 0, xnew, fnew); } #endif rfT.xval = xnew; rfT.fval = fnew; rfT.its = its; if (foundStraddle) { if (posStraddle) { if (fnew > 0.0) { if (xnew < xPosF) { xPosF = xnew; fPosF = fnew; } } else { if (xnew > xNegF) { xNegF = xnew; fNegF = fnew; } } } else { if (fnew > 0.0) { if (xnew > xPosF) { xPosF = xnew; fPosF = fnew; } } else { if (xnew < xNegF) { xNegF = xnew; fNegF = fnew; } } } } if (! foundStraddle) { if (fnew > fnoise) { if (!foundPosF) { foundPosF = 1; rfT.foundPos = 1; xPosF = xnew; fPosF = fnew; foundStraddle = 1; if (xPosF > xNegF) { posStraddle = 1; } else { posStraddle = 0; } } } else if (fnew < - fnoise) { if (!foundNegF) { foundNegF = 1; rfT.foundNeg = 1; xNegF = xnew; fNegF = fnew; foundStraddle = 1; if (xPosF > xNegF) { posStraddle = 1; } else { posStraddle = 0; } } } } x1 = x2; f1 = f2; x2 = xnew; f2 = fnew; /* * As we go on to new data points, we make sure that * we have the best straddle of the solution with the choice of F1 and F2 when * we do have a straddle to work with. */ if (foundStraddle) { bool foundBetterPos = false; bool foundBetterNeg = false; if (posStraddle) { if (f2 > 0.0) { if (xPosF < x2) { foundBetterPos = false; x2 = xPosF; f2 = fPosF; } if (f1 > 0.0) { if (foundBetterPos) { x1 = xNegF; f1 = fNegF; } else { if (x1 >= x2) { x1 = xNegF; f1 = fNegF; } } } } else { if (xNegF > x2) { foundBetterNeg = false; x2 = xNegF; f2 = fNegF; } if (f1 < 0.0) { if (foundBetterNeg) { x1 = xPosF; f1 = fPosF; } else { if (x1 <= x2) { x1 = xPosF; f1 = fPosF; } } } } } else { if (f2 < 0.0) { if (xNegF < x2) { foundBetterNeg = false; x2 = xNegF; f2 = fNegF; } if (f1 < 0.0) { if (foundBetterNeg) { x1 = xPosF; f1 = fPosF; } else { if (x1 >= x2) { x1 = xPosF; f1 = fPosF; } } } } else { if (xPosF > x2) { foundBetterPos = true; x2 = xPosF; f2 = fPosF; } if (f1 > 0.0) { if (foundBetterNeg) { x1 = xNegF; f1 = fNegF; } else { if (x1 <= x2) { x1 = xNegF; f1 = fNegF; } } } } } AssertThrow((f1* f2 <= 0.0), "F1 and F2 aren't bounding"); } deltaX2 = deltaXnew; deltaXnew = x2 - x1; deltaXConverged_ = 0.5 * deltaXConverged_ + 0.5 * (m_rtolx * 0.5 * (fabs(x2) + fabs(x1)) + m_atolx); rfT.deltaXConverged = deltaXConverged_; rfT.deltaFConverged = fnorm * m_rtolf; if (foundStraddle) { rfT.delX = std::max(fabs(deltaX2), fabs(deltaXnew)); } else { rfT.delX = std::max(fabs(deltaX2), fabs(deltaXnew)); if (x2 < x1) { rfT.delX = std::max(rfT.delX, x2 - xmin); } else { rfT.delX = std::max(rfT.delX, xmax - x2); } } /* * Section To Determine CONVERGENCE criteria */ doFinalFuncCall = 0; if ((fabs(fnew / fnorm) < m_rtolf) && foundStraddle) { if (fabs(deltaX2) < deltaXConverged_ && fabs(deltaXnew) < deltaXConverged_) { converged = 1; rfT.reasoning += "NormalConvergence"; retn = ROOTFIND_SUCCESS; } else if (fabs(slope) > 1.0E-100) { double xdels = fabs(fnew / slope); if (xdels < deltaXConverged_ * 0.3) { converged = 1; rfT.reasoning += "NormalConvergence-SlopelimitsDelX"; doFinalFuncCall = 1; retn = ROOTFIND_SUCCESS; } } /* * Check for excess convergence in the x coordinate */ if (!converged) { if (foundStraddle) { doublereal denom = fabs(x1 - x2); if (denom < 1.0E-200) { retn = ROOTFIND_FAILEDCONVERGENCE; converged = true; rfT.reasoning += "ConvergenceFZero but X1X2Identical"; } if (theSame(x2, x1, 1.0E-2)) { converged = true; rfT.reasoning += " ConvergenceF and XSame"; retn = ROOTFIND_SUCCESS; } } } } else { /* * We are here when F is not converged, but we may want to end anyway */ if (!converged) { if (foundStraddle) { doublereal denom = fabs(x1 - x2); if (denom < 1.0E-200) { retn = ROOTFIND_FAILEDCONVERGENCE; converged = true; rfT.reasoning += "FNotConverged but X1X2Identical"; } /* * The premise here is that if x1 and x2 get close to one another, * then the accuracy of the calculation gets destroyed. */ if (theSame(x2, x1, 1.0E-5)) { converged = true; retn = ROOTFIND_SUCCESS_XCONVERGENCEONLY; rfT.reasoning += "FNotConverged but XSame"; } } } } } while (! converged && its < itmax); done: if (converged) { rfT.slope = slope; rfHistory_.push_back(rfT); rfT.clear(); rfT.its = its; AssertThrow((f1* f2 <= 0.0), "F1 and F2 aren't bounding"); double x_fpos = x2; double x_fneg = x1; if (f2 < 0.0) { x_fpos = x1; x_fneg = x2; } rfT.delX = fabs(x_fpos - x_fneg); if (doFinalFuncCall || (fabs(f1) < 2.0 * fabs(f2))) { double delXtmp = deltaXControlled(x2, x1); slope = (f2 - f1) / delXtmp; xnew = x2 - f2 / slope; its++; fnew = func(xnew); if (fnew > 0.0) { if (fabs(xnew - x_fneg) < fabs(x_fpos - x_fneg)) { x_fpos = xnew; rfT.delX = fabs(xnew - x_fneg); } } else { if (fabs(xnew - x_fpos) < fabs(x_fpos - x_fneg)) { x_fneg = xnew; rfT.delX = fabs(xnew - x_fpos); } } rfT.its = its; if (fabs(fnew) < fabs(f2) && (fabs(fnew) < fabs(f1))) { *xbest = xnew; if (doFinalFuncCall) { rfT.reasoning += "CONVERGENCE: Another Evaluation Requested"; rfT.delX = fabs(xnew - x2); } else { rfT.reasoning += "CONVERGENCE: Another Evaluation done because f1 < f2"; rfT.delX = fabs(xnew - x1); } rfT.fval = fnew; rfT.xval = xnew; x2 = xnew; f2 = fnew; } else if (fabs(f1) < fabs(f2)) { rfT.its = its; rfT.xval = xnew; rfT.fval = fnew; rfT.slope = slope; rfT.reasoning += "CONVERGENCE: Another Evaluation not as good as Second Point "; rfHistory_.push_back(rfT); rfT.clear(); rfT.its = its; std::swap(f1, f2); std::swap(x1, x2); *xbest = x2; if (fabs(fnew) < fabs(f1)) { if (f1 * fnew > 0.0) { std::swap(f1, fnew); std::swap(x1, xnew); } } rfT.its = its; rfT.xval = *xbest; rfT.fval = f2; rfT.delX = fabs(x_fpos - x_fneg); rfT.reasoning += "CONVERGENCE: NormalEnding -> Second point used"; } else { rfT.its = its; rfT.xval = xnew; rfT.fval = fnew; rfT.slope = slope; rfT.reasoning += "CONVERGENCE: Another Evaluation not as good as First Point "; rfHistory_.push_back(rfT); rfT.clear(); rfT.its = its; *xbest = x2; rfT.xval = *xbest; rfT.fval = f2; rfT.delX = fabs(x_fpos - x_fneg); rfT.reasoning += "CONVERGENCE: NormalEnding -> Last point used"; } } else { *xbest = x2; rfT.xval = *xbest; rfT.fval = f2; rfT.delX = fabs(x2 - x1); rfT.reasoning += "CONVERGENCE: NormalEnding -> Last point used"; } funcTargetValue = f2 + m_funcTargetValue; rfT.slope = slope; if (printLvl >= 1) { writelogf("RootFind success: convergence achieved\n"); } #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, " | RootFind success in %d its, fnorm = %g\n", its, fnorm); } #endif rfHistory_.push_back(rfT); } else { rfT.reasoning = "FAILED CONVERGENCE "; rfT.slope = slope; rfT.its = its; if (retn == ROOTFIND_SOLNHIGHERTHANXMAX) { if (printLvl >= 1) { writelogf("RootFind ERROR: Soln probably lies higher than xmax, %g: best guess = %g\n", xmax, *xbest); } rfT.reasoning += "Soln probably lies higher than xmax, " + fp2str(xmax) + ": best guess = " + fp2str(*xbest); } else if (retn == ROOTFIND_SOLNLOWERTHANXMIN) { if (printLvl >= 1) { writelogf("RootFind ERROR: Soln probably lies lower than xmin, %g: best guess = %g\n", xmin, *xbest); } rfT.reasoning += "Soln probably lies lower than xmin, " + fp2str(xmin) + ": best guess = " + fp2str(*xbest); } else { retn = ROOTFIND_FAILEDCONVERGENCE; if (printLvl >= 1) { writelogf("RootFind ERROR: maximum iterations exceeded without convergence, cause unknown\n"); } rfT.reasoning += "Maximum iterations exceeded without convergence, cause unknown"; } #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fprintf(fp, "\nRootFind failure in %d its\n", its); } #endif *xbest = x2; funcTargetValue = f2 + m_funcTargetValue; rfT.xval = *xbest; rfT.fval = f2; rfHistory_.push_back(rfT); } #ifdef DEBUG_MODE if (printLvl >= 3 && writeLogAllowed_) { fclose(fp); } #endif if (printLvl >= 2) { printTable(); } return retn; } doublereal RootFind::func(doublereal x) { doublereal r; #ifdef DEBUG_MODE checkFinite(x); #endif m_residFunc->evalSS(0.0, &x, &r); #ifdef DEBUG_MODE checkFinite(r); #endif doublereal ff = r - m_funcTargetValue; if (x >= x_maxTried_) { x_maxTried_ = x; fx_maxTried_ = ff; } if (x <= x_minTried_) { x_minTried_ = x; fx_minTried_ = ff; } return ff; } void RootFind::setTol(doublereal rtolf, doublereal atolf, doublereal rtolx, doublereal atolx) { m_atolf = atolf; m_rtolf = rtolf; if (rtolx <= 0.0) { m_rtolx = atolf; } else { m_rtolx = rtolx; } if (atolx <= 0.0) { m_atolx = atolf; } else { m_atolx = atolx; } } void RootFind::setPrintLvl(int printlvl) { printLvl = printlvl; } void RootFind::setFuncIsGenerallyIncreasing(bool value) { if (value) { FuncIsGenerallyDecreasing_ = false; } FuncIsGenerallyIncreasing_ = value; } void RootFind::setFuncIsGenerallyDecreasing(bool value) { if (value) { FuncIsGenerallyIncreasing_ = false; } FuncIsGenerallyDecreasing_ = value; } void RootFind::setDeltaX(doublereal deltaXNorm) { DeltaXnorm_ = deltaXNorm; specifiedDeltaXnorm_ = 1; } void RootFind::setDeltaXMax(doublereal deltaX) { DeltaXMax_ = deltaX; specifiedDeltaXMax_ = 1; } void RootFind::printTable() { printf("\t----------------------------------------------------------------------------------------------------------------------------------------\n"); printf("\t RootFinder Summary table: \n"); printf("\t FTarget = %g\n", m_funcTargetValue); printf("\t Iter | xval delX deltaXConv | slope | foundP foundN| F - F_targ deltaFConv | Reasoning\n"); printf("\t----------------------------------------------------------------------------------------------------------------------------------------\n"); for (int i = 0; i < (int) rfHistory_.size(); i++) { struct rfTable rfT = rfHistory_[i]; printf("\t %3d |%- 17.11E %- 13.7E %- 13.7E |%- 13.5E| %3d %3d | %- 12.5E %- 12.5E | %s \n", rfT.its, rfT.xval, rfT.delX, rfT.deltaXConverged, rfT.slope, rfT.foundPos, rfT.foundNeg, rfT.fval, rfT.deltaFConverged, (rfT.reasoning).c_str()); } printf("\t----------------------------------------------------------------------------------------------------------------------------------------\n"); } }