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