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