CoolProp 7.1.0
An open-source fluid property and humid air property database
Solvers.cpp
Go to the documentation of this file.
1#include <vector>
2#include "Solvers.h"
3#include "math.h"
4#include "MatrixMath.h"
5#include <iostream>
6#include "CoolPropTools.h"
7#include <Eigen/Dense>
8
9namespace CoolProp {
10
13std::vector<std::vector<double>> FuncWrapperND::Jacobian(const std::vector<double>& x) {
14 double epsilon;
15 std::size_t N = x.size();
16 std::vector<double> r, xp;
17 std::vector<std::vector<double>> J(N, std::vector<double>(N, 0));
18 std::vector<double> r0 = call(x);
19 // Build the Jacobian by column
20 for (std::size_t i = 0; i < N; ++i) {
21 xp = x;
22 epsilon = 0.001 * x[i];
23 xp[i] += epsilon;
24 r = call(xp);
25
26 for (std::size_t j = 0; j < N; ++j) {
27 J[j][i] = (r[j] - r0[j]) / epsilon;
28 }
29 }
30 return J;
31}
32
50std::vector<double> NDNewtonRaphson_Jacobian(FuncWrapperND* f, const std::vector<double>& x, double tol, int maxiter, double w) {
51 int iter = 0;
52 f->errstring.clear();
53 std::vector<double> f0, v;
54 std::vector<std::vector<double>> JJ;
55 std::vector<double> x0 = x;
56 Eigen::VectorXd r(x0.size());
57 Eigen::MatrixXd J(x0.size(), x0.size());
58 double error = 999;
59 while (iter == 0 || std::abs(error) > tol) {
60 f0 = f->call(x0);
61 JJ = f->Jacobian(x0);
62
63 for (std::size_t i = 0; i < x0.size(); ++i) {
64 r(i) = f0[i];
65 for (std::size_t j = 0; j < x0.size(); ++j) {
66 J(i, j) = JJ[i][j];
67 }
68 }
69
70 Eigen::VectorXd v = J.colPivHouseholderQr().solve(-r);
71
72 // Update the guess
73 double max_relchange = -1;
74 for (std::size_t i = 0; i < x0.size(); i++) {
75 x0[i] += w * v(i);
76 double relchange = std::abs(v(i) / x0[i]);
77 if (std::abs(x0[i]) > 1e-16 && relchange > max_relchange) {
78 max_relchange = relchange;
79 }
80 }
81
82 // Stop if the solution is not changing by more than numerical precision
83 double max_abschange = v.cwiseAbs().maxCoeff();
84 if (max_abschange < DBL_EPSILON * 100) {
85 return x0;
86 }
87 if (max_relchange < 1e-12) {
88 return x0;
89 }
90 error = root_sum_square(f0);
91 if (iter > maxiter) {
92 f->errstring = "reached maximum number of iterations";
93 x0[0] = _HUGE;
94 }
95 iter++;
96 }
97 return x0;
98}
99
109double Newton(FuncWrapper1DWithDeriv* f, double x0, double ftol, int maxiter) {
110 double x, dx, dfdx, fval = 999;
111
112 // Initialize
113 f->iter = 0;
114 f->errstring.clear();
115 x = x0;
116 while (f->iter < maxiter || std::abs(fval) > ftol) {
117 fval = f->call(x);
118 dfdx = f->deriv(x);
119 dx = -fval / dfdx;
120
121 if (!ValidNumber(fval)) {
122 f->errstring = "Residual function in newton returned invalid number";
123 throw ValueError("Residual function in newton returned invalid number");
124 };
125
126 if (f->verbosity > 0){
127 std::cout << format("i: %d, x: %0.15g, dx: %g, f: %g, dfdx: %g", f->iter, x, dx, fval, dfdx) << std::endl;
128 }
129
130 x += dx;
131
132 if (std::abs(dx / x) < 1e-11) {
133 return x;
134 }
135
136 if (f->iter > maxiter) {
137 f->errstring = "Newton reached maximum number of iterations";
138 throw SolutionError(format("Newton reached maximum number of iterations"));
139 }
140 f->iter++;
141 }
142 return x;
143}
160double Halley(FuncWrapper1DWithTwoDerivs* f, double x0, double ftol, int maxiter, double xtol_rel) {
161 double x, dx, fval = 999, dfdx, d2fdx2;
162
163 // Initialize
164 f->iter = 0;
165 f->errstring.clear();
166 x = x0;
167
168 // The relaxation factor (less than 1 for smaller steps)
169 double omega = f->options.get_double("omega", 1.0);
170
171 while (f->iter < 2 || std::abs(fval) > ftol) {
172 if (f->input_not_in_range(x)) {
173 std::string msg = format("Input [%0.15g] is out of range", x);
174 f->errstring = msg;
175 throw ValueError(msg);
176 }
177
178 fval = f->call(x);
179 dfdx = f->deriv(x);
180 d2fdx2 = f->second_deriv(x);
181
182 if (!ValidNumber(fval)) {
183 f->errstring = "Residual function in Halley returned invalid number";
184 throw ValueError("Residual function in Halley returned invalid number");
185 };
186 if (!ValidNumber(dfdx)) {
187 f->errstring = "Derivative function in Halley returned invalid number";
188 throw ValueError("Derivative function in Halley returned invalid number");
189 };
190
191 dx = -omega * (2 * fval * dfdx) / (2 * POW2(dfdx) - fval * d2fdx2);
192
193 if (f->verbosity > 0){
194 std::cout << format("i: %d, x: %0.15g, dx: %g, f: %g, dfdx: %g, d2fdx2: %g", f->iter, x, dx, fval, dfdx, d2fdx2) << std::endl;
195 }
196
197 x += dx;
198
199
200
201 if (std::abs(dx / x) < xtol_rel) {
202 return x;
203 }
204
205 if (f->iter > maxiter) {
206 f->errstring = "Halley reached maximum number of iterations";
207 throw SolutionError(format("Halley reached maximum number of iterations"));
208 }
209 f->iter += 1;
210 }
211 return x;
212}
213
230double Householder4(FuncWrapper1DWithThreeDerivs* f, double x0, double ftol, int maxiter, double xtol_rel) {
231 double x, dx, fval = 999, dfdx, d2fdx2, d3fdx3;
232
233 // Initialization
234 f->iter = 1;
235 f->errstring.clear();
236 x = x0;
237
238 // The relaxation factor (less than 1 for smaller steps)
239 double omega = f->options.get_double("omega", 1.0);
240
241 while (f->iter < 2 || std::abs(fval) > ftol) {
242 if (f->input_not_in_range(x)) {
243 throw ValueError(format("Input [%g] is out of range", x));
244 }
245
246 fval = f->call(x);
247 dfdx = f->deriv(x);
248 d2fdx2 = f->second_deriv(x);
249 d3fdx3 = f->third_deriv(x);
250
251 if (!ValidNumber(fval)) {
252 throw ValueError("Residual function in Householder4 returned invalid number");
253 };
254 if (!ValidNumber(dfdx)) {
255 throw ValueError("Derivative function in Householder4 returned invalid number");
256 };
257 if (!ValidNumber(d2fdx2)) {
258 throw ValueError("Second derivative function in Householder4 returned invalid number");
259 };
260 if (!ValidNumber(d3fdx3)) {
261 throw ValueError("Third derivative function in Householder4 returned invalid number");
262 };
263
264 dx = -omega * fval * (POW2(dfdx) - fval * d2fdx2 / 2.0) / (POW3(dfdx) - fval * dfdx * d2fdx2 + d3fdx3 * POW2(fval) / 6.0);
265
266 x += dx;
267
268 if (std::abs(dx / x) < xtol_rel) {
269 return x;
270 }
271
272 if (f->iter > maxiter) {
273 f->errstring = "reached maximum number of iterations";
274 throw SolutionError(format("Householder4 reached maximum number of iterations"));
275 }
276 f->iter += 1;
277 }
278 return x;
279}
280
291double Secant(FuncWrapper1D* f, double x0, double dx, double tol, int maxiter) {
292#if defined(COOLPROP_DEEP_DEBUG)
293 static std::vector<double> xlog, flog;
294 xlog.clear();
295 flog.clear();
296#endif
297
298 // Initialization
299 double x1 = 0, x2 = 0, x3 = 0, y1 = 0, y2 = 0, x = x0, fval = 999;
300 f->iter = 1;
301 f->errstring.clear();
302
303 // The relaxation factor (less than 1 for smaller steps)
304 double omega = f->options.get_double("omega", 1.0);
305
306 if (std::abs(dx) == 0) {
307 f->errstring = "dx cannot be zero";
308 return _HUGE;
309 }
310 while (f->iter <= 2 || std::abs(fval) > tol) {
311 if (f->iter == 1) {
312 x1 = x0;
313 x = x1;
314 }
315 if (f->iter == 2) {
316 x2 = x0 + dx;
317 x = x2;
318 }
319 if (f->iter > 2) {
320 x = x2;
321 }
322
323 if (f->input_not_in_range(x)) {
324 throw ValueError(format("Input [%g] is out of range", x));
325 }
326
327 fval = f->call(x);
328
329#if defined(COOLPROP_DEEP_DEBUG)
330 xlog.push_back(x);
331 flog.push_back(fval);
332#endif
333
334 if (!ValidNumber(fval)) {
335 throw ValueError("Residual function in secant returned invalid number");
336 };
337 if (f->iter == 1) {
338 y1 = fval;
339 }
340 if (f->iter > 1) {
341 double deltax = x2 - x1;
342 if (std::abs(deltax) < 1e-14) {
343 return x;
344 }
345 y2 = fval;
346 double deltay = y2 - y1;
347 if (f->iter > 2 && std::abs(deltay) < 1e-14) {
348 return x;
349 }
350 x3 = x2 - omega * y2 / (y2 - y1) * (x2 - x1);
351 y1 = y2;
352 x1 = x2;
353 x2 = x3;
354 }
355 if (f->iter > maxiter) {
356 f->errstring = std::string("reached maximum number of iterations");
357 throw SolutionError(format("Secant reached maximum number of iterations"));
358 }
359 f->iter += 1;
360 }
361 return x3;
362}
363
376double BoundedSecant(FuncWrapper1D* f, double x0, double xmin, double xmax, double dx, double tol, int maxiter) {
377 double x1 = 0, x2 = 0, x3 = 0, y1 = 0, y2 = 0, x, fval = 999;
378 int iter = 1;
379 f->errstring.clear();
380 if (std::abs(dx) == 0) {
381 f->errstring = "dx cannot be zero";
382 return _HUGE;
383 }
384 while (iter <= 3 || std::abs(fval) > tol) {
385 if (iter == 1) {
386 x1 = x0;
387 x = x1;
388 } else if (iter == 2) {
389 x2 = x0 + dx;
390 x = x2;
391 } else {
392 x = x2;
393 }
394 fval = f->call(x);
395 if (iter == 1) {
396 y1 = fval;
397 } else {
398 y2 = fval;
399 x3 = x2 - y2 / (y2 - y1) * (x2 - x1);
400 // Check bounds, go half the way to the limit if limit is exceeded
401 if (x3 < xmin) {
402 x3 = (xmin + x2) / 2;
403 }
404 if (x3 > xmax) {
405 x3 = (xmax + x2) / 2;
406 }
407 y1 = y2;
408 x1 = x2;
409 x2 = x3;
410 }
411 if (iter > maxiter) {
412 f->errstring = "reached maximum number of iterations";
413 throw SolutionError(format("BoundedSecant reached maximum number of iterations"));
414 }
415 iter = iter + 1;
416 }
417 f->errcode = 0;
418 return x3;
419}
420
432double ExtrapolatingSecant(FuncWrapper1D* f, double x0, double dx, double tol, int maxiter)
433{
434 #if defined(COOLPROP_DEEP_DEBUG)
435 static std::vector<double> xlog, flog;
436 xlog.clear(); flog.clear();
437 #endif
438
439 // Initialization
440 double x1=0,x2=0,x3=0,y0=0,y1=0,y2=0,x=x0,fval=999;
441 f->iter=1;
442 f->errstring.clear();
443
444 // The relaxation factor (less than 1 for smaller steps)
445 double omega = f->options.get_double("omega", 1.0);
446
447 if (std::abs(dx)==0){ f->errstring="dx cannot be zero"; return _HUGE;}
448 while (f->iter<=2 || std::abs(fval)>tol)
449 {
450 if (f->iter==1){x1=x0; x=x1;}
451 if (f->iter==2){x2=x0+dx; x=x2;}
452 if (f->iter>2) {x=x2;}
453
454 if (f->input_not_in_range(x)){
455 throw ValueError(format("Input [%g] is out of range",x));
456 }
457
458 fval = f->call(x);
459
460 #if defined(COOLPROP_DEEP_DEBUG)
461 xlog.push_back(x);
462 flog.push_back(fval);
463 #endif
464
465 if (!ValidNumber(fval)){
466 if (f->iter==1){return x;}
467 else {return x2-omega*y1/(y1-y0)*(x2-x1);}
468 };
469 if (f->iter==1){y1=fval;}
470 if (f->iter>1)
471 {
472 double deltax = x2-x1;
473 if (std::abs(deltax)<1e-14){
474 return x;
475 }
476 y2=fval;
477 double deltay = y2-y1;
478 if (f->iter > 2 && std::abs(deltay)<1e-14){
479 return x;
480 }
481 x3=x2-omega*y2/(y2-y1)*(x2-x1);
482 y0=y1;y1=y2; x1=x2; x2=x3;
483
484 }
485 if (f->iter>maxiter)
486 {
487 f->errstring=std::string("reached maximum number of iterations");
488 throw SolutionError(format("Secant reached maximum number of iterations"));
489 }
490 f->iter += 1;
491 }
492 return x3;
493}
494
510double Brent(FuncWrapper1D* f, double a, double b, double macheps, double t, int maxiter) {
511 int iter;
512 f->errstring.clear();
513 double fa, fb, c, fc, m, tol, d, e, p, q, s, r;
514 fa = f->call(a);
515 fb = f->call(b);
516
517 // If one of the boundaries is to within tolerance, just stop
518 if (std::abs(fb) < t) {
519 return b;
520 }
521 if (!ValidNumber(fb)) {
522 throw ValueError(format("Brent's method f(b) is NAN for b = %g, other input was a = %g", b, a).c_str());
523 }
524 if (std::abs(fa) < t) {
525 return a;
526 }
527 if (!ValidNumber(fa)) {
528 throw ValueError(format("Brent's method f(a) is NAN for a = %g, other input was b = %g", a, b).c_str());
529 }
530 if (fa * fb > 0) {
531 throw ValueError(format("Inputs in Brent [%f,%f] do not bracket the root. Function values are [%f,%f]", a, b, fa, fb));
532 }
533
534 c = a;
535 fc = fa;
536 iter = 1;
537 if (std::abs(fc) < std::abs(fb)) {
538 // Goto ext: from Brent ALGOL code
539 a = b;
540 b = c;
541 c = a;
542 fa = fb;
543 fb = fc;
544 fc = fa;
545 }
546 d = b - a;
547 e = b - a;
548 m = 0.5 * (c - b);
549 tol = 2 * macheps * std::abs(b) + t;
550 while (std::abs(m) > tol && fb != 0) {
551 // See if a bisection is forced
552 if (std::abs(e) < tol || std::abs(fa) <= std::abs(fb)) {
553 m = 0.5 * (c - b);
554 d = e = m;
555 } else {
556 s = fb / fa;
557 if (a == c) {
558 //Linear interpolation
559 p = 2 * m * s;
560 q = 1 - s;
561 } else {
562 //Inverse quadratic interpolation
563 q = fa / fc;
564 r = fb / fc;
565 m = 0.5 * (c - b);
566 p = s * (2 * m * q * (q - r) - (b - a) * (r - 1));
567 q = (q - 1) * (r - 1) * (s - 1);
568 }
569 if (p > 0) {
570 q = -q;
571 } else {
572 p = -p;
573 }
574 s = e;
575 e = d;
576 m = 0.5 * (c - b);
577 if (2 * p < 3 * m * q - std::abs(tol * q) || p < std::abs(0.5 * s * q)) {
578 d = p / q;
579 } else {
580 m = 0.5 * (c - b);
581 d = e = m;
582 }
583 }
584 a = b;
585 fa = fb;
586 if (std::abs(d) > tol) {
587 b += d;
588 } else if (m > 0) {
589 b += tol;
590 } else {
591 b += -tol;
592 }
593 fb = f->call(b);
594 if (!ValidNumber(fb)) {
595 throw ValueError(format("Brent's method f(t) is NAN for t = %g", b).c_str());
596 }
597 if (std::abs(fb) < macheps) {
598 return b;
599 }
600 if (fb * fc > 0) {
601 // Goto int: from Brent ALGOL code
602 c = a;
603 fc = fa;
604 d = e = b - a;
605 }
606 if (std::abs(fc) < std::abs(fb)) {
607 // Goto ext: from Brent ALGOL code
608 a = b;
609 b = c;
610 c = a;
611 fa = fb;
612 fb = fc;
613 fc = fa;
614 }
615 m = 0.5 * (c - b);
616 tol = 2 * macheps * std::abs(b) + t;
617 iter += 1;
618 if (!ValidNumber(a)) {
619 throw ValueError(format("Brent's method a is NAN").c_str());
620 }
621 if (!ValidNumber(b)) {
622 throw ValueError(format("Brent's method b is NAN").c_str());
623 }
624 if (!ValidNumber(c)) {
625 throw ValueError(format("Brent's method c is NAN").c_str());
626 }
627 if (iter > maxiter) {
628 throw SolutionError(format("Brent's method reached maximum number of steps of %d ", maxiter));
629 }
630 if (std::abs(fb) < 2 * macheps * std::abs(b)) {
631 return b;
632 }
633 }
634 return b;
635}
636
637}; /* namespace CoolProp */