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