CoolProp 7.2.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 if (std::abs(dx / x) < xtol_rel) {
200 return x;
201 }
202
203 if (f->iter > maxiter) {
204 f->errstring = "Halley reached maximum number of iterations";
205 throw SolutionError(format("Halley reached maximum number of iterations"));
206 }
207 f->iter += 1;
208 }
209 return x;
210}
211
228double Householder4(FuncWrapper1DWithThreeDerivs* f, double x0, double ftol, int maxiter, double xtol_rel) {
229 double x, dx, fval = 999, dfdx, d2fdx2, d3fdx3;
230
231 // Initialization
232 f->iter = 1;
233 f->errstring.clear();
234 x = x0;
235
236 // The relaxation factor (less than 1 for smaller steps)
237 double omega = f->options.get_double("omega", 1.0);
238
239 while (f->iter < 2 || std::abs(fval) > ftol) {
240 if (f->input_not_in_range(x)) {
241 throw ValueError(format("Input [%g] is out of range", x));
242 }
243
244 fval = f->call(x);
245 dfdx = f->deriv(x);
246 d2fdx2 = f->second_deriv(x);
247 d3fdx3 = f->third_deriv(x);
248
249 if (!ValidNumber(fval)) {
250 throw ValueError("Residual function in Householder4 returned invalid number");
251 };
252 if (!ValidNumber(dfdx)) {
253 throw ValueError("Derivative function in Householder4 returned invalid number");
254 };
255 if (!ValidNumber(d2fdx2)) {
256 throw ValueError("Second derivative function in Householder4 returned invalid number");
257 };
258 if (!ValidNumber(d3fdx3)) {
259 throw ValueError("Third derivative function in Householder4 returned invalid number");
260 };
261
262 dx = -omega * fval * (POW2(dfdx) - fval * d2fdx2 / 2.0) / (POW3(dfdx) - fval * dfdx * d2fdx2 + d3fdx3 * POW2(fval) / 6.0);
263
264 x += dx;
265
266 if (std::abs(dx / x) < xtol_rel) {
267 return x;
268 }
269
270 if (f->iter > maxiter) {
271 f->errstring = "reached maximum number of iterations";
272 throw SolutionError(format("Householder4 reached maximum number of iterations"));
273 }
274 f->iter += 1;
275 }
276 return x;
277}
278
289double Secant(FuncWrapper1D* f, double x0, double dx, double tol, int maxiter) {
290#if defined(COOLPROP_DEEP_DEBUG)
291 static std::vector<double> xlog, flog;
292 xlog.clear();
293 flog.clear();
294#endif
295
296 // Initialization
297 double x1 = 0, x2 = 0, x3 = 0, y1 = 0, y2 = 0, x = x0, fval = 999;
298 f->iter = 1;
299 f->errstring.clear();
300
301 // The relaxation factor (less than 1 for smaller steps)
302 double omega = f->options.get_double("omega", 1.0);
303
304 if (std::abs(dx) == 0) {
305 f->errstring = "dx cannot be zero";
306 return _HUGE;
307 }
308 while (f->iter <= 2 || std::abs(fval) > tol) {
309 if (f->iter == 1) {
310 x1 = x0;
311 x = x1;
312 }
313 if (f->iter == 2) {
314 x2 = x0 + dx;
315 x = x2;
316 }
317 if (f->iter > 2) {
318 x = x2;
319 }
320
321 if (f->input_not_in_range(x)) {
322 throw ValueError(format("Input [%g] is out of range", x));
323 }
324
325 fval = f->call(x);
326
327#if defined(COOLPROP_DEEP_DEBUG)
328 xlog.push_back(x);
329 flog.push_back(fval);
330#endif
331
332 if (!ValidNumber(fval)) {
333 throw ValueError("Residual function in secant returned invalid number");
334 };
335 if (f->iter == 1) {
336 y1 = fval;
337 }
338 if (f->iter > 1) {
339 double deltax = x2 - x1;
340 if (std::abs(deltax) < 1e-14) {
341 return x;
342 }
343 y2 = fval;
344 double deltay = y2 - y1;
345 if (f->iter > 2 && std::abs(deltay) < 1e-14) {
346 return x;
347 }
348 x3 = x2 - omega * y2 / (y2 - y1) * (x2 - x1);
349 y1 = y2;
350 x1 = x2;
351 x2 = x3;
352 }
353 if (f->iter > maxiter) {
354 f->errstring = std::string("reached maximum number of iterations");
355 throw SolutionError(format("Secant reached maximum number of iterations"));
356 }
357 f->iter += 1;
358 }
359 return x3;
360}
361
374double BoundedSecant(FuncWrapper1D* f, double x0, double xmin, double xmax, double dx, double tol, int maxiter) {
375 double x1 = 0, x2 = 0, x3 = 0, y1 = 0, y2 = 0, x, fval = 999;
376 int iter = 1;
377 f->errstring.clear();
378 if (std::abs(dx) == 0) {
379 f->errstring = "dx cannot be zero";
380 return _HUGE;
381 }
382 while (iter <= 3 || std::abs(fval) > tol) {
383 if (iter == 1) {
384 x1 = x0;
385 x = x1;
386 } else if (iter == 2) {
387 x2 = x0 + dx;
388 x = x2;
389 } else {
390 x = x2;
391 }
392 fval = f->call(x);
393 if (iter == 1) {
394 y1 = fval;
395 } else {
396 y2 = fval;
397 x3 = x2 - y2 / (y2 - y1) * (x2 - x1);
398 // Check bounds, go half the way to the limit if limit is exceeded
399 if (x3 < xmin) {
400 x3 = (xmin + x2) / 2;
401 }
402 if (x3 > xmax) {
403 x3 = (xmax + x2) / 2;
404 }
405 y1 = y2;
406 x1 = x2;
407 x2 = x3;
408 }
409 if (iter > maxiter) {
410 f->errstring = "reached maximum number of iterations";
411 throw SolutionError(format("BoundedSecant reached maximum number of iterations"));
412 }
413 iter = iter + 1;
414 }
415 f->errcode = 0;
416 return x3;
417}
418
430double ExtrapolatingSecant(FuncWrapper1D* f, double x0, double dx, double tol, int maxiter) {
431#if defined(COOLPROP_DEEP_DEBUG)
432 static std::vector<double> xlog, flog;
433 xlog.clear();
434 flog.clear();
435#endif
436
437 // Initialization
438 double x1 = 0, x2 = 0, x3 = 0, y0 = 0, y1 = 0, y2 = 0, x = x0, fval = 999;
439 f->iter = 1;
440 f->errstring.clear();
441
442 // The relaxation factor (less than 1 for smaller steps)
443 double omega = f->options.get_double("omega", 1.0);
444
445 if (std::abs(dx) == 0) {
446 f->errstring = "dx cannot be zero";
447 return _HUGE;
448 }
449 while (f->iter <= 2 || std::abs(fval) > tol) {
450 if (f->iter == 1) {
451 x1 = x0;
452 x = x1;
453 }
454 if (f->iter == 2) {
455 x2 = x0 + dx;
456 x = x2;
457 }
458 if (f->iter > 2) {
459 x = x2;
460 }
461
462 if (f->input_not_in_range(x)) {
463 throw ValueError(format("Input [%g] is out of range", x));
464 }
465
466 fval = f->call(x);
467
468#if defined(COOLPROP_DEEP_DEBUG)
469 xlog.push_back(x);
470 flog.push_back(fval);
471#endif
472
473 if (!ValidNumber(fval)) {
474 if (f->iter == 1) {
475 return x;
476 } else {
477 return x2 - omega * y1 / (y1 - y0) * (x2 - x1);
478 }
479 };
480 if (f->iter == 1) {
481 y1 = fval;
482 }
483 if (f->iter > 1) {
484 double deltax = x2 - x1;
485 if (std::abs(deltax) < 1e-14) {
486 return x;
487 }
488 y2 = fval;
489 double deltay = y2 - y1;
490 if (f->iter > 2 && std::abs(deltay) < 1e-14) {
491 return x;
492 }
493 x3 = x2 - omega * y2 / (y2 - y1) * (x2 - x1);
494 y0 = y1;
495 y1 = y2;
496 x1 = x2;
497 x2 = x3;
498 }
499 if (f->iter > maxiter) {
500 f->errstring = std::string("reached maximum number of iterations");
501 throw SolutionError(format("Secant reached maximum number of iterations"));
502 }
503 f->iter += 1;
504 }
505 return x3;
506}
507
523double Brent(FuncWrapper1D* f, double a, double b, double macheps, double t, int maxiter) {
524 int iter;
525 f->errstring.clear();
526 double fa, fb, c, fc, m, tol, d, e, p, q, s, r;
527 fa = f->call(a);
528 fb = f->call(b);
529
530 // If one of the boundaries is to within tolerance, just stop
531 if (std::abs(fb) < t) {
532 return b;
533 }
534 if (!ValidNumber(fb)) {
535 throw ValueError(format("Brent's method f(b) is NAN for b = %g, other input was a = %g", b, a).c_str());
536 }
537 if (std::abs(fa) < t) {
538 return a;
539 }
540 if (!ValidNumber(fa)) {
541 throw ValueError(format("Brent's method f(a) is NAN for a = %g, other input was b = %g", a, b).c_str());
542 }
543 if (fa * fb > 0) {
544 throw ValueError(format("Inputs in Brent [%f,%f] do not bracket the root. Function values are [%f,%f]", a, b, fa, fb));
545 }
546
547 c = a;
548 fc = fa;
549 iter = 1;
550 if (std::abs(fc) < std::abs(fb)) {
551 // Goto ext: from Brent ALGOL code
552 a = b;
553 b = c;
554 c = a;
555 fa = fb;
556 fb = fc;
557 fc = fa;
558 }
559 d = b - a;
560 e = b - a;
561 m = 0.5 * (c - b);
562 tol = 2 * macheps * std::abs(b) + t;
563 while (std::abs(m) > tol && fb != 0) {
564 // See if a bisection is forced
565 if (std::abs(e) < tol || std::abs(fa) <= std::abs(fb)) {
566 m = 0.5 * (c - b);
567 d = e = m;
568 } else {
569 s = fb / fa;
570 if (a == c) {
571 //Linear interpolation
572 p = 2 * m * s;
573 q = 1 - s;
574 } else {
575 //Inverse quadratic interpolation
576 q = fa / fc;
577 r = fb / fc;
578 m = 0.5 * (c - b);
579 p = s * (2 * m * q * (q - r) - (b - a) * (r - 1));
580 q = (q - 1) * (r - 1) * (s - 1);
581 }
582 if (p > 0) {
583 q = -q;
584 } else {
585 p = -p;
586 }
587 s = e;
588 e = d;
589 m = 0.5 * (c - b);
590 if (2 * p < 3 * m * q - std::abs(tol * q) || p < std::abs(0.5 * s * q)) {
591 d = p / q;
592 } else {
593 m = 0.5 * (c - b);
594 d = e = m;
595 }
596 }
597 a = b;
598 fa = fb;
599 if (std::abs(d) > tol) {
600 b += d;
601 } else if (m > 0) {
602 b += tol;
603 } else {
604 b += -tol;
605 }
606 fb = f->call(b);
607 if (!ValidNumber(fb)) {
608 throw ValueError(format("Brent's method f(t) is NAN for t = %g", b).c_str());
609 }
610 if (std::abs(fb) < macheps) {
611 return b;
612 }
613 if (fb * fc > 0) {
614 // Goto int: from Brent ALGOL code
615 c = a;
616 fc = fa;
617 d = e = b - a;
618 }
619 if (std::abs(fc) < std::abs(fb)) {
620 // Goto ext: from Brent ALGOL code
621 a = b;
622 b = c;
623 c = a;
624 fa = fb;
625 fb = fc;
626 fc = fa;
627 }
628 m = 0.5 * (c - b);
629 tol = 2 * macheps * std::abs(b) + t;
630 iter += 1;
631 if (!ValidNumber(a)) {
632 throw ValueError(format("Brent's method a is NAN").c_str());
633 }
634 if (!ValidNumber(b)) {
635 throw ValueError(format("Brent's method b is NAN").c_str());
636 }
637 if (!ValidNumber(c)) {
638 throw ValueError(format("Brent's method c is NAN").c_str());
639 }
640 if (iter > maxiter) {
641 throw SolutionError(format("Brent's method reached maximum number of steps of %d ", maxiter));
642 }
643 if (std::abs(fb) < 2 * macheps * std::abs(b)) {
644 return b;
645 }
646 }
647 return b;
648}
649
650}; /* namespace CoolProp */