Cantera  2.2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RootFind.cpp
Go to the documentation of this file.
1 /**
2  * @file: RootFind.cpp root finder for 1D problems
3  */
4 
5 /*
6  * Copyright 2004 Sandia Corporation. Under the terms of Contract
7  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
8  * retains certain rights in this software.
9  * See file License.txt for licensing information.
10  */
11 
13 
14 // turn on debugging for now
15 #ifndef DEBUG_MODE
16 #define DEBUG_MODE
17 #undef DEBUG_MODE_ENABLED
18 #define DEBUG_MODE_ENABLED 1
19 #endif
20 
21 #include "cantera/base/utilities.h"
23 
24 using namespace std;
25 namespace Cantera
26 {
27 
28 //! Print out a form for the current function evaluation
29 /*!
30  * @param fp Pointer to the FILE object
31  * @param xval Current value of x
32  * @param fval Current value of f
33  * @param its Current iteration value
34  */
35 static void print_funcEval(FILE* fp, doublereal xval, doublereal fval, int its)
36 {
37  fprintf(fp,"\n");
38  fprintf(fp,"...............................................................\n");
39  fprintf(fp,".................. RootFind Function Evaluation ...............\n");
40  fprintf(fp,".................. iteration = %5d ........................\n", its);
41  fprintf(fp,".................. value = %12.5g ......................\n", xval);
42  fprintf(fp,".................. funct = %12.5g ......................\n", fval);
43  fprintf(fp,"...............................................................\n");
44  fprintf(fp,"\n");
45 }
46 
47 RootFind::RootFind(ResidEval* resid) :
48  m_residFunc(resid),
49  m_funcTargetValue(0.0),
50  m_atolf(1.0E-11),
51  m_atolx(1.0E-11),
52  m_rtolf(1.0E-5),
53  m_rtolx(1.0E-5),
54  m_maxstep(1000),
55  printLvl(0),
56  writeLogAllowed_(false),
57  DeltaXnorm_(0.01),
58  specifiedDeltaXnorm_(0),
59  DeltaXMax_(1.0E6),
60  specifiedDeltaXMax_(0),
61  FuncIsGenerallyIncreasing_(false),
62  FuncIsGenerallyDecreasing_(false),
63  deltaXConverged_(0.0),
64  x_maxTried_(-1.0E300),
65  fx_maxTried_(0.0),
66  x_minTried_(1.0E300),
67  fx_minTried_(0.0)
68 {
69 }
70 
72  m_residFunc(r.m_residFunc),
73  m_funcTargetValue(0.0),
74  m_atolf(1.0E-11),
75  m_atolx(1.0E-11),
76  m_rtolf(1.0E-5),
77  m_rtolx(1.0E-5),
78  m_maxstep(1000),
79  printLvl(0),
80  writeLogAllowed_(false),
81  DeltaXnorm_(0.01),
82  specifiedDeltaXnorm_(0),
83  DeltaXMax_(1.0E6),
84  specifiedDeltaXMax_(0),
85  FuncIsGenerallyIncreasing_(false),
86  FuncIsGenerallyDecreasing_(false),
87  deltaXConverged_(0.0),
88  x_maxTried_(-1.0E300),
89  fx_maxTried_(0.0),
90  x_minTried_(1.0E300),
91  fx_minTried_(0.0)
92 {
93  *this = r;
94 }
95 
97 {
98  if (this == &right) {
99  return *this;
100  }
101  m_residFunc = right.m_residFunc;
103  m_atolf = right.m_atolf;
104  m_atolx = right.m_atolx;
105  m_rtolf = right.m_rtolf;
106  m_rtolx = right.m_rtolx;
107  m_maxstep = right.m_maxstep;
108  printLvl = right.printLvl;
110  DeltaXnorm_ = right.DeltaXnorm_;
112  DeltaXMax_ = right.DeltaXMax_;
117  x_maxTried_ = right.x_maxTried_;
118  fx_maxTried_ = right.fx_maxTried_;
119  x_minTried_ = right.x_minTried_;
120  fx_minTried_ = right.fx_minTried_;
121 
122  return *this;
123 }
124 
125 doublereal RootFind::delXNonzero(doublereal x1) const
126 {
127  doublereal deltaX = 1.0E-14 * fabs(x1);
128  doublereal delmin = DeltaXnorm_ * 1.0E-14;
129  if (delmin > deltaX) {
130  return delmin;
131  }
132  return deltaX;
133 }
134 
135 doublereal RootFind::delXMeaningful(doublereal x1) const
136 {
137  doublereal del = delXNonzero(x1);
138  if (deltaXConverged_ > del) {
139  return deltaXConverged_;
140  }
141  return del;
142 }
143 
144 double RootFind::deltaXControlled(doublereal x2, doublereal x1) const
145 {
146  doublereal sgnn = 1.0;
147  if (x1 > x2) {
148  sgnn = -1.0;
149  }
150  doublereal deltaX = x2 - x1;
151  doublereal x = fabs(x2) + fabs(x1);
152  doublereal deltaXm = delXNonzero(x);
153  if (fabs(deltaX) < deltaXm) {
154  deltaX = sgnn * deltaXm;
155  }
156  return deltaX;
157 }
158 
159 bool RootFind::theSame(doublereal x2, doublereal x1, doublereal factor) const
160 {
161  doublereal x = fabs(x2) + fabs(x1);
162  doublereal deltaX = delXMeaningful(x);
163  doublereal deltaXSmall = factor * deltaX;
164  deltaXSmall = std::max(deltaXSmall , x * 1.0E-15);
165  if (fabs(x2 - x1) < deltaXSmall) {
166  return true;
167  }
168  return false;
169 }
170 
171 int RootFind::solve(doublereal xmin, doublereal xmax, int itmax, doublereal& funcTargetValue, doublereal* xbest)
172 {
173  /*
174  * We store the function target and then actually calculate a modified functional
175  *
176  * func = eval(x1) - m_funcTargetValue = 0
177  */
178  m_funcTargetValue = funcTargetValue;
179 
180  static int callNum = 0;
181  const char* stre = "RootFind ERROR: ";
182  const char* strw = "RootFind WARNING: ";
183  int converged = 0;
184  int bottomBump = 0;
185  int topBump = 0;
186  FILE* fp = 0;
187  int doFinalFuncCall = 0;
188  doublereal x1, x2, xnew, f1, f2, fnew, slope;
189  doublereal deltaX2 = 0.0, deltaXnew = 0.0;
190 
191  int posStraddle = 0;
192  int retn = ROOTFIND_FAILEDCONVERGENCE;
193  int foundPosF = 0;
194  int foundNegF = 0;
195  int foundStraddle = 0;
196  doublereal xPosF = 0.0;
197  doublereal fPosF = 1.0E300;
198  doublereal xNegF = 0.0;
199  doublereal fNegF = -1.0E300;
200  doublereal fnorm; /* A valid norm for the making the function value dimensionless */
201  doublereal xDelMin;
202  doublereal sgn;
203  doublereal fnoise = 0.0;
204  rfHistory_.clear();
205  rfTable rfT;
206  rfT.clear();
207  rfT.reasoning = "First Point: ";
208 
209  callNum++;
210  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
211  char fileName[80];
212  sprintf(fileName, "RootFind_%d.log", callNum);
213  fp = fopen(fileName, "w");
214  fprintf(fp, " Iter TP_its xval Func_val | Reasoning\n");
215  fprintf(fp, "-----------------------------------------------------"
216  "-------------------------------\n");
217  } else if (printLvl >= 3) {
218  writelog("WARNING: RootFind: printlvl >= 3, but debug mode not turned on\n");
219  }
220  if (xmax <= xmin) {
221  writelogf("%sxmin and xmax are bad: %g %g\n", stre, xmin, xmax);
222  funcTargetValue = func(*xbest);
223  return ROOTFIND_BADINPUT;
224  }
225 
226  /*
227  * If the maximum step size has not been specified, set it here to 1/5 of the
228  * domain range of x.
229  */
230  if (!specifiedDeltaXMax_) {
231  DeltaXMax_ = 0.2 *(xmax - xmin);
232  }
233 
234  if (!specifiedDeltaXnorm_) {
235  DeltaXnorm_ = 0.2 * DeltaXMax_;
236  } else {
237  if (DeltaXnorm_ > DeltaXMax_) {
238  if (specifiedDeltaXnorm_) {
240  } else {
241  DeltaXnorm_ = 0.5 * DeltaXMax_;
242  }
243  }
244  }
245 
246  /*
247  * Calculate an initial value of deltaXConverged_
248  */
249  deltaXConverged_ = m_rtolx * (*xbest) + m_atolx;
251  writelogf("%s DeltaXnorm_, %g, is too small compared to tols, increasing to %g\n",
254  }
255 
256  /*
257  * Find the first function value f1 = func(x1), by using the value entered into xbest.
258  * Process it
259  */
260  x1 = *xbest;
261  if (x1 < xmin || x1 > xmax) {
262  x1 = (xmin + xmax) / 2.0;
263  rfT.reasoning += " x1 set middle between xmin and xmax because entrance is outside bounds.";
264  } else {
265  rfT.reasoning += " x1 set to entrance x.";
266  }
267 
268  x_maxTried_ = x1;
269  x_minTried_ = x1;
270  int its = 1;
271  f1 = func(x1);
272 
273  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
274  print_funcEval(fp, x1, f1, its);
275  fprintf(fp, "%-5d %-5d %-15.5E %-15.5E\n", -2, 0, x1, f1);
276  }
277 
278  if (f1 == 0.0) {
279  *xbest = x1;
280  return 0;
281  } else if (f1 > fnoise) {
282  foundPosF = 1;
283  xPosF = x1;
284  fPosF = f1;
285  } else if (f1 < -fnoise) {
286  foundNegF = 1;
287  xNegF = x1;
288  fNegF = f1;
289  }
290  rfT.its = its;
291  rfT.TP_its = 0;
292  rfT.xval = x1;
293  rfT.fval = f1;
294  rfT.foundPos = foundPosF;
295  rfT.foundNeg = foundNegF;
296  rfT.deltaXConverged = m_rtolx * (fabs(x1) + 0.001);
297  rfT.deltaFConverged = fabs(f1) * m_rtolf;
298  rfT.delX = xmax - xmin;
299  rfHistory_.push_back(rfT);
300  rfT.clear();
301 
302  /*
303  * Now, this is actually a tricky part of the algorithm - Find the x value for
304  * the second point. It's tricky because we don't have a valid idea of the scale of x yet
305  *
306  */
307  rfT.reasoning = "Second Point: ";
308  if (x1 == 0.0) {
309  x2 = x1 + 0.01 * DeltaXnorm_;
310  rfT.reasoning += "Set by DeltaXnorm_";
311  } else {
312  x2 = x1 * 1.0001;
313  rfT.reasoning += "Set slightly higher.";
314  }
315  if (x2 > xmax) {
316  x2 = x1 - 0.01 * DeltaXnorm_;
317  rfT.reasoning += " - But adjusted to be within bounds";
318  }
319 
320  /*
321  * Find the second function value f2 = func(x2), Process it
322  */
323  deltaX2 = x2 - x1;
324  its++;
325  f2 = func(x2);
326  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
327  print_funcEval(fp, x2, f2, its);
328  fprintf(fp, "%-5d %-5d %-15.5E %-15.5E", -1, 0, x2, f2);
329  }
330 
331  /*
332  * Calculate the norm of the function, this is the nominal value of f. We try
333  * to reduce the nominal value of f by rtolf, this is the main convergence requirement.
334  */
335  if (m_funcTargetValue != 0.0) {
336  fnorm = m_atolf + fabs(m_funcTargetValue);
337  } else {
338  fnorm = 0.5*(fabs(f1) + fabs(f2)) + fabs(m_funcTargetValue) + m_atolf;
339  }
340  fnoise = 1.0E-100;
341 
342 
343  if (f2 > fnoise) {
344  if (!foundPosF) {
345  foundPosF = 1;
346  xPosF = x2;
347  fPosF = f2;
348  }
349  } else if (f2 < - fnoise) {
350  if (!foundNegF) {
351  foundNegF = 1;
352  xNegF = x2;
353  fNegF = f2;
354  }
355  } else if (f2 == 0.0) {
356  *xbest = x2;
357  return ROOTFIND_SUCCESS;
358  }
359  rfT.its = its;
360  rfT.TP_its = 0;
361  rfT.xval = x2;
362  rfT.fval = f2;
363  rfT.foundPos = foundPosF;
364  rfT.foundNeg = foundNegF;
365 
366 
367  /*
368  * See if we have already achieved a straddle
369  */
370  foundStraddle = foundPosF && foundNegF;
371  if (foundStraddle) {
372  if (xPosF > xNegF) {
373  posStraddle = 1;
374  } else {
375  posStraddle = 0;
376  }
377  }
378 
379  bool useNextStrat = false;
380  bool slopePointingToHigher = true;
381  // ---------------------------------------------------------------------------------------------
382  // MAIN LOOP
383  // ---------------------------------------------------------------------------------------------
384  do {
385  /*
386  * Find an estimate of the next point, xnew, to try based on
387  * a linear approximation from the last two points.
388  */
389  if (DEBUG_MODE_ENABLED && fabs(x2 - x1) < 1.0E-14) {
390  printf(" RootFind: we are here x2 = %g x1 = %g\n", x2, x1);
391  }
392  doublereal delXtmp = deltaXControlled(x2, x1);
393  slope = (f2 - f1) / delXtmp;
394  rfT.slope = slope;
395  rfHistory_.push_back(rfT);
396  rfT.clear();
397  rfT.reasoning = "";
398  if (fabs(slope) <= 1.0E-100) {
399  if (printLvl >= 2) {
400  writelogf("%s functions evals produced the same result, %g, at %g and %g\n",
401  strw, f2, x1, x2);
402  }
403  xnew = x2 + DeltaXnorm_;
404  slopePointingToHigher = true;
405  useNextStrat = true;
406  rfT.reasoning += "Slope is close to zero. ";
407  } else {
408  useNextStrat = false;
409  xnew = x2 - f2 / slope;
410  if (xnew > x2) {
411  slopePointingToHigher = true;
412  } else {
413  slopePointingToHigher = false;
414  }
415  rfT.reasoning += "Slope is good. ";
416  }
417  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
418  fprintf(fp, " | xlin = %-11.5E", xnew);
419  }
420  deltaXnew = xnew - x2;
421  /*
422  * If the suggested step size is too big, throw out step
423  */
424  if (!foundStraddle) {
425  if (fabs(xnew - x2) > DeltaXMax_) {
426  useNextStrat = true;
427  rfT.reasoning += "Too large change in xnew from slope. ";
428  }
429  if (fabs(deltaXnew) < fabs(deltaX2)) {
430  deltaXnew = 1.2 * deltaXnew;
431  xnew = x2 + deltaXnew;
432  }
433  }
434  /*
435  * If the slope can't be trusted using a different strategy for picking the next point
436  */
437  if (useNextStrat) {
438  rfT.reasoning += "Using DeltaXnorm, " + fp2str(DeltaXnorm_) + " and FuncIsGenerallyIncreasing hints. ";
439  if (f2 < 0.0) {
441  if (slopePointingToHigher) {
442  xnew = std::min(x2 + 3.0*DeltaXnorm_, xnew);
443  } else {
444  xnew = x2 + DeltaXnorm_;
445  }
446  } else if (FuncIsGenerallyDecreasing_) {
447  if (!slopePointingToHigher) {
448  xnew = std::max(x2 - 3.0*DeltaXnorm_, xnew);
449  } else {
450  xnew = x2 - DeltaXnorm_;
451  }
452  } else {
453  if (slopePointingToHigher) {
454  xnew = x2 + DeltaXnorm_;
455  } else {
456  xnew = x2 - DeltaXnorm_;
457  }
458  }
459  } else {
461  if (!slopePointingToHigher) {
462  xnew = std::max(x2 + 3.0*DeltaXnorm_, xnew);
463  } else {
464  xnew = x2 + DeltaXnorm_;
465  }
466  } else if (FuncIsGenerallyIncreasing_) {
467  if (! slopePointingToHigher) {
468  xnew = std::min(x2 - 3.0*DeltaXnorm_, xnew);
469  } else {
470  xnew = x2 - DeltaXnorm_;
471  }
472  } else {
473  if (slopePointingToHigher) {
474  xnew = x2 + DeltaXnorm_;
475  } else {
476  xnew = x2 - DeltaXnorm_;
477  }
478  }
479  }
480  }
481 
482  /*
483  * Here, if we have a straddle, we purposefully overshoot the smaller side by 5%. Yes it does lead to
484  * more iterations. However, we're interested in bounding x, and not just doing Newton's method.
485  */
486  if (foundStraddle) {
487  double delta = fabs(x2 - x1);
488  if (fabs(xnew - x1) < .01 * delta) {
489  xnew = x1 + 0.01 * (x2 - x1);
490  } else if (fabs(xnew - x2) < .01 * delta) {
491  xnew = x1 + 0.01 * (x2 - x1);
492  } else if ((xnew > x1 && xnew < x2) || (xnew < x1 && xnew > x2)) {
493  if (fabs(xnew - x1) < fabs(x2 - xnew)) {
494  xnew = x1 + 20./19. * (xnew - x1);
495  } else {
496  xnew = x2 + 20./19. * (xnew - x2);
497  }
498  }
499  }
500  /*
501  * OK, we have an estimate xnew.
502  *
503  *
504  * Put heuristic bounds on the step jump
505  */
506  if ((xnew > x1 && xnew < x2) || (xnew < x1 && xnew > x2)) {
507  /*
508  * If we are doing a jump in between the two previous points, make sure
509  * the new trial is no closer that 10% of the distances between x2-x1 to
510  * any of the original points. This is an important part of finding a good bound.
511  */
512  xDelMin = fabs(x2 - x1) / 10.;
513  if (fabs(xnew - x1) < xDelMin) {
514  xnew = x1 + sign(xnew-x1) * xDelMin;
515  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
516  fprintf(fp, " | x10%% = %-11.5E", xnew);
517  }
518  }
519  if (fabs(xnew - x2) < 0.1 * xDelMin) {
520  xnew = x2 + sign(xnew-x2) * 0.1 * xDelMin;
521  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
522  fprintf(fp, " | x10%% = %-11.5E", xnew);
523  }
524  }
525  } else {
526  /*
527  * If we are venturing into new ground, only allow the step jump
528  * to increase by 50% at each iteration, unless the step jump is less than
529  * the user has said that it is ok to take
530  */
531  doublereal xDelMax = 1.5 * fabs(x2 - x1);
532  if (specifiedDeltaXnorm_) {
533  if (0.5 * DeltaXnorm_ > xDelMax) {
534  xDelMax = 0.5 *DeltaXnorm_ ;
535  }
536  }
537  if (fabs(xDelMax) < fabs(xnew - x2)) {
538  xnew = x2 + sign(xnew-x2) * xDelMax;
539  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
540  fprintf(fp, " | xlimitsize = %-11.5E", xnew);
541  }
542  }
543  /*
544  * If we are doing a jump outside the two previous points, make sure
545  * the new trial is no closer that 10% of the distances between x2-x1 to
546  * any of the original points. This is an important part of finding a good bound.
547  */
548  xDelMin = 0.1 * fabs(x2 - x1);
549  if (fabs(xnew - x2) < xDelMin) {
550  xnew = x2 + sign(xnew - x2) * xDelMin;
551  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
552  fprintf(fp, " | x10%% = %-11.5E", xnew);
553  }
554  }
555  if (fabs(xnew - x1) < xDelMin) {
556  xnew = x1 + sign(xnew - x1) * xDelMin;
557  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
558  fprintf(fp, " | x10%% = %-11.5E", xnew);
559  }
560  }
561  }
562  /*
563  * HKM -> Not sure this section is needed
564  */
565  if (foundStraddle) {
566  double xorig = xnew;
567  if (posStraddle) {
568  if (f2 > 0.0) {
569  if (xnew > x2) {
570  xnew = (xNegF + x2)/2;
571  }
572  if (xnew < xNegF) {
573  xnew = (xNegF + x2)/2;
574  }
575  } else {
576  if (xnew < x2) {
577  xnew = (xPosF + x2)/2;
578  }
579  if (xnew > xPosF) {
580  xnew = (xPosF + x2)/2;
581  }
582  }
583  } else {
584  if (f2 > 0.0) {
585  if (xnew < x2) {
586  xnew = (xNegF + x2)/2;
587  }
588  if (xnew > xNegF) {
589  xnew = (xNegF + x2)/2;
590  }
591  } else {
592  if (xnew > x2) {
593  xnew = (xPosF + x2)/2;
594  }
595  if (xnew < xPosF) {
596  xnew = (xPosF + x2)/2;
597  }
598  }
599  }
600  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
601  if (xorig != xnew) {
602  fprintf(fp, " | xstraddle = %-11.5E", xnew);
603  }
604  }
605  }
606 
607  /*
608  * Enforce a minimum stepsize if we haven't found a straddle.
609  */
610  deltaXnew = xnew - x2;
611  if (fabs(deltaXnew) < 1.2 * delXMeaningful(xnew)) {
612  if (!foundStraddle) {
613  sgn = 1.0;
614  if (x2 > xnew) {
615  sgn = -1.0;
616  }
617  deltaXnew = 1.2 * delXMeaningful(xnew) * sgn;
618  rfT.reasoning += "Enforcing minimum stepsize from " + fp2str(xnew - x2) +
619  " to " + fp2str(deltaXnew);
620  xnew = x2 + deltaXnew;
621  }
622  }
623 
624  /*
625  * Guard against going above xmax or below xmin
626  */
627  if (xnew > xmax) {
628  topBump++;
629  if (topBump < 3) {
630  xnew = x2 + (xmax - x2) / 2.0;
631  rfT.reasoning += ("xval reduced to " + fp2str(xnew) + " because predicted xnew was above max value of " + fp2str(xmax));
632  } else {
633  if (x2 == xmax || x1 == xmax) {
634  // we are here when we are bumping against the top limit.
635  // No further action is possible
637  *xbest = xnew;
638  rfT.slope = slope;
639  rfT.reasoning += "Giving up because we're at xmax and xnew point higher: " + fp2str(xnew);
640  goto done;
641  } else {
642  rfT.reasoning += "xval reduced from " + fp2str(xnew) + " to the max value, " + fp2str(xmax);
643  xnew = xmax;
644  }
645  }
646  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
647  fprintf(fp, " | xlimitmax = %-11.5E", xnew);
648  }
649  }
650  if (xnew < xmin) {
651  bottomBump++;
652  if (bottomBump < 3) {
653  rfT.reasoning += ("xnew increased from " + fp2str(xnew) +" to " + fp2str(x2 - (x2 - xmin) / 2.0) +
654  " because above min value of " + fp2str(xmin));
655  xnew = x2 - (x2 - xmin) / 2.0;
656  } else {
657  if (x2 == xmin || x1 == xmin) {
658  // we are here when we are bumping against the bottom limit.
659  // No further action is possible
661  *xbest = xnew;
662  rfT.slope = slope;
663  rfT.reasoning = "Giving up because we're already at xmin and xnew points lower: " + fp2str(xnew);
664  goto done;
665  } else {
666  rfT.reasoning += "xval increased from " + fp2str(xnew) + " to the min value, " + fp2str(xmin);
667  xnew = xmin;
668  }
669  }
670  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
671  fprintf(fp, " | xlimitmin = %-11.5E", xnew);
672  }
673  }
674 
675  its++;
676  fnew = func(xnew);
677 
678  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
679  fprintf(fp,"\n");
680  print_funcEval(fp, xnew, fnew, its);
681  fprintf(fp, "%-5d %-5d %-15.5E %-15.5E", its, 0, xnew, fnew);
682  }
683  rfT.xval = xnew;
684  rfT.fval = fnew;
685  rfT.its = its;
686  if (foundStraddle) {
687  if (posStraddle) {
688  if (fnew > 0.0) {
689  if (xnew < xPosF) {
690  xPosF = xnew;
691  fPosF = fnew;
692  }
693  } else {
694  if (xnew > xNegF) {
695  xNegF = xnew;
696  fNegF = fnew;
697  }
698  }
699  } else {
700  if (fnew > 0.0) {
701  if (xnew > xPosF) {
702  xPosF = xnew;
703  fPosF = fnew;
704  }
705  } else {
706  if (xnew < xNegF) {
707  xNegF = xnew;
708  fNegF = fnew;
709  }
710  }
711  }
712  }
713 
714  if (! foundStraddle) {
715  if (fnew > fnoise) {
716  if (!foundPosF) {
717  foundPosF = 1;
718  rfT.foundPos = 1;
719  xPosF = xnew;
720  fPosF = fnew;
721  foundStraddle = 1;
722  if (xPosF > xNegF) {
723  posStraddle = 1;
724  } else {
725  posStraddle = 0;
726  }
727  }
728  } else if (fnew < - fnoise) {
729  if (!foundNegF) {
730  foundNegF = 1;
731  rfT.foundNeg = 1;
732  xNegF = xnew;
733  fNegF = fnew;
734  foundStraddle = 1;
735  if (xPosF > xNegF) {
736  posStraddle = 1;
737  } else {
738  posStraddle = 0;
739  }
740  }
741  }
742  }
743 
744  x1 = x2;
745  f1 = f2;
746 
747  x2 = xnew;
748  f2 = fnew;
749 
750  /*
751  * As we go on to new data points, we make sure that
752  * we have the best straddle of the solution with the choice of F1 and F2 when
753  * we do have a straddle to work with.
754  */
755  if (foundStraddle) {
756  bool foundBetterPos = false;
757  bool foundBetterNeg = false;
758  if (posStraddle) {
759  if (f2 > 0.0) {
760  if (xPosF < x2) {
761  foundBetterPos = false;
762  x2 = xPosF;
763  f2 = fPosF;
764  }
765  if (f1 > 0.0) {
766  if (foundBetterPos) {
767  x1 = xNegF;
768  f1 = fNegF;
769  } else {
770  if (x1 >= x2) {
771  x1 = xNegF;
772  f1 = fNegF;
773  }
774  }
775  }
776  } else {
777  if (xNegF > x2) {
778  foundBetterNeg = false;
779  x2 = xNegF;
780  f2 = fNegF;
781  }
782  if (f1 < 0.0) {
783  if (foundBetterNeg) {
784  x1 = xPosF;
785  f1 = fPosF;
786  } else {
787  if (x1 <= x2) {
788  x1 = xPosF;
789  f1 = fPosF;
790  }
791  }
792  }
793  }
794  } else {
795  if (f2 < 0.0) {
796  if (xNegF < x2) {
797  foundBetterNeg = false;
798  x2 = xNegF;
799  f2 = fNegF;
800  }
801  if (f1 < 0.0) {
802  if (foundBetterNeg) {
803  x1 = xPosF;
804  f1 = fPosF;
805  } else {
806  if (x1 >= x2) {
807  x1 = xPosF;
808  f1 = fPosF;
809  }
810  }
811  }
812  } else {
813  if (xPosF > x2) {
814  foundBetterPos = true;
815  x2 = xPosF;
816  f2 = fPosF;
817  }
818  if (f1 > 0.0) {
819  if (foundBetterNeg) {
820  x1 = xNegF;
821  f1 = fNegF;
822  } else {
823  if (x1 <= x2) {
824  x1 = xNegF;
825  f1 = fNegF;
826  }
827  }
828  }
829  }
830  }
831  AssertThrow((f1* f2 <= 0.0), "F1 and F2 aren't bounding");
832  }
833 
834  deltaX2 = deltaXnew;
835  deltaXnew = x2 - x1;
836  deltaXConverged_ = 0.5 * deltaXConverged_ + 0.5 * (m_rtolx * 0.5 * (fabs(x2) + fabs(x1)) + m_atolx);
837  rfT.deltaXConverged = deltaXConverged_;
838  rfT.deltaFConverged = fnorm * m_rtolf;
839  if (foundStraddle) {
840  rfT.delX = std::max(fabs(deltaX2), fabs(deltaXnew));
841  } else {
842  rfT.delX = std::max(fabs(deltaX2), fabs(deltaXnew));
843  if (x2 < x1) {
844  rfT.delX = std::max(rfT.delX, x2 - xmin);
845  } else {
846  rfT.delX = std::max(rfT.delX, xmax - x2);
847  }
848  }
849  /*
850  * Section To Determine CONVERGENCE criteria
851  */
852  doFinalFuncCall = 0;
853  if ((fabs(fnew / fnorm) < m_rtolf) && foundStraddle) {
854  if (fabs(deltaX2) < deltaXConverged_ && fabs(deltaXnew) < deltaXConverged_) {
855  converged = 1;
856  rfT.reasoning += "NormalConvergence";
857  retn = ROOTFIND_SUCCESS;
858  }
859 
860  else if (fabs(slope) > 1.0E-100) {
861  double xdels = fabs(fnew / slope);
862  if (xdels < deltaXConverged_ * 0.3) {
863  converged = 1;
864  rfT.reasoning += "NormalConvergence-SlopelimitsDelX";
865  doFinalFuncCall = 1;
866  retn = ROOTFIND_SUCCESS;
867  }
868  }
869 
870 
871  /*
872  * Check for excess convergence in the x coordinate
873  */
874  if (!converged) {
875  if (foundStraddle) {
876  doublereal denom = fabs(x1 - x2);
877  if (denom < 1.0E-200) {
879  converged = true;
880  rfT.reasoning += "ConvergenceFZero but X1X2Identical";
881  }
882  if (theSame(x2, x1, 1.0E-2)) {
883  converged = true;
884  rfT.reasoning += " ConvergenceF and XSame";
885  retn = ROOTFIND_SUCCESS;
886  }
887  }
888  }
889  } else {
890  /*
891  * We are here when F is not converged, but we may want to end anyway
892  */
893  if (!converged) {
894  if (foundStraddle) {
895  doublereal denom = fabs(x1 - x2);
896  if (denom < 1.0E-200) {
898  converged = true;
899  rfT.reasoning += "FNotConverged but X1X2Identical";
900  }
901  /*
902  * The premise here is that if x1 and x2 get close to one another,
903  * then the accuracy of the calculation gets destroyed.
904  */
905  if (theSame(x2, x1, 1.0E-5)) {
906  converged = true;
908  rfT.reasoning += "FNotConverged but XSame";
909  }
910  }
911  }
912  }
913  } while (! converged && its < itmax);
914 
915 done:
916  if (converged) {
917  rfT.slope = slope;
918  rfHistory_.push_back(rfT);
919  rfT.clear();
920  rfT.its = its;
921  AssertThrow((f1* f2 <= 0.0), "F1 and F2 aren't bounding");
922 
923  double x_fpos = x2;
924  double x_fneg = x1;
925  if (f2 < 0.0) {
926  x_fpos = x1;
927  x_fneg = x2;
928  }
929  rfT.delX = fabs(x_fpos - x_fneg);
930  if (doFinalFuncCall || (fabs(f1) < 2.0 * fabs(f2))) {
931  double delXtmp = deltaXControlled(x2, x1);
932  slope = (f2 - f1) / delXtmp;
933  xnew = x2 - f2 / slope;
934  its++;
935  fnew = func(xnew);
936  if (fnew > 0.0) {
937  if (fabs(xnew - x_fneg) < fabs(x_fpos - x_fneg)) {
938  x_fpos = xnew;
939  rfT.delX = fabs(xnew - x_fneg);
940  }
941  } else {
942  if (fabs(xnew - x_fpos) < fabs(x_fpos - x_fneg)) {
943  x_fneg = xnew;
944  rfT.delX = fabs(xnew - x_fpos);
945  }
946  }
947  rfT.its = its;
948  if (fabs(fnew) < fabs(f2) && (fabs(fnew) < fabs(f1))) {
949  *xbest = xnew;
950  if (doFinalFuncCall) {
951  rfT.reasoning += "CONVERGENCE: Another Evaluation Requested";
952  rfT.delX = fabs(xnew - x2);
953  } else {
954  rfT.reasoning += "CONVERGENCE: Another Evaluation done because f1 < f2";
955  rfT.delX = fabs(xnew - x1);
956  }
957  rfT.fval = fnew;
958  rfT.xval = xnew;
959  x2 = xnew;
960  f2 = fnew;
961  } else if (fabs(f1) < fabs(f2)) {
962  rfT.its = its;
963  rfT.xval = xnew;
964  rfT.fval = fnew;
965 
966  rfT.slope = slope;
967  rfT.reasoning += "CONVERGENCE: Another Evaluation not as good as Second Point ";
968  rfHistory_.push_back(rfT);
969  rfT.clear();
970  rfT.its = its;
971  std::swap(f1, f2);
972  std::swap(x1, x2);
973  *xbest = x2;
974  if (fabs(fnew) < fabs(f1)) {
975  if (f1 * fnew > 0.0) {
976  std::swap(f1, fnew);
977  std::swap(x1, xnew);
978  }
979  }
980 
981  rfT.its = its;
982  rfT.xval = *xbest;
983  rfT.fval = f2;
984  rfT.delX = fabs(x_fpos - x_fneg);
985  rfT.reasoning += "CONVERGENCE: NormalEnding -> Second point used";
986  } else {
987  rfT.its = its;
988  rfT.xval = xnew;
989  rfT.fval = fnew;
990 
991  rfT.slope = slope;
992  rfT.reasoning += "CONVERGENCE: Another Evaluation not as good as First Point ";
993  rfHistory_.push_back(rfT);
994  rfT.clear();
995  rfT.its = its;
996  *xbest = x2;
997  rfT.xval = *xbest;
998  rfT.fval = f2;
999  rfT.delX = fabs(x_fpos - x_fneg);
1000  rfT.reasoning += "CONVERGENCE: NormalEnding -> Last point used";
1001  }
1002  } else {
1003 
1004  *xbest = x2;
1005 
1006  rfT.xval = *xbest;
1007  rfT.fval = f2;
1008  rfT.delX = fabs(x2 - x1);
1009  rfT.reasoning += "CONVERGENCE: NormalEnding -> Last point used";
1010  }
1011  funcTargetValue = f2 + m_funcTargetValue;
1012  rfT.slope = slope;
1013 
1014  if (printLvl >= 1) {
1015  writelogf("RootFind success: convergence achieved\n");
1016  }
1017  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
1018  fprintf(fp, " | RootFind success in %d its, fnorm = %g\n", its, fnorm);
1019  }
1020  rfHistory_.push_back(rfT);
1021  } else {
1022  rfT.reasoning = "FAILED CONVERGENCE ";
1023  rfT.slope = slope;
1024  rfT.its = its;
1025  if (retn == ROOTFIND_SOLNHIGHERTHANXMAX) {
1026  if (printLvl >= 1) {
1027  writelogf("RootFind ERROR: Soln probably lies higher than xmax, %g: best guess = %g\n", xmax, *xbest);
1028  }
1029  rfT.reasoning += "Soln probably lies higher than xmax, " + fp2str(xmax) + ": best guess = " + fp2str(*xbest);
1030  } else if (retn == ROOTFIND_SOLNLOWERTHANXMIN) {
1031  if (printLvl >= 1) {
1032  writelogf("RootFind ERROR: Soln probably lies lower than xmin, %g: best guess = %g\n", xmin, *xbest);
1033  }
1034  rfT.reasoning += "Soln probably lies lower than xmin, " + fp2str(xmin) + ": best guess = " + fp2str(*xbest);
1035  } else {
1037  if (printLvl >= 1) {
1038  writelogf("RootFind ERROR: maximum iterations exceeded without convergence, cause unknown\n");
1039  }
1040  rfT.reasoning += "Maximum iterations exceeded without convergence, cause unknown";
1041  }
1042  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
1043  fprintf(fp, "\nRootFind failure in %d its\n", its);
1044  }
1045 
1046  *xbest = x2;
1047  funcTargetValue = f2 + m_funcTargetValue;
1048  rfT.xval = *xbest;
1049  rfT.fval = f2;
1050  rfHistory_.push_back(rfT);
1051  }
1052  if (DEBUG_MODE_ENABLED && printLvl >= 3 && writeLogAllowed_) {
1053  fclose(fp);
1054  }
1055 
1056  if (printLvl >= 2) {
1057  printTable();
1058  }
1059 
1060  return retn;
1061 }
1062 
1063 doublereal RootFind::func(doublereal x)
1064 {
1065  doublereal r;
1066  if (DEBUG_MODE_ENABLED) {
1067  checkFinite(x);
1068  }
1069  m_residFunc->evalSS(0.0, &x, &r);
1070  if (DEBUG_MODE_ENABLED) {
1071  checkFinite(r);
1072  }
1073  doublereal ff = r - m_funcTargetValue;
1074  if (x >= x_maxTried_) {
1075  x_maxTried_ = x;
1076  fx_maxTried_ = ff;
1077  }
1078  if (x <= x_minTried_) {
1079  x_minTried_ = x;
1080  fx_minTried_ = ff;
1081  }
1082  return ff;
1083 }
1084 
1085 void RootFind::setTol(doublereal rtolf, doublereal atolf, doublereal rtolx, doublereal atolx)
1086 {
1087  m_atolf = atolf;
1088  m_rtolf = rtolf;
1089  if (rtolx <= 0.0) {
1090  m_rtolx = atolf;
1091  } else {
1092  m_rtolx = rtolx;
1093  }
1094  if (atolx <= 0.0) {
1095  m_atolx = atolf;
1096  } else {
1097  m_atolx = atolx;
1098  }
1099 }
1100 
1101 void RootFind::setPrintLvl(int printlvl)
1102 {
1103  printLvl = printlvl;
1104 }
1105 
1107 {
1108  if (value) {
1110  }
1112 }
1113 
1115 {
1116  if (value) {
1118  }
1120 }
1121 
1122 void RootFind::setDeltaX(doublereal deltaXNorm)
1123 {
1124  DeltaXnorm_ = deltaXNorm;
1126 }
1127 
1128 void RootFind::setDeltaXMax(doublereal deltaX)
1129 {
1130  DeltaXMax_ = deltaX;
1131  specifiedDeltaXMax_ = 1;
1132 }
1133 
1135 {
1136  printf("\t----------------------------------------------------------------------------------------------------------------------------------------\n");
1137  printf("\t RootFinder Summary table: \n");
1138  printf("\t FTarget = %g\n", m_funcTargetValue);
1139  printf("\t Iter | xval delX deltaXConv | slope | foundP foundN| F - F_targ deltaFConv | Reasoning\n");
1140  printf("\t----------------------------------------------------------------------------------------------------------------------------------------\n");
1141  for (int i = 0; i < (int) rfHistory_.size(); i++) {
1142  struct rfTable rfT = rfHistory_[i];
1143  printf("\t %3d |%- 17.11E %- 13.7E %- 13.7E |%- 13.5E| %3d %3d | %- 12.5E %- 12.5E | %s \n",
1144  rfT.its, rfT.xval, rfT.delX, rfT.deltaXConverged, rfT.slope, rfT.foundPos, rfT.foundNeg, rfT.fval,
1145  rfT.deltaFConverged, (rfT.reasoning).c_str());
1146  }
1147  printf("\t----------------------------------------------------------------------------------------------------------------------------------------\n");
1148 }
1149 
1150 }
void checkFinite(const double tmp)
Check to see that a number is finite (not NaN, +Inf or -Inf)
Definition: checkFinite.cpp:43
void setDeltaX(doublereal deltaXNorm)
Set the minimum value of deltaX.
Definition: RootFind.cpp:1122
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.
Definition: RootFind.h:39
doublereal DeltaXnorm_
Delta X norm. This is the nominal value of deltaX that will be used by the program.
Definition: RootFind.h:349
#define ROOTFIND_SUCCESS_XCONVERGENCEONLY
This return value means that the root finder resolved a solution in the x coordinate However...
Definition: RootFind.h:37
doublereal fx_minTried_
Internal variable tracking f(x) of smallest x tried.
Definition: RootFind.h:386
bool FuncIsGenerallyDecreasing_
Boolean indicating whether the function is decreasing with x.
Definition: RootFind.h:367
doublereal x_maxTried_
Internal variable tracking largest x tried.
Definition: RootFind.h:377
Header file for implicit nonlinear solver of a one dimensional function (see Numerical Utilities with...
std::vector< struct rfTable > rfHistory_
Vector of iteration histories.
Definition: RootFind.h:430
void setFuncIsGenerallyDecreasing(bool value)
Set the function behavior flag.
Definition: RootFind.cpp:1114
Structure containing the iteration history.
Definition: RootFind.h:389
int specifiedDeltaXnorm_
Boolean indicating whether DeltaXnorm_ has been specified by the user or not.
Definition: RootFind.h:352
RootFind & operator=(const RootFind &right)
Assignment operator.
Definition: RootFind.cpp:96
void printTable()
Print the iteration history table.
Definition: RootFind.cpp:1134
doublereal delXNonzero(doublereal x1) const
Calculate a deltaX from an input value of x.
Definition: RootFind.cpp:125
int printLvl
Print level.
Definition: RootFind.h:341
doublereal m_atolx
Absolute tolerance for the value of x.
Definition: RootFind.h:321
int sign(T x)
Sign of a number. Returns -1 if x < 0, 1 if x > 0 and 0 if x == 0.
Definition: global.h:276
doublereal m_rtolx
Relative tolerance for the value of x.
Definition: RootFind.h:327
bool writeLogAllowed_
Boolean to turn on the possibility of writing a log file.
Definition: RootFind.h:345
doublereal m_rtolf
Relative tolerance for the value of f and x.
Definition: RootFind.h:324
doublereal DeltaXMax_
Delta X Max. This is the maximum value of deltaX that will be used by the program.
Definition: RootFind.h:358
doublereal x_minTried_
Internal variable tracking smallest x tried.
Definition: RootFind.h:383
void setTol(doublereal rtolf, doublereal atolf, doublereal rtolx=0.0, doublereal atolx=0.0)
Set the tolerance parameters for the rootfinder.
Definition: RootFind.cpp:1085
void writelogf(const char *fmt,...)
Write a formatted message to the screen.
Definition: global.cpp:38
doublereal deltaXControlled(doublereal x2, doublereal x1) const
Calculate a controlled, nonzero delta between two numbers.
Definition: RootFind.cpp:144
Root finder for 1D problems.
Definition: RootFind.h:131
void setFuncIsGenerallyIncreasing(bool value)
Set the function behavior flag.
Definition: RootFind.cpp:1106
Virtual base class for DAE residual function evaluators.
Definition: ResidEval.h:34
static void print_funcEval(FILE *fp, doublereal xval, doublereal fval, int its)
Print out a form for the current function evaluation.
Definition: RootFind.cpp:35
doublereal m_funcTargetValue
Target value for the function. We seek the value of f that is equal to this value.
Definition: RootFind.h:315
std::string fp2str(const double x, const std::string &fmt)
Convert a double into a c++ string.
Definition: stringUtils.cpp:28
bool FuncIsGenerallyIncreasing_
Boolean indicating whether the function is an increasing with x.
Definition: RootFind.h:364
#define AssertThrow(expr, procedure)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:283
doublereal fx_maxTried_
Internal variable tracking f(x) of largest x tried.
Definition: RootFind.h:380
#define ROOTFIND_SOLNHIGHERTHANXMAX
This means that the rootfinder believes the solution is higher than xmax.
Definition: RootFind.h:45
ResidEval * m_residFunc
Pointer to the residual function evaluator.
Definition: RootFind.h:312
int solve(doublereal xmin, doublereal xmax, int itmax, doublereal &funcTargetValue, doublereal *xbest)
Using a line search method, find the root of a 1D function.
Definition: RootFind.cpp:171
void setDeltaXMax(doublereal deltaX)
Set the maximum value of deltaX.
Definition: RootFind.cpp:1128
#define ROOTFIND_SOLNLOWERTHANXMIN
This means that the rootfinder believes the solution is lower than xmin.
Definition: RootFind.h:43
#define ROOTFIND_BADINPUT
This means that the input to the root solver was defective.
Definition: RootFind.h:41
doublereal m_maxstep
Maximum number of step sizes.
Definition: RootFind.h:330
Contains declarations for string manipulation functions within Cantera.
doublereal delXMeaningful(doublereal x1) const
Calculate a deltaX from an input value of x.
Definition: RootFind.cpp:135
doublereal deltaXConverged_
Value of delta X that is needed for convergence.
Definition: RootFind.h:374
void writelog(const std::string &msg)
Write a message to the screen.
Definition: global.cpp:33
RootFind(ResidEval *resid)
Constructor for the object.
Definition: RootFind.cpp:47
void setPrintLvl(int printLvl)
Set the print level from the rootfinder.
Definition: RootFind.cpp:1101
doublereal m_atolf
Absolute tolerance for the value of f.
Definition: RootFind.h:318
int specifiedDeltaXMax_
Boolean indicating whether DeltaXMax_ has been specified by the user or not.
Definition: RootFind.h:361
#define ROOTFIND_SUCCESS
This means that the root solver was a success.
Definition: RootFind.h:29
bool theSame(doublereal x2, doublereal x1, doublereal factor=1.0) const
Function to decide whether two real numbers are the same or not.
Definition: RootFind.cpp:159
doublereal func(doublereal x)
Return the function value.
Definition: RootFind.cpp:1063