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