CoolProp 8.0.0
An open-source fluid property and humid air property database
numerics.h
Go to the documentation of this file.
1#ifndef COOLPROP_NUMERICS_H
2#define COOLPROP_NUMERICS_H
3
4#include <vector>
5#include <set>
6#include <cfloat>
7#include <stdlib.h> // For abs
8#include <algorithm> // For max
9#include <numeric>
10#include <cmath>
11#include <array>
14#include "CoolProp/Exceptions.h"
15#include <iostream> // for std::cerr in Spline
16
17#if defined(HUGE_VAL) && !defined(_HUGE)
18# define _HUGE HUGE_VAL
19#else
20// GCC Version of huge value macro
21# if defined(HUGE) && !defined(_HUGE)
22# define _HUGE HUGE
23# endif
24#endif
25
26template <typename T, size_t N>
27std::array<T, N> create_filled_array(T value) {
28 std::array<T, N> arr;
29 arr.fill(value);
30 return arr;
31}
32
33inline bool ValidNumber(double x) {
34 // Idea from http://www.johndcook.com/IEEE_exceptions_in_cpp.html
35 return (x <= DBL_MAX && x >= -DBL_MAX);
36};
37
38#ifndef M_PI
39# define M_PI 3.14159265358979323846
40#endif
41
42#ifndef COOLPROP_OK
43# define COOLPROP_OK 1
44#endif
45
46// Undefine these terrible macros defined in windows header
47#undef min
48#undef max
49
50/* "THE BEER-WARE LICENSE" (Revision 42): Devin Lane wrote this file. As long as you retain
51 * this notice you can do whatever you want with this stuff. If we meet some day, and you
52 * think this stuff is worth it, you can buy me a beer in return.
53 *
54 * From http://shiftedbits.org/2011/01/30/cubic-spline-interpolation/
55 *
56 * IHB(05/01/2016): Removed overload and renamed the interpolate function (cython cannot disambiguate the functions)
57 *
58 * Templated on type of X, Y. X and Y must have operator +, -, *, /. Y must have defined
59 * a constructor that takes a scalar. */
60template <typename X, typename Y>
61class Spline
62{
63 public:
65 Spline() = default;
66
68 Spline(const std::vector<X>& x, const std::vector<Y>& y) {
69 if (x.size() != y.size()) {
70 std::cerr << "X and Y must be the same size " << '\n';
71 return;
72 }
73
74 if (x.size() < 3) {
75 std::cerr << "Must have at least three points for interpolation" << '\n';
76 return;
77 }
78
79 using size_type = typename std::vector<X>::difference_type;
80
81 size_type n = y.size() - 1;
82
83 std::vector<Y> b(n), d(n), a(n), c(n + 1), l(n + 1), u(n + 1), z(n + 1);
84 std::vector<X> h(n + 1);
85
86 l[0] = Y(1);
87 u[0] = Y(0);
88 z[0] = Y(0);
89 h[0] = x[1] - x[0];
90
91 for (size_type i = 1; i < n; i++) {
92 h[i] = x[i + 1] - x[i];
93 l[i] = Y(2 * (x[i + 1] - x[i - 1])) - Y(h[i - 1]) * u[i - 1];
94 u[i] = Y(h[i]) / l[i];
95 a[i] = (Y(3) / Y(h[i])) * (y[i + 1] - y[i]) - (Y(3) / Y(h[i - 1])) * (y[i] - y[i - 1]);
96 z[i] = (a[i] - Y(h[i - 1]) * z[i - 1]) / l[i];
97 }
98
99 l[n] = Y(1);
100 z[n] = c[n] = Y(0);
101
102 for (size_type j = n - 1; j >= 0; j--) {
103 c[j] = z[j] - u[j] * c[j + 1];
104 b[j] = (y[j + 1] - y[j]) / Y(h[j]) - (Y(h[j]) * (c[j + 1] + Y(2) * c[j])) / Y(3);
105 d[j] = (c[j + 1] - c[j]) / Y(3 * h[j]);
106 }
107
108 for (size_type i = 0; i < n; i++) {
109 mElements.push_back(Element(x[i], y[i], b[i], c[i], d[i]));
110 }
111 }
112 virtual ~Spline() = default;
113
114 Y operator[](const X& x) const {
115 return interpolate(x);
116 }
117
118 Y interpolate(const X& x) const {
119 if (mElements.size() == 0) return Y();
120
121 typename std::vector<element_type>::const_iterator it;
122 it = std::lower_bound(mElements.begin(), mElements.end(), element_type(x));
123 if (it != mElements.begin()) {
124 it--;
125 }
126
127 return it->eval(x);
128 }
129
130 /* Evaluate at multiple locations, assuming xx is sorted ascending */
131 std::vector<Y> interpolate_vec(const std::vector<X>& xx) const {
132 if (mElements.size() == 0) return std::vector<Y>(xx.size());
133
134 typename std::vector<X>::const_iterator it;
135 typename std::vector<element_type>::const_iterator it2;
136 it2 = mElements.begin();
137 std::vector<Y> ys;
138 for (it = xx.begin(); it != xx.end(); it++) {
139 it2 = std::lower_bound(it2, mElements.end(), element_type(*it));
140 if (it2 != mElements.begin()) {
141 it2--;
142 }
143
144 ys.push_back(it2->eval(*it));
145 }
146
147 return ys;
148 }
149
150 protected:
152 {
153 public:
154 Element(X _x) : x(_x) {}
155 Element(X _x, Y _a, Y _b, Y _c, Y _d) : x(_x), a(_a), b(_b), c(_c), d(_d) {}
156
157 Y eval(const X& xx) const {
158 X xix(xx - x);
159 return a + b * xix + c * (xix * xix) + d * (xix * xix * xix);
160 }
161
162 bool operator<(const Element& e) const {
163 return x < e.x;
164 }
165 bool operator<(const X& xx) const {
166 return x < xx;
167 }
168
170 Y a, b, c, d;
171 };
172
174 std::vector<element_type> mElements;
175};
176
178template <typename T>
179T maxvectordiff(const std::vector<T>& z1, const std::vector<T>& z2) {
180 T maxvecdiff = 0;
181 for (std::size_t i = 0; i < z1.size(); ++i) {
182 T diff = std::abs(z1[i] - z2[i]);
183 if (std::abs(diff) > maxvecdiff) {
184 maxvecdiff = diff;
185 }
186 }
187 return maxvecdiff;
188}
189
191template <typename T>
192std::vector<T> linspace(T xmin, T xmax, std::size_t n) {
193 std::vector<T> x(n, 0.0);
194
195 for (std::size_t i = 0; i < n; ++i) {
196 x[i] = (xmax - xmin) / (n - 1) * i + xmin;
197 }
198 return x;
199}
201template <typename T>
202std::vector<T> log10space(T xmin, T xmax, std::size_t n) {
203 std::vector<T> x(n, 0.0);
204 T logxmin = log10(xmin), logxmax = log10(xmax);
205
206 for (std::size_t i = 0; i < n; ++i) {
207 x[i] = exp((logxmax - logxmin) / (n - 1) * i + logxmin);
208 }
209 return x;
210}
212template <typename T>
213std::vector<T> logspace(T xmin, T xmax, std::size_t n) {
214 std::vector<T> x(n, 0.0);
215 T logxmin = log(xmin), logxmax = log(xmax);
216
217 for (std::size_t i = 0; i < n; ++i) {
218 x[i] = exp((logxmax - logxmin) / (n - 1) * i + logxmin);
219 }
220 return x;
221}
222
231template <typename T>
232void bisect_vector(const std::vector<T>& vec, T val, std::size_t& i) {
233 T rL, rM, rR;
234 std::size_t N = vec.size(), L = 0, R = N - 1, M = (L + R) / 2;
235 // Move the right limits in until they are good
236 while (!ValidNumber(vec[R])) {
237 if (R == 1) {
238 throw CoolProp::ValueError("All the values in bisection vector are invalid");
239 }
240 R--;
241 }
242 // Move the left limits in until they are good
243 while (!ValidNumber(vec[L])) {
244 if (L == vec.size() - 1) {
245 throw CoolProp::ValueError("All the values in bisection vector are invalid");
246 }
247 L++;
248 }
249 rL = vec[L] - val;
250 rR = vec[R] - val;
251 while (R - L > 1) {
252 if (!ValidNumber(vec[M])) {
253 std::size_t MR = M, ML = M;
254 // Move middle-right to the right until it is ok
255 while (!ValidNumber(vec[MR])) {
256 if (MR == vec.size() - 1) {
257 throw CoolProp::ValueError("All the values in bisection vector are invalid");
258 }
259 MR++;
260 }
261 // Move middle-left to the left until it is ok
262 while (!ValidNumber(vec[ML])) {
263 if (ML == 1) {
264 throw CoolProp::ValueError("All the values in bisection vector are invalid");
265 }
266 ML--;
267 }
268 T rML = vec[ML] - val;
269 T rMR = vec[MR] - val;
270 // Figure out which chunk is the good part
271 if (rR * rML > 0 && rL * rML < 0) {
272 // solution is between L and ML
273 R = ML;
274 rR = vec[ML] - val;
275 } else if (rR * rMR < 0 && rL * rMR > 0) {
276 // solution is between R and MR
277 L = MR;
278 rL = vec[MR] - val;
279 } else {
281 format("Unable to bisect segmented vector; neither chunk contains the solution val:%g left:(%g,%g) right:(%g,%g)", val, vec[L],
282 vec[ML], vec[MR], vec[R]));
283 }
284 M = (L + R) / 2;
285 } else {
286 rM = vec[M] - val;
287 if (rR * rM > 0 && rL * rM < 0) {
288 // solution is between L and M
289 R = M;
290 rR = vec[R] - val;
291 } else {
292 // solution is between R and M
293 L = M;
294 rL = vec[L] - val;
295 }
296 M = (L + R) / 2;
297 }
298 }
299 i = L;
300}
301
311template <typename T>
312void bisect_segmented_vector_slice(const std::vector<std::vector<T>>& mat, std::size_t j, T val, std::size_t& i) {
313 T rL, rM, rR;
314 std::size_t N = mat[j].size(), L = 0, R = N - 1, M = (L + R) / 2;
315 // Move the right limits in until they are good
316 while (!ValidNumber(mat[R][j])) {
317 if (R == 1) {
318 throw CoolProp::ValueError("All the values in bisection vector are invalid");
319 }
320 R--;
321 }
322 rR = mat[R][j] - val;
323 // Move the left limits in until they are good
324 while (!ValidNumber(mat[L][j])) {
325 if (L == mat.size() - 1) {
326 throw CoolProp::ValueError("All the values in bisection vector are invalid");
327 }
328 L++;
329 }
330 rL = mat[L][j] - val;
331 while (R - L > 1) {
332 if (!ValidNumber(mat[M][j])) {
333 std::size_t MR = M, ML = M;
334 // Move middle-right to the right until it is ok
335 while (!ValidNumber(mat[MR][j])) {
336 if (MR == mat.size() - 1) {
337 throw CoolProp::ValueError("All the values in bisection vector are invalid");
338 }
339 MR++;
340 }
341 // Move middle-left to the left until it is ok
342 while (!ValidNumber(mat[ML][j])) {
343 if (ML == 1) {
344 throw CoolProp::ValueError("All the values in bisection vector are invalid");
345 }
346 ML--;
347 }
348 T rML = mat[ML][j] - val;
349 T rMR = mat[MR][j] - val;
350 // Figure out which chunk is the good part
351 if (rR * rMR > 0 && rL * rML < 0) {
352 // solution is between L and ML
353 R = ML;
354 rR = mat[ML][j] - val;
355 } else if (rR * rMR < 0 && rL * rML > 0) {
356 // solution is between R and MR
357 L = MR;
358 rL = mat[MR][j] - val;
359 } else {
361 format("Unable to bisect segmented vector slice; neither chunk contains the solution %g lef:(%g,%g) right:(%g,%g)", val, mat[L][j],
362 mat[ML][j], mat[MR][j], mat[R][j]));
363 }
364 M = (L + R) / 2;
365 } else {
366 rM = mat[M][j] - val;
367 if (rR * rM > 0 && rL * rM < 0) {
368 // solution is between L and M
369 R = M;
370 rR = mat[R][j] - val;
371 } else {
372 // solution is between R and M
373 L = M;
374 rL = mat[L][j] - val;
375 }
376 M = (L + R) / 2;
377 }
378 }
379 i = L;
380}
381
382// From http://rosettacode.org/wiki/Power_set#C.2B.2B
383inline std::size_t powerset_dereference(std::set<std::size_t>::const_iterator v) {
384 return *v;
385};
386
387// From http://rosettacode.org/wiki/Power_set#C.2B.2B
388inline std::set<std::set<std::size_t>> powerset(std::set<std::size_t> const& set) {
389 std::set<std::set<std::size_t>> result;
390 std::vector<std::set<std::size_t>::const_iterator> elements;
391 do {
392 std::set<std::size_t> tmp;
393 std::transform(elements.begin(), elements.end(), std::inserter(tmp, tmp.end()), powerset_dereference);
394 result.insert(tmp);
395 if (!elements.empty() && ++elements.back() == set.end()) {
396 elements.pop_back();
397 } else {
398 std::set<std::size_t>::const_iterator iter;
399 if (elements.empty()) {
400 iter = set.begin();
401 } else {
402 iter = elements.back();
403 ++iter;
404 }
405 for (; iter != set.end(); ++iter) {
406 elements.push_back(iter);
407 }
408 }
409 } while (!elements.empty());
410
411 return result;
412}
413
415bool inline check_abs(double A, double B, double D) {
416 double max = std::abs(A);
417 double min = std::abs(B);
418 if (min > max) {
419 max = min;
420 min = std::abs(A);
421 }
422 if (max > DBL_EPSILON * 1e3)
423 return ((1.0 - min / max * 1e0) < D);
424 else
426 format("Too small numbers: %f cannot be tested with an accepted error of %f for a machine precision of %f. ", max, D, DBL_EPSILON));
427};
428bool inline check_abs(double A, double B) {
429 return check_abs(A, B, 1e5 * DBL_EPSILON);
430};
431
432template <class T>
433void normalize_vector(std::vector<T>& x) {
434 // Sum up all the elements in the vector
435 T sumx = std::accumulate(x.begin(), x.end(), static_cast<T>(0));
436 // Normalize the components by dividing each by the sum
437 for (std::size_t i = 0; i < x.size(); ++i) {
438 x[i] /= sumx;
439 }
440};
441
446{
447 protected:
449 std::vector<std::vector<double>> A;
450 std::vector<double> B;
451
452 public:
453 double a, b, c, d;
454 SplineClass() : Nconstraints(0), A(4, std::vector<double>(4, 0)), B(4, 0), a(_HUGE), b(_HUGE), c(_HUGE), d(_HUGE) {}
455 bool build();
456 bool add_value_constraint(double x, double y);
457 void add_4value_constraints(double x1, double x2, double x3, double x4, double y1, double y2, double y3, double y4);
458 bool add_derivative_constraint(double x, double dydx);
459 double evaluate(double x);
460};
461
463template <class T>
465 if (n == 0) return 1;
466 return n * factorial(n - 1);
467}
470template <class T1, class T2>
471T1 nth_derivative_of_x_to_m(T1 x, T2 n, T2 m) {
472 if (n > m) {
473 return 0;
474 } else {
475 return factorial(m) / factorial(m - n) * pow(x, m - n);
476 }
477}
478
479void MatInv_2(double A[2][2], double B[2][2]);
480
481double root_sum_square(const std::vector<double>& x);
482double interp1d(const std::vector<double>* x, const std::vector<double>* y, double x0);
483double powInt(double x, int y);
484
485template <class T>
486T POW2(const T& x) {
487 return x * x;
488}
489template <class T>
490T POW3(T x) {
491 return POW2(x) * x;
492}
493template <class T>
494T POW4(T x) {
495 return POW2(x) * POW2(x);
496}
497#define POW5(x) ((x) * (x) * (x) * (x) * (x))
498#define POW6(x) ((x) * (x) * (x) * (x) * (x) * (x))
499#define POW7(x) ((x) * (x) * (x) * (x) * (x) * (x) * (x))
500
501template <class T>
502T LinearInterp(T x0, T x1, T y0, T y1, T x) {
503 return (y1 - y0) / (x1 - x0) * (x - x0) + y0;
504};
505template <class T1, class T2>
506T2 LinearInterp(const std::vector<T1>& x, const std::vector<T1>& y, std::size_t i0, std::size_t i1, T2 val) {
507 return LinearInterp(x[i0], x[i1], y[i0], y[i1], static_cast<T1>(val));
508};
509
510template <class T>
511T QuadInterp(T x0, T x1, T x2, T f0, T f1, T f2, T x) {
512 /* Quadratic interpolation.
513 Based on method from Kreyszig,
514 Advanced Engineering Mathematics, 9th Edition
515 */
516 T L0, L1, L2;
517 L0 = ((x - x1) * (x - x2)) / ((x0 - x1) * (x0 - x2));
518 L1 = ((x - x0) * (x - x2)) / ((x1 - x0) * (x1 - x2));
519 L2 = ((x - x0) * (x - x1)) / ((x2 - x0) * (x2 - x1));
520 return L0 * f0 + L1 * f1 + L2 * f2;
521};
522template <class T1, class T2>
523T2 QuadInterp(const std::vector<T1>& x, const std::vector<T1>& y, std::size_t i0, std::size_t i1, std::size_t i2, T2 val) {
524 return QuadInterp(x[i0], x[i1], x[i2], y[i0], y[i1], y[i2], static_cast<T1>(val));
525};
526
527template <class T>
528T CubicInterp(T x0, T x1, T x2, T x3, T f0, T f1, T f2, T f3, T x) {
529 /*
530 Lagrange cubic interpolation as from
531 http://nd.edu/~jjwteach/441/PdfNotes/lecture6.pdf
532 */
533 T L0, L1, L2, L3;
534 L0 = ((x - x1) * (x - x2) * (x - x3)) / ((x0 - x1) * (x0 - x2) * (x0 - x3));
535 L1 = ((x - x0) * (x - x2) * (x - x3)) / ((x1 - x0) * (x1 - x2) * (x1 - x3));
536 L2 = ((x - x0) * (x - x1) * (x - x3)) / ((x2 - x0) * (x2 - x1) * (x2 - x3));
537 L3 = ((x - x0) * (x - x1) * (x - x2)) / ((x3 - x0) * (x3 - x1) * (x3 - x2));
538 return L0 * f0 + L1 * f1 + L2 * f2 + L3 * f3;
539};
542template <class T>
543T CubicInterpFirstDeriv(T x0, T x1, T x2, T x3, T f0, T f1, T f2, T f3, T x) {
544 // Based on http://math.stackexchange.com/a/809946/66405
545 T L0 = ((x - x1) * (x - x2) * (x - x3)) / ((x0 - x1) * (x0 - x2) * (x0 - x3));
546 T dL0_dx = (1 / (x - x1) + 1 / (x - x2) + 1 / (x - x3)) * L0;
547 T L1 = ((x - x0) * (x - x2) * (x - x3)) / ((x1 - x0) * (x1 - x2) * (x1 - x3));
548 T dL1_dx = (1 / (x - x0) + 1 / (x - x2) + 1 / (x - x3)) * L1;
549 T L2 = ((x - x0) * (x - x1) * (x - x3)) / ((x2 - x0) * (x2 - x1) * (x2 - x3));
550 T dL2_dx = (1 / (x - x0) + 1 / (x - x1) + 1 / (x - x3)) * L2;
551 T L3 = ((x - x0) * (x - x1) * (x - x2)) / ((x3 - x0) * (x3 - x1) * (x3 - x2));
552 T dL3_dx = (1 / (x - x0) + 1 / (x - x1) + 1 / (x - x2)) * L3;
553 return dL0_dx * f0 + dL1_dx * f1 + dL2_dx * f2 + dL3_dx * f3;
554};
555template <class T1, class T2>
556T2 CubicInterp(const std::vector<T1>& x, const std::vector<T1>& y, std::size_t i0, std::size_t i1, std::size_t i2, std::size_t i3, T2 val) {
557 return CubicInterp(x[i0], x[i1], x[i2], x[i3], y[i0], y[i1], y[i2], y[i3], static_cast<T1>(val));
558};
559
560template <class T>
561bool is_in_closed_range(T x1, T x2, T x) {
562 return (x >= std::min(x1, x2) && x <= std::max(x1, x2));
563};
564
578void solve_cubic(double a, double b, double c, double d, int& N, double& x0, double& x1, double& x2);
579
580void solve_quartic(double a, double b, double c, double d, double e, int& N, double& x0, double& x1, double& x2, double& x3);
581
582template <class T>
583inline T min3(T x1, T x2, T x3) {
584 return std::min(std::min(x1, x2), x3);
585};
586template <class T>
587inline T max3(T x1, T x2, T x3) {
588 return std::max(std::max(x1, x2), x3);
589};
590template <class T>
591inline T min4(T x1, T x2, T x3, T x4) {
592 return std::min(std::min(std::min(x1, x2), x3), x4);
593};
594template <class T>
595inline T max4(T x1, T x2, T x3, T x4) {
596 return std::max(std::max(std::max(x1, x2), x3), x4);
597};
598
599inline bool double_equal(double a, double b) {
600 return std::abs(a - b) <= 1 * DBL_EPSILON * std::max(std::abs(a), std::abs(b));
601};
602
603template <class T>
604T max_abs_value(const std::vector<T>& x) {
605 T max = 0;
606 std::size_t N = x.size();
607 for (std::size_t i = 0; i < N; ++i) {
608 T axi = std::abs(x[i]);
609 if (axi > max) {
610 max = axi;
611 }
612 }
613 return max;
614}
615
616template <class T>
617T min_abs_value(const std::vector<T>& x) {
618 T min = 1e40;
619 std::size_t N = x.size();
620 for (std::size_t i = 0; i < N; ++i) {
621 T axi = std::abs(x[i]);
622 if (axi < min) {
623 min = axi;
624 }
625 }
626 return min;
627}
628
629inline int Kronecker_delta(std::size_t i, std::size_t j) {
630 if (i == j) {
631 return static_cast<int>(1);
632 } else {
633 return static_cast<int>(0);
634 }
635};
636inline int Kronecker_delta(int i, int j) {
637 if (i == j) {
638 return 1;
639 } else {
640 return 0;
641 }
642};
643
645template <typename T>
646void sort3(T& a, T& b, T& c) {
647 if (a > b) {
648 std::swap(a, b);
649 }
650 if (a > c) {
651 std::swap(a, c);
652 }
653 if (b > c) {
654 std::swap(b, c);
655 }
656}
657
668template <class T>
669T angle_difference(T angle1, T angle2) {
670 return fmod(angle1 - angle2 + M_PI, 2 * M_PI) - M_PI;
671}
672
674inline double get_HUGE() {
675 return _HUGE;
676}
677
678#if defined(_MSC_VER)
679// Microsoft version of math.h doesn't include acosh or asinh, so we just define them here.
680// It was included from Visual Studio 2013
681# if _MSC_VER < 1800
682double acosh(double x) {
683 return log(x + sqrt(x * x - 1.0));
684}
685double asinh(double value) {
686 if (value > 0) {
687 return log(value + sqrt(value * value + 1));
688 } else {
689 return -log(-value + sqrt(value * value + 1));
690 }
691}
692# endif
693#endif
694
695#if defined(__ISPOWERPC__)
696// PPC version of math.h doesn't include acosh or asinh, so we just define them here
697double acosh(double x) {
698 return log(x + sqrt(x * x - 1.0));
699}
700double asinh(double value) {
701 if (value > 0) {
702 return log(value + sqrt(value * value + 1));
703 } else {
704 return -log(-value + sqrt(value * value + 1));
705 }
706}
707#endif
708
709#if defined(__ISPOWERPC__)
710# undef EOS
711#endif
712
713#endif