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