21 static void print_funcEval(FILE* fp, doublereal xval, doublereal fval,
int its)
24 fprintf(fp,
"...............................................................\n");
25 fprintf(fp,
".................. RootFind Function Evaluation ...............\n");
26 fprintf(fp,
".................. iteration = %5d ........................\n", its);
27 fprintf(fp,
".................. value = %12.5g ......................\n", xval);
28 fprintf(fp,
".................. funct = %12.5g ......................\n", fval);
29 fprintf(fp,
"...............................................................\n");
35 m_funcTargetValue(0.0),
42 writeLogAllowed_(false),
44 specifiedDeltaXnorm_(0),
46 specifiedDeltaXMax_(0),
47 FuncIsGenerallyIncreasing_(false),
48 FuncIsGenerallyDecreasing_(false),
49 deltaXConverged_(0.0),
50 x_maxTried_(-1.0E300),
56 "boost::math::tools::toms748_solve for an alternative.");
60 m_residFunc(r.m_residFunc),
61 m_funcTargetValue(0.0),
68 writeLogAllowed_(false),
70 specifiedDeltaXnorm_(0),
72 specifiedDeltaXMax_(0),
73 FuncIsGenerallyIncreasing_(false),
74 FuncIsGenerallyDecreasing_(false),
75 deltaXConverged_(0.0),
76 x_maxTried_(-1.0E300),
82 "To be removed after Cantera 2.3.");
89 "To be removed after Cantera 2.3.");
119 doublereal deltaX = 1.0E-14 * fabs(x1);
121 if (delmin > deltaX) {
138 doublereal sgnn = 1.0;
142 doublereal deltaX = x2 - x1;
143 doublereal x = fabs(x2) + fabs(x1);
145 if (fabs(deltaX) < deltaXm) {
146 deltaX = sgnn * deltaXm;
153 doublereal x = fabs(x2) + fabs(x1);
155 doublereal deltaXSmall = factor * deltaX;
156 deltaXSmall = std::max(deltaXSmall , x * 1.0E-15);
157 if (fabs(x2 - x1) < deltaXSmall) {
163 int RootFind::solve(doublereal xmin, doublereal xmax,
int itmax, doublereal& funcTargetValue, doublereal* xbest)
169 static int callNum = 0;
170 const char* stre =
"RootFind ERROR: ";
171 const char* strw =
"RootFind WARNING: ";
176 int doFinalFuncCall = 0;
177 doublereal x1, x2, xnew, f1, f2, fnew, slope = 0;
178 doublereal deltaX2 = 0.0, deltaXnew = 0.0;
183 int foundStraddle = 0;
184 doublereal xPosF = 0.0;
185 doublereal fPosF = 1.0E300;
186 doublereal xNegF = 0.0;
187 doublereal fNegF = -1.0E300;
191 doublereal fnoise = 0.0;
195 rfT.reasoning =
"First Point: ";
199 fp = fopen(fmt::format(
"RootFind_%d.log", callNum).c_str(),
"w");
200 fprintf(fp,
" Iter TP_its xval Func_val | Reasoning\n");
201 fprintf(fp,
"-----------------------------------------------------" 202 "-------------------------------\n");
204 writelog(
"WARNING: RootFind: printlvl >= 3, but debug mode not turned on\n");
207 writelogf(
"%sxmin and xmax are bad: %g %g\n", stre, xmin, xmax);
208 funcTargetValue =
func(*xbest);
233 writelogf(
"%s DeltaXnorm_, %g, is too small compared to tols, increasing to %g\n",
241 if (x1 < xmin || x1 > xmax) {
242 x1 = (xmin + xmax) / 2.0;
243 rfT.reasoning +=
" x1 set middle between xmin and xmax because entrance is outside bounds.";
245 rfT.reasoning +=
" x1 set to entrance x.";
255 fprintf(fp,
"%-5d %-5d %-15.5E %-15.5E\n", -2, 0, x1, f1);
261 }
else if (f1 > fnoise) {
265 }
else if (f1 < -fnoise) {
274 rfT.foundPos = foundPosF;
275 rfT.foundNeg = foundNegF;
276 rfT.deltaXConverged =
m_rtolx * (fabs(x1) + 0.001);
277 rfT.deltaFConverged = fabs(f1) *
m_rtolf;
278 rfT.delX = xmax - xmin;
285 rfT.reasoning =
"Second Point: ";
288 rfT.reasoning +=
"Set by DeltaXnorm_";
291 rfT.reasoning +=
"Set slightly higher.";
295 rfT.reasoning +=
" - But adjusted to be within bounds";
304 fprintf(fp,
"%-5d %-5d %-15.5E %-15.5E", -1, 0, x2, f2);
322 }
else if (f2 < - fnoise) {
328 }
else if (f2 == 0.0) {
336 rfT.foundPos = foundPosF;
337 rfT.foundNeg = foundNegF;
340 foundStraddle = foundPosF && foundNegF;
349 bool useNextStrat =
false;
350 bool slopePointingToHigher =
true;
353 while (!converged && its < itmax) {
356 if (fabs(x2 - x1) < 1.0E-14) {
357 writelogf(
" RootFind: we are here x2 = %g x1 = %g\n", x2, x1);
360 slope = (f2 - f1) / delXtmp;
365 if (fabs(slope) <= 1.0E-100) {
367 writelogf(
"%s functions evals produced the same result, %g, at %g and %g\n",
371 slopePointingToHigher =
true;
373 rfT.reasoning +=
"Slope is close to zero. ";
375 useNextStrat =
false;
376 xnew = x2 - f2 / slope;
378 slopePointingToHigher =
true;
380 slopePointingToHigher =
false;
382 rfT.reasoning +=
"Slope is good. ";
385 fprintf(fp,
" | xlin = %-11.5E", xnew);
387 deltaXnew = xnew - x2;
390 if (!foundStraddle) {
393 rfT.reasoning +=
"Too large change in xnew from slope. ";
395 if (fabs(deltaXnew) < fabs(deltaX2)) {
396 deltaXnew = 1.2 * deltaXnew;
397 xnew = x2 + deltaXnew;
404 rfT.reasoning += fmt::format(
"Using DeltaXnorm, {} and FuncIsGenerallyIncreasing hints. ",
DeltaXnorm_);
407 if (slopePointingToHigher) {
413 if (!slopePointingToHigher) {
419 if (slopePointingToHigher) {
427 if (!slopePointingToHigher) {
433 if (! slopePointingToHigher) {
439 if (slopePointingToHigher) {
452 double delta = fabs(x2 - x1);
453 if (fabs(xnew - x1) < .01 * delta) {
454 xnew = x1 + 0.01 * (x2 - x1);
455 }
else if (fabs(xnew - x2) < .01 * delta) {
456 xnew = x1 + 0.01 * (x2 - x1);
457 }
else if ((xnew > x1 && xnew < x2) || (xnew < x1 && xnew > x2)) {
458 if (fabs(xnew - x1) < fabs(x2 - xnew)) {
459 xnew = x1 + 20./19. * (xnew - x1);
461 xnew = x2 + 20./19. * (xnew - x2);
467 if ((xnew > x1 && xnew < x2) || (xnew < x1 && xnew > x2)) {
472 xDelMin = fabs(x2 - x1) / 10.;
473 if (fabs(xnew - x1) < xDelMin) {
474 xnew = x1 +
sign(xnew-x1) * xDelMin;
476 fprintf(fp,
" | x10%% = %-11.5E", xnew);
479 if (fabs(xnew - x2) < 0.1 * xDelMin) {
480 xnew = x2 +
sign(xnew-x2) * 0.1 * xDelMin;
482 fprintf(fp,
" | x10%% = %-11.5E", xnew);
489 doublereal xDelMax = 1.5 * fabs(x2 - x1);
493 if (fabs(xDelMax) < fabs(xnew - x2)) {
494 xnew = x2 +
sign(xnew-x2) * xDelMax;
496 fprintf(fp,
" | xlimitsize = %-11.5E", xnew);
504 xDelMin = 0.1 * fabs(x2 - x1);
505 if (fabs(xnew - x2) < xDelMin) {
506 xnew = x2 +
sign(xnew - x2) * xDelMin;
508 fprintf(fp,
" | x10%% = %-11.5E", xnew);
511 if (fabs(xnew - x1) < xDelMin) {
512 xnew = x1 +
sign(xnew - x1) * xDelMin;
514 fprintf(fp,
" | x10%% = %-11.5E", xnew);
525 xnew = (xNegF + x2)/2;
528 xnew = (xNegF + x2)/2;
532 xnew = (xPosF + x2)/2;
535 xnew = (xPosF + x2)/2;
541 xnew = (xNegF + x2)/2;
544 xnew = (xNegF + x2)/2;
548 xnew = (xPosF + x2)/2;
551 xnew = (xPosF + x2)/2;
556 fprintf(fp,
" | xstraddle = %-11.5E", xnew);
561 deltaXnew = xnew - x2;
562 if (fabs(deltaXnew) < 1.2 *
delXMeaningful(xnew) && !foundStraddle) {
568 rfT.reasoning += fmt::format(
"Enforcing minimum stepsize from {} to {}",
569 xnew - x2, deltaXnew);
570 xnew = x2 + deltaXnew;
577 xnew = x2 + (xmax - x2) / 2.0;
578 rfT.reasoning += fmt::format(
"xval reduced to {} because predicted xnew was above max value of {}", xnew, xmax);
580 if (x2 == xmax || x1 == xmax) {
586 rfT.reasoning += fmt::format(
"Giving up because we're at xmax and xnew point higher: {}", xnew);
589 rfT.reasoning += fmt::format(
"xval reduced from {} to the max value, {}", xnew, xmax);
594 fprintf(fp,
" | xlimitmax = %-11.5E", xnew);
599 if (bottomBump < 3) {
600 rfT.reasoning += fmt::format(
"xnew increased from {} to {} because above min value of {}",
601 xnew, x2 - (x2 - xmin) / 2.0, xmin);
602 xnew = x2 - (x2 - xmin) / 2.0;
604 if (x2 == xmin || x1 == xmin) {
610 rfT.reasoning = fmt::format(
"Giving up because we're already at xmin and xnew points lower: {}", xnew);
613 rfT.reasoning += fmt::format(
"xval increased from {} to the min value, {}", xnew, xmin);
618 fprintf(fp,
" | xlimitmin = %-11.5E", xnew);
628 fprintf(fp,
"%-5d %-5d %-15.5E %-15.5E", its, 0, xnew, fnew);
661 if (! foundStraddle) {
675 }
else if (fnew < - fnoise) {
701 bool foundBetterPos =
false;
702 bool foundBetterNeg =
false;
706 foundBetterPos =
false;
711 if (foundBetterPos) {
723 foundBetterNeg =
false;
728 if (foundBetterNeg) {
742 foundBetterNeg =
false;
747 if (foundBetterNeg) {
759 foundBetterPos =
true;
764 if (foundBetterNeg) {
776 AssertThrow((f1* f2 <= 0.0),
"F1 and F2 aren't bounding");
783 rfT.deltaFConverged = fnorm *
m_rtolf;
785 rfT.delX = std::max(fabs(deltaX2), fabs(deltaXnew));
787 rfT.delX = std::max(fabs(deltaX2), fabs(deltaXnew));
789 rfT.delX = std::max(rfT.delX, x2 - xmin);
791 rfT.delX = std::max(rfT.delX, xmax - x2);
796 if ((fabs(fnew / fnorm) <
m_rtolf) && foundStraddle) {
799 rfT.reasoning +=
"NormalConvergence";
801 }
else if (fabs(slope) > 1.0E-100) {
802 double xdels = fabs(fnew / slope);
805 rfT.reasoning +=
"NormalConvergence-SlopelimitsDelX";
812 if (!converged && foundStraddle) {
813 doublereal denom = fabs(x1 - x2);
814 if (denom < 1.0E-200) {
817 rfT.reasoning +=
"ConvergenceFZero but X1X2Identical";
821 rfT.reasoning +=
" ConvergenceF and XSame";
827 if (!converged && foundStraddle) {
828 doublereal denom = fabs(x1 - x2);
829 if (denom < 1.0E-200) {
832 rfT.reasoning +=
"FNotConverged but X1X2Identical";
840 rfT.reasoning +=
"FNotConverged but XSame";
852 AssertThrow((f1* f2 <= 0.0),
"F1 and F2 aren't bounding");
860 rfT.delX = fabs(x_fpos - x_fneg);
861 if (doFinalFuncCall || (fabs(f1) < 2.0 * fabs(f2))) {
863 slope = (f2 - f1) / delXtmp;
864 xnew = x2 - f2 / slope;
868 if (fabs(xnew - x_fneg) < fabs(x_fpos - x_fneg)) {
870 rfT.delX = fabs(xnew - x_fneg);
873 if (fabs(xnew - x_fpos) < fabs(x_fpos - x_fneg)) {
875 rfT.delX = fabs(xnew - x_fpos);
879 if (fabs(fnew) < fabs(f2) && (fabs(fnew) < fabs(f1))) {
881 if (doFinalFuncCall) {
882 rfT.reasoning +=
"CONVERGENCE: Another Evaluation Requested";
883 rfT.delX = fabs(xnew - x2);
885 rfT.reasoning +=
"CONVERGENCE: Another Evaluation done because f1 < f2";
886 rfT.delX = fabs(xnew - x1);
892 }
else if (fabs(f1) < fabs(f2)) {
898 rfT.reasoning +=
"CONVERGENCE: Another Evaluation not as good as Second Point ";
905 if (fabs(fnew) < fabs(f1) && f1 * fnew > 0.0) {
913 rfT.delX = fabs(x_fpos - x_fneg);
914 rfT.reasoning +=
"CONVERGENCE: NormalEnding -> Second point used";
921 rfT.reasoning +=
"CONVERGENCE: Another Evaluation not as good as First Point ";
928 rfT.delX = fabs(x_fpos - x_fneg);
929 rfT.reasoning +=
"CONVERGENCE: NormalEnding -> Last point used";
935 rfT.delX = fabs(x2 - x1);
936 rfT.reasoning +=
"CONVERGENCE: NormalEnding -> Last point used";
942 writelogf(
"RootFind success: convergence achieved\n");
945 fprintf(fp,
" | RootFind success in %d its, fnorm = %g\n", its, fnorm);
949 rfT.reasoning =
"FAILED CONVERGENCE ";
954 writelogf(
"RootFind ERROR: Soln probably lies higher than xmax, %g: best guess = %g\n", xmax, *xbest);
956 rfT.reasoning += fmt::format(
"Soln probably lies higher than xmax, {}: best guess = {}", xmax, *xbest);
959 writelogf(
"RootFind ERROR: Soln probably lies lower than xmin, %g: best guess = %g\n", xmin, *xbest);
961 rfT.reasoning += fmt::format(
"Soln probably lies lower than xmin, {}: best guess = {}", xmin, *xbest);
965 writelogf(
"RootFind ERROR: maximum iterations exceeded without convergence, cause unknown\n");
967 rfT.reasoning +=
"Maximum iterations exceeded without convergence, cause unknown";
970 fprintf(fp,
"\nRootFind failure in %d its\n", its);
1059 writelogf(
"\t----------------------------------------------------------------------------------------------------------------------------------------\n");
1060 writelogf(
"\t RootFinder Summary table: \n");
1062 writelogf(
"\t Iter | xval delX deltaXConv | slope | foundP foundN| F - F_targ deltaFConv | Reasoning\n");
1063 writelogf(
"\t----------------------------------------------------------------------------------------------------------------------------------------\n");
1064 for (
int i = 0; i < (int)
rfHistory_.size(); i++) {
1066 writelogf(
"\t %3d |%- 17.11E %- 13.7E %- 13.7E |%- 13.5E| %3d %3d | %- 12.5E %- 12.5E | %s \n",
1067 rfT.its, rfT.xval, rfT.delX, rfT.deltaXConverged, rfT.slope, rfT.foundPos, rfT.foundNeg, rfT.fval,
1068 rfT.deltaFConverged, rfT.reasoning);
1070 writelogf(
"\t----------------------------------------------------------------------------------------------------------------------------------------\n");
void checkFinite(const double tmp)
Check to see that a number is finite (not NaN, +Inf or -Inf)
void setDeltaX(doublereal deltaXNorm)
Set the minimum value of deltaX.
Various templated functions that carry out common vector operations (see Templated Utility Functions)...
#define ROOTFIND_FAILEDCONVERGENCE
This means that the root solver failed to achieve convergence.
doublereal DeltaXnorm_
Delta X norm.
#define ROOTFIND_SUCCESS_XCONVERGENCEONLY
This return value means that the root finder resolved a solution in the x coordinate, however, convergence in F was not achieved.
doublereal fx_minTried_
Internal variable tracking f(x) of smallest x tried.
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
bool FuncIsGenerallyDecreasing_
Boolean indicating whether the function is decreasing with x.
doublereal x_maxTried_
Internal variable tracking largest x tried.
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Header file for implicit nonlinear solver of a one dimensional function (see Numerical Utilities with...
std::vector< struct rfTable > rfHistory_
Vector of iteration histories.
void setFuncIsGenerallyDecreasing(bool value)
Set the function behavior flag.
Structure containing the iteration history.
int specifiedDeltaXnorm_
Boolean indicating whether DeltaXnorm_ has been specified by the user or not.
RootFind & operator=(const RootFind &right)
void printTable()
Print the iteration history table.
doublereal delXNonzero(doublereal x1) const
Calculate a deltaX from an input value of x.
doublereal m_atolx
Absolute tolerance for the value of x.
int sign(T x)
Sign of a number. Returns -1 if x < 0, 1 if x > 0 and 0 if x == 0.
doublereal m_rtolx
Relative tolerance for the value of x.
bool writeLogAllowed_
Boolean to turn on the possibility of writing a log file.
doublereal m_rtolf
Relative tolerance for the value of f and x.
doublereal DeltaXMax_
Delta X Max.
doublereal x_minTried_
Internal variable tracking smallest x tried.
void setTol(doublereal rtolf, doublereal atolf, doublereal rtolx=0.0, doublereal atolx=0.0)
Set the tolerance parameters for the rootfinder.
Root finder for 1D problems.
void setFuncIsGenerallyIncreasing(bool value)
Set the function behavior flag.
Virtual base class for DAE residual function evaluators.
static void print_funcEval(FILE *fp, doublereal xval, doublereal fval, int its)
Print out a form for the current function evaluation.
doublereal m_funcTargetValue
Target value for the function.
bool FuncIsGenerallyIncreasing_
Boolean indicating whether the function is an increasing with x.
#define AssertThrow(expr, procedure)
Assertion must be true or an error is thrown.
bool theSame(doublereal x2, doublereal x1, doublereal factor=1.0) const
Function to decide whether two real numbers are the same or not.
doublereal fx_maxTried_
Internal variable tracking f(x) of largest x tried.
doublereal delXMeaningful(doublereal x1) const
Calculate a deltaX from an input value of x.
#define ROOTFIND_SOLNHIGHERTHANXMAX
This means that the rootfinder believes the solution is higher than xmax.
doublereal deltaXControlled(doublereal x2, doublereal x1) const
Calculate a controlled, nonzero delta between two numbers.
ResidEval * m_residFunc
Pointer to the residual function evaluator.
int solve(doublereal xmin, doublereal xmax, int itmax, doublereal &funcTargetValue, doublereal *xbest)
Using a line search method, find the root of a 1D function.
void writelogf(const char *fmt, const Args &... args)
Write a formatted message to the screen.
void setDeltaXMax(doublereal deltaX)
Set the maximum value of deltaX.
#define ROOTFIND_SOLNLOWERTHANXMIN
This means that the rootfinder believes the solution is lower than xmin.
#define ROOTFIND_BADINPUT
This means that the input to the root solver was defective.
doublereal m_maxstep
Maximum number of step sizes.
Contains declarations for string manipulation functions within Cantera.
doublereal deltaXConverged_
Value of delta X that is needed for convergence.
RootFind(ResidEval *resid)
Constructor for the object.
void setPrintLvl(int printLvl)
Set the print level from the rootfinder.
Namespace for the Cantera kernel.
doublereal m_atolf
Absolute tolerance for the value of f.
int specifiedDeltaXMax_
Boolean indicating whether DeltaXMax_ has been specified by the user or not.
#define ROOTFIND_SUCCESS
This means that the root solver was a success.
doublereal func(doublereal x)
Return the function value.