CoolProp 6.8.1dev
An open-source fluid property and humid air property database
CoolProp-Tests.cpp
Go to the documentation of this file.
1
2
3#include "AbstractState.h"
4#include "DataStructures.h"
5#include "../Backends/Helmholtz/HelmholtzEOSMixtureBackend.h"
6#include "../Backends/Helmholtz/HelmholtzEOSBackend.h"
8
9// ############################################
10// TESTS
11// ############################################
12
13#if defined(ENABLE_CATCH)
14
16# include <catch2/catch_all.hpp>
17# include "CoolPropTools.h"
18# include "CoolProp.h"
19
20using namespace CoolProp;
21
22namespace TransportValidation {
23
24// A structure to hold the values for one validation call
25struct vel
26{
27 public:
28 std::string in1, in2, out, fluid;
29 double v1, v2, tol, expected;
30 vel(std::string fluid, std::string in1, double v1, std::string in2, double v2, std::string out, double expected, double tol) {
31 this->in1 = in1;
32 this->in2 = in2;
33 this->fluid = fluid;
34 this->v1 = v1;
35 this->v2 = v2;
36 this->expected = expected;
37 this->tol = tol;
38 };
39};
40
41vel viscosity_validation_data[] = {
42 // From Vogel, JPCRD, 1998
43 vel("Propane", "T", 90, "Dmolar", 16.52e3, "V", 7388e-6, 1e-3),
44 vel("Propane", "T", 150, "Dmolar", 15.14e3, "V", 656.9e-6, 5e-3),
45 vel("Propane", "T", 600, "Dmolar", 10.03e3, "V", 73.92e-6, 5e-3),
46 vel("Propane", "T", 280, "Dmolar", 11.78e3, "V", 117.4e-6, 1e-3),
47
48 // Huber, FPE, 2004
49 vel("n-Octane", "T", 300, "Dmolar", 6177.2, "V", 553.60e-6, 1e-3),
50 vel("n-Nonane", "T", 300, "Dmolar", 5619.1, "V", 709.53e-6, 1e-3),
51 vel("n-Decane", "T", 300, "Dmolar", 5150.4, "V", 926.44e-6, 1e-3),
52
53 // Huber, Energy & Fuels, 2004
54 vel("n-Dodecane", "T", 300, "Dmolar", 4411.5, "V", 1484.8e-6, 1e-3),
55 vel("n-Dodecane", "T", 500, "Dmolar", 3444.7, "V", 183.76e-6, 1e-3),
56
57 // Huber, I&ECR, 2006
58 vel("R125", "T", 300, "Dmolar", 10596.9998, "V", 177.37e-6, 1e-3),
59 vel("R125", "T", 400, "Dmolar", 30.631, "V", 17.070e-6, 1e-3),
60
61 // From REFPROP 9.1 since Huber I&ECR 2003 does not provide validation data
62 vel("R134a", "T", 185, "Q", 0, "V", 0.0012698376398294414, 1e-3),
63 vel("R134a", "T", 185, "Q", 1, "V", 7.4290821400170869e-006, 1e-3),
64 vel("R134a", "T", 360, "Q", 0, "V", 7.8146319978982133e-005, 1e-3),
65 vel("R134a", "T", 360, "Q", 1, "V", 1.7140264998576107e-005, 1e-3),
66
67 // From REFPROP 9.1 since Kiselev, IECR, 2005 does not provide validation data
68 vel("Ethanol", "T", 300, "Q", 0, "V", 0.0010439017679191723, 1e-3),
69 vel("Ethanol", "T", 300, "Q", 1, "V", 8.8293820936046416e-006, 1e-3),
70 vel("Ethanol", "T", 500, "Q", 0, "V", 6.0979347125450671e-005, 1e-3),
71 vel("Ethanol", "T", 500, "Q", 1, "V", 1.7229157141572511e-005, 1e-3),
72
73 // From CoolProp v5 implementation of correlation - more or less agrees with REFPROP
74 // Errata in BibTeX File
75 vel("Hydrogen", "T", 35, "Dmass", 100, "V", 5.47889e-005, 1e-3),
76
77 // From Meng 2012 experimental data (note erratum in BibTeX file)
78 vel("DimethylEther", "T", 253.146, "Dmass", 734.28, "V", 0.20444e-3, 3e-3),
79 vel("DimethylEther", "T", 373.132, "Dmass", 613.78, "V", 0.09991e-3, 3e-3),
80
81 // From Fenghour, JPCRD, 1995
82 vel("Ammonia", "T", 200, "Dmolar", 3.9, "V", 6.95e-6, 1e-3),
83 vel("Ammonia", "T", 200, "Dmolar", 42754.4, "V", 507.28e-6, 1e-3),
84 vel("Ammonia", "T", 398, "Dmolar", 7044.7, "V", 17.67e-6, 1e-3),
85 vel("Ammonia", "T", 398, "Dmolar", 21066.7, "V", 43.95e-6, 1e-3),
86
87 // From Lemmon and Jacobsen, JPCRD, 2004
88 vel("Nitrogen", "T", 100, "Dmolar", 1e-14, "V", 6.90349e-6, 1e-3),
89 vel("Nitrogen", "T", 300, "Dmolar", 1e-14, "V", 17.8771e-6, 1e-3),
90 vel("Nitrogen", "T", 100, "Dmolar", 25000, "V", 79.7418e-6, 1e-3),
91 vel("Nitrogen", "T", 200, "Dmolar", 10000, "V", 21.0810e-6, 1e-3),
92 vel("Nitrogen", "T", 300, "Dmolar", 5000, "V", 20.7430e-6, 1e-3),
93 vel("Nitrogen", "T", 126.195, "Dmolar", 11180, "V", 18.2978e-6, 1e-3),
94 vel("Argon", "T", 100, "Dmolar", 1e-14, "V", 8.18940e-6, 1e-3),
95 vel("Argon", "T", 300, "Dmolar", 1e-14, "V", 22.7241e-6, 1e-3),
96 vel("Argon", "T", 100, "Dmolar", 33000, "V", 184.232e-6, 1e-3),
97 vel("Argon", "T", 200, "Dmolar", 10000, "V", 25.5662e-6, 1e-3),
98 vel("Argon", "T", 300, "Dmolar", 5000, "V", 26.3706e-6, 1e-3),
99 vel("Argon", "T", 150.69, "Dmolar", 13400, "V", 27.6101e-6, 1e-3),
100 vel("Oxygen", "T", 100, "Dmolar", 1e-14, "V", 7.70243e-6, 1e-3),
101 vel("Oxygen", "T", 300, "Dmolar", 1e-14, "V", 20.6307e-6, 1e-3),
102 vel("Oxygen", "T", 100, "Dmolar", 35000, "V", 172.136e-6, 1e-3),
103 vel("Oxygen", "T", 200, "Dmolar", 10000, "V", 22.4445e-6, 1e-3),
104 vel("Oxygen", "T", 300, "Dmolar", 5000, "V", 23.7577e-6, 1e-3),
105 vel("Oxygen", "T", 154.6, "Dmolar", 13600, "V", 24.7898e-6, 1e-3),
106 vel("Air", "T", 100, "Dmolar", 1e-14, "V", 7.09559e-6, 1e-3),
107 vel("Air", "T", 300, "Dmolar", 1e-14, "V", 18.5230e-6, 1e-3),
108 vel("Air", "T", 100, "Dmolar", 28000, "V", 107.923e-6, 1e-3),
109 vel("Air", "T", 200, "Dmolar", 10000, "V", 21.1392e-6, 1e-3),
110 vel("Air", "T", 300, "Dmolar", 5000, "V", 21.3241e-6, 1e-3),
111 vel("Air", "T", 132.64, "Dmolar", 10400, "V", 17.7623e-6, 1e-3),
112
113 // From Michailidou, JPCRD, 2013
114 vel("Hexane", "T", 250, "Dmass", 1e-14, "V", 5.2584e-6, 1e-3),
115 vel("Hexane", "T", 400, "Dmass", 1e-14, "V", 8.4149e-6, 1e-3),
116 vel("Hexane", "T", 550, "Dmass", 1e-14, "V", 11.442e-6, 1e-3),
117 vel("Hexane", "T", 250, "Dmass", 700, "V", 528.2e-6, 1e-3),
118 vel("Hexane", "T", 400, "Dmass", 600, "V", 177.62e-6, 1e-3),
119 vel("Hexane", "T", 550, "Dmass", 500, "V", 95.002e-6, 1e-3),
120
121 // From Assael, JPCRD, 2014
122 vel("Heptane", "T", 250, "Dmass", 1e-14, "V", 4.9717e-6, 1e-3),
123 vel("Heptane", "T", 400, "Dmass", 1e-14, "V", 7.8361e-6, 1e-3),
124 vel("Heptane", "T", 550, "Dmass", 1e-14, "V", 10.7394e-6, 1e-3),
125 vel("Heptane", "T", 250, "Dmass", 720, "V", 725.69e-6, 1e-3),
126 vel("Heptane", "T", 400, "Dmass", 600, "V", 175.94e-6, 1e-3),
127 vel("Heptane", "T", 550, "Dmass", 500, "V", 95.105e-6, 1e-3),
128
129 // From Laesecke, JPCRD, 1998: https://pmc.ncbi.nlm.nih.gov/articles/PMC5514612/pdf/nihms869002.pdf
130 vel("CO2", "T", 100, "Dmass", 1e-5, "V", 0.0053757e-3, 1e-4),
131 vel("CO2", "T", 2000, "Dmass", 1e-5, "V", 0.066079e-3, 1e-4),
132 vel("CO2", "T", 10000, "Dmass", 1e-5, "V", 0.17620e-3, 1e-4),
133 vel("CO2", "T", 220, "Dmass", 3, "V", 0.011104e-3, 1e-4),
134 vel("CO2", "T", 225, "Dmass", 1150, "V", 0.22218e-3, 1e-4),
135 vel("CO2", "T", 300, "Dmass", 65, "V", 0.015563e-3, 1e-4),
136 vel("CO2", "T", 300, "Dmass", 1400, "V", 0.50594e-3, 1e-4),
137 vel("CO2", "T", 700, "Dmass", 100, "V", 0.033112e-3, 1e-4),
138 vel("CO2", "T", 700, "Dmass", 1200, "V", 0.22980e-3, 1e-4),
139
140 // Tanaka, IJT, 1996
141 vel("R123", "T", 265, "Dmass", 1545.8, "V", 627.1e-6, 1e-3),
142 vel("R123", "T", 265, "Dmass", 1.614, "V", 9.534e-6, 1e-3),
143 vel("R123", "T", 415, "Dmass", 1079.4, "V", 121.3e-6, 1e-3),
144 vel("R123", "T", 415, "Dmass", 118.9, "V", 15.82e-6, 1e-3),
145
146 // Huber, JPCRD, 2008 and IAPWS
147 vel("Water", "T", 298.15, "Dmass", 998, "V", 889.735100e-6, 1e-7),
148 vel("Water", "T", 298.15, "Dmass", 1200, "V", 1437.649467e-6, 1e-7),
149 vel("Water", "T", 373.15, "Dmass", 1000, "V", 307.883622e-6, 1e-7),
150 vel("Water", "T", 433.15, "Dmass", 1, "V", 14.538324e-6, 1e-7),
151 vel("Water", "T", 433.15, "Dmass", 1000, "V", 217.685358e-6, 1e-7),
152 vel("Water", "T", 873.15, "Dmass", 1, "V", 32.619287e-6, 1e-7),
153 vel("Water", "T", 873.15, "Dmass", 100, "V", 35.802262e-6, 1e-7),
154 vel("Water", "T", 873.15, "Dmass", 600, "V", 77.430195e-6, 1e-7),
155 vel("Water", "T", 1173.15, "Dmass", 1, "V", 44.217245e-6, 1e-7),
156 vel("Water", "T", 1173.15, "Dmass", 100, "V", 47.640433e-6, 1e-7),
157 vel("Water", "T", 1173.15, "Dmass", 400, "V", 64.154608e-6, 1e-7),
158 vel("Water", "T", 647.35, "Dmass", 122, "V", 25.520677e-6, 1e-7),
159 vel("Water", "T", 647.35, "Dmass", 222, "V", 31.337589e-6, 1e-7),
160 vel("Water", "T", 647.35, "Dmass", 272, "V", 36.228143e-6, 1e-7),
161 vel("Water", "T", 647.35, "Dmass", 322, "V", 42.961579e-6, 1e-7),
162 vel("Water", "T", 647.35, "Dmass", 372, "V", 45.688204e-6, 1e-7),
163 vel("Water", "T", 647.35, "Dmass", 422, "V", 49.436256e-6, 1e-7),
164
165 // Quinones-Cisneros, JPCRD, 2012
166 vel("SF6", "T", 300, "Dmass", 1e-14, "V", 15.2887e-6, 1e-4),
167 vel("SF6", "T", 300, "Dmass", 5.92, "V", 15.3043e-6, 1e-4),
168 vel("SF6", "T", 300, "Dmass", 1345.1, "V", 117.417e-6, 1e-4),
169 vel("SF6", "T", 400, "Dmass", 1e-14, "V", 19.6796e-6, 1e-4),
170 vel("SF6", "T", 400, "Dmass", 278.47, "V", 24.4272e-6, 1e-4),
171 vel("SF6", "T", 400, "Dmass", 1123.8, "V", 84.7835e-6, 1e-4),
172
173 // Quinones-Cisneros, JCED, 2012, data from validation
174 vel("H2S", "T", 200, "P", 1000e5, "V", 0.000460287, 1e-3),
175 vel("H2S", "T", 200, "P", 0.251702e5, "V", 8.02322E-06, 1e-3),
176 vel("H2S", "T", 596.961, "P", 1000e5, "V", 6.94741E-05, 1e-3),
177 vel("H2S", "T", 596.961, "P", 1e5, "V", 2.38654E-05, 1e-3),
178
179 // Geller, Purdue Conference, 2000
180 //vel("R410A", "T", 243.15, "Q", 0, "V", 238.61e-6, 5e-2),
181 //vel("R410A", "T", 243.15, "Q", 1, "V", 10.37e-6, 5e-2),
182 //vel("R410A", "T", 333.15, "Q", 0, "V", 70.71e-6, 5e-2),
183 //vel("R410A", "T", 333.15, "Q", 1, "V", 19.19e-6, 5e-2),
184 //vel("R407C", "T", 243.15, "Q", 0, "V", 304.18e-6, 1e-2),
185 //vel("R407C", "T", 243.15, "Q", 1, "V", 9.83e-6, 1e-2),
186 //vel("R407C", "T", 333.15, "Q", 0, "V", 95.96e-6, 1e-2),
187 //vel("R407C", "T", 333.15, "Q", 1, "V", 16.38e-6, 1e-2),
188 //vel("R404A", "T", 243.15, "Q", 0, "V", 264.67e-6, 1e-2),
189 //vel("R404A", "T", 243.15, "Q", 1, "V", 10.13e-6, 1e-2),
190 //vel("R404A", "T", 333.15, "Q", 0, "V", 73.92e-6, 1e-2),
191 //vel("R404A", "T", 333.15, "Q", 1, "V", 18.56e-6, 1e-2),
192 //vel("R507A", "T", 243.15, "Q", 0, "V", 284.59e-6, 3e-2),
193 //vel("R507A", "T", 243.15, "Q", 1, "V", 9.83e-6, 1e-2),
194 //vel("R507A", "T", 333.15, "Q", 0, "V", 74.37e-6, 1e-2),
195 //vel("R507A", "T", 333.15, "Q", 1, "V", 19.35e-6, 1e-2),
196
197 // From Arp, NIST, 1998
198 vel("Helium", "T", 3.6, "P", 0.180e6, "V", 3.745e-6, 1e-2),
199 vel("Helium", "T", 50, "P", 0.180e6, "V", 6.376e-6, 1e-2),
200 vel("Helium", "T", 400, "P", 0.180e6, "V", 24.29e-6, 1e-2),
201
202 // From Shan, ASHRAE, 2000
203 vel("R23", "T", 180, "Dmolar", 21097, "V", 353.88e-6, 1e-4),
204 vel("R23", "T", 420, "Dmolar", 7564, "V", 39.459e-6, 1e-4),
205 vel("R23", "T", 370, "Dmolar", 32.62, "V", 18.213e-6, 1e-4),
206
207 // From Friend, JPCRD, 1991
208 vel("Ethane", "T", 100, "Dmolar", 21330, "V", 878.6e-6, 1e-2),
209 vel("Ethane", "T", 430, "Dmolar", 12780, "V", 58.70e-6, 1e-2),
210 vel("Ethane", "T", 500, "Dmolar", 11210, "V", 48.34e-6, 1e-2),
211
212 // From Xiang, JPCRD, 2006
213 vel("Methanol", "T", 300, "Dmass", 0.12955, "V", 0.009696e-3, 1e-3),
214 vel("Methanol", "T", 300, "Dmass", 788.41, "V", 0.5422e-3, 1e-3),
215 vel("Methanol", "T", 630, "Dmass", 0.061183, "V", 0.02081e-3, 1e-3),
216 vel("Methanol", "T", 630, "Dmass", 888.50, "V", 0.2405e-3, 1e-1), // They use a different EOS in the high pressure region
217
218 // From REFPROP 9.1 since no data provided
219 vel("n-Butane", "T", 150, "Q", 0, "V", 0.0013697657668, 1e-4),
220 vel("n-Butane", "T", 400, "Q", 1, "V", 1.2027464524762453e-005, 1e-4),
221 vel("IsoButane", "T", 120, "Q", 0, "V", 0.0060558450757844271, 1e-4),
222 vel("IsoButane", "T", 400, "Q", 1, "V", 1.4761041187617117e-005, 2e-4),
223 vel("R134a", "T", 175, "Q", 0, "V", 0.0017558494524138289, 1e-4),
224 vel("R134a", "T", 360, "Q", 1, "V", 1.7140264998576107e-005, 1e-4),
225
226 // From Tariq, JPCRD, 2014
227 vel("Cyclohexane", "T", 300, "Dmolar", 1e-10, "V", 7.058e-6, 1e-4),
228 vel("Cyclohexane", "T", 300, "Dmolar", 0.0430e3, "V", 6.977e-6, 1e-4),
229 vel("Cyclohexane", "T", 300, "Dmolar", 9.1756e3, "V", 863.66e-6, 1e-4),
230 vel("Cyclohexane", "T", 300, "Dmolar", 9.9508e3, "V", 2850.18e-6, 1e-4),
231 vel("Cyclohexane", "T", 500, "Dmolar", 1e-10, "V", 11.189e-6, 1e-4),
232 vel("Cyclohexane", "T", 500, "Dmolar", 6.0213e3, "V", 94.842e-6, 1e-4),
233 vel("Cyclohexane", "T", 500, "Dmolar", 8.5915e3, "V", 380.04e-6, 1e-4),
234 vel("Cyclohexane", "T", 700, "Dmolar", 1e-10, "V", 15.093e-6, 1e-4),
235 vel("Cyclohexane", "T", 700, "Dmolar", 7.4765e3, "V", 176.749e-6, 1e-4),
236
237 // From Avgeri, JPCRD, 2014
238 vel("Benzene", "T", 300, "Dmass", 1e-10, "V", 7.625e-6, 1e-4),
239 vel("Benzene", "T", 400, "Dmass", 1e-10, "V", 10.102e-6, 1e-4),
240 vel("Benzene", "T", 550, "Dmass", 1e-10, "V", 13.790e-6, 1e-4),
241 vel("Benzene", "T", 300, "Dmass", 875, "V", 608.52e-6, 1e-4),
242 vel("Benzene", "T", 400, "Dmass", 760, "V", 211.74e-6, 1e-4),
243 vel("Benzene", "T", 550, "Dmass", 500, "V", 60.511e-6, 1e-4),
244
245 // From Cao, JPCRD, 2016
246 vel("m-Xylene", "T", 300, "Dmolar", 1e-10, "V", 6.637e-6, 1e-4),
247 vel("m-Xylene", "T", 300, "Dmolar", 0.04 * 1e3, "V", 6.564e-6, 1e-4),
248 vel("m-Xylene", "T", 300, "Dmolar", 8.0849 * 1e3, "V", 569.680e-6, 1e-4),
249 vel("m-Xylene", "T", 300, "Dmolar", 8.9421 * 1e3, "V", 1898.841e-6, 1e-4),
250 vel("m-Xylene", "T", 400, "Dmolar", 1e-10, "V", 8.616e-6, 1e-4),
251 vel("m-Xylene", "T", 400, "Dmolar", 0.04 * 1e3, "V", 8.585e-6, 1e-4),
252 vel("m-Xylene", "T", 400, "Dmolar", 7.2282 * 1e3, "V", 238.785e-6, 1e-4),
253 vel("m-Xylene", "T", 400, "Dmolar", 8.4734 * 1e3, "V", 718.950e-6, 1e-4),
254 vel("m-Xylene", "T", 600, "Dmolar", 1e-10, "V", 12.841e-6, 1e-4),
255 vel("m-Xylene", "T", 600, "Dmolar", 0.04 * 1e3, "V", 12.936e-6, 1e-4),
256 vel("m-Xylene", "T", 600, "Dmolar", 7.6591 * 1e3, "V", 299.164e-6, 1e-4),
257
258 // From Cao, JPCRD, 2016
259 vel("o-Xylene", "T", 300, "Dmolar", 1e-10, "V", 6.670e-6, 1e-4),
260 vel("o-Xylene", "T", 300, "Dmolar", 0.04 * 1e3, "V", 6.598e-6, 1e-4),
261 vel("o-Xylene", "T", 300, "Dmolar", 8.2369 * 1e3, "V", 738.286e-6, 1e-4),
262 vel("o-Xylene", "T", 300, "Dmolar", 8.7845 * 1e3, "V", 1645.436e-6, 1e-4),
263 vel("o-Xylene", "T", 400, "Dmolar", 1e-10, "V", 8.658e-6, 1e-4),
264 vel("o-Xylene", "T", 400, "Dmolar", 0.04 * 1e3, "V", 8.634e-6, 1e-4),
265 vel("o-Xylene", "T", 400, "Dmolar", 7.4060 * 1e3, "V", 279.954e-6, 1e-4),
266 vel("o-Xylene", "T", 400, "Dmolar", 8.2291 * 1e3, "V", 595.652e-6, 1e-4),
267 vel("o-Xylene", "T", 600, "Dmolar", 1e-10, "V", 12.904e-6, 1e-4),
268 vel("o-Xylene", "T", 600, "Dmolar", 0.04 * 1e3, "V", 13.018e-6, 1e-4),
269 vel("o-Xylene", "T", 600, "Dmolar", 7.2408 * 1e3, "V", 253.530e-6, 1e-4),
270
271 // From Balogun, JPCRD, 2016
272 vel("p-Xylene", "T", 300, "Dmolar", 1e-10, "V", 6.604e-6, 1e-4),
273 vel("p-Xylene", "T", 300, "Dmolar", 0.049 * 1e3, "V", 6.405e-6, 1e-4),
274 vel("p-Xylene", "T", 300, "Dmolar", 8.0548 * 1e3, "V", 593.272e-6, 1e-4),
275 vel("p-Xylene", "T", 300, "Dmolar", 8.6309 * 1e3, "V", 1266.337e-6, 1e-4),
276 vel("p-Xylene", "T", 400, "Dmolar", 1e-10, "V", 8.573e-6, 1e-4),
277 vel("p-Xylene", "T", 400, "Dmolar", 7.1995 * 1e3, "V", 239.202e-6, 1e-4),
278 vel("p-Xylene", "T", 400, "Dmolar", 8.0735 * 1e3, "V", 484.512e-6, 1e-4),
279 vel("p-Xylene", "T", 600, "Dmolar", 1e-10, "V", 12.777e-6, 1e-4),
280 vel("p-Xylene", "T", 600, "Dmolar", 7.0985 * 1e3, "V", 209.151e-6, 1e-4),
281
282 // From Mylona, JPCRD, 2014
283 vel("EthylBenzene", "T", 617, "Dmass", 316, "V", 33.22e-6, 1e-2),
284
285 // Heavy Water, IAPWS formulation
286 vel("HeavyWater", "T", 0.5000 * 643.847, "Dmass", 3.07 * 358, "V", 12.0604912273 * 55.2651e-6, 1e-5),
287 vel("HeavyWater", "T", 0.9000 * 643.847, "Dmass", 2.16 * 358, "V", 1.6561616211 * 55.2651e-6, 1e-5),
288 vel("HeavyWater", "T", 1.2000 * 643.847, "Dmass", 0.8 * 358, "V", 0.7651099154 * 55.2651e-6, 1e-5),
289
290 // Toluene, Avgeri, JPCRD, 2015
291 vel("Toluene", "T", 300, "Dmass", 1e-10, "V", 7.023e-6, 1e-4),
292 vel("Toluene", "T", 400, "Dmass", 1e-10, "V", 9.243e-6, 1e-4),
293 vel("Toluene", "T", 550, "Dmass", 1e-10, "V", 12.607e-6, 1e-4),
294 vel("Toluene", "T", 300, "Dmass", 865, "V", 566.78e-6, 1e-4),
295 vel("Toluene", "T", 400, "Dmass", 770, "V", 232.75e-6, 1e-4),
296 vel("Toluene", "T", 550, "Dmass", 550, "V", 80.267e-6, 1e-4),
297
298};
299
300class TransportValidationFixture
301{
302 protected:
303 CoolPropDbl actual, x1, x2;
304 shared_ptr<CoolProp::AbstractState> pState;
306
307 public:
308 TransportValidationFixture() {}
309 ~TransportValidationFixture() {}
310 void set_backend(std::string backend, std::string fluid_name) {
311 pState.reset(CoolProp::AbstractState::factory(backend, fluid_name));
312 }
313 void set_pair(std::string& in1, double v1, std::string& in2, double v2) {
314 double o1, o2;
317 CoolProp::input_pairs pair = CoolProp::generate_update_pair(iin1, v1, iin2, v2, o1, o2);
318 pState->update(pair, o1, o2);
319 }
320 void get_value(parameters key) {
321 actual = pState->keyed_output(key);
322 }
323};
324
325TEST_CASE_METHOD(TransportValidationFixture, "Compare viscosities against published data", "[viscosity],[transport]") {
326 int inputsN = sizeof(viscosity_validation_data) / sizeof(viscosity_validation_data[0]);
327 for (int i = 0; i < inputsN; ++i) {
328 vel el = viscosity_validation_data[i];
329 CHECK_NOTHROW(set_backend("HEOS", el.fluid));
330
331 CAPTURE(el.fluid);
332 CAPTURE(el.in1);
333 CAPTURE(el.v1);
334 CAPTURE(el.in2);
335 CAPTURE(el.v2);
336 CHECK_NOTHROW(set_pair(el.in1, el.v1, el.in2, el.v2));
337 CHECK_NOTHROW(get_value(CoolProp::iviscosity));
338 CAPTURE(el.expected);
339 CAPTURE(actual);
340 CHECK(std::abs(actual / el.expected - 1) < el.tol);
341 }
342}
343
344vel conductivity_validation_data[] = {
346
347 // From Assael, JPCRD, 2013
348 vel("Hexane", "T", 250, "Dmass", 700, "L", 137.62e-3, 1e-4),
349 vel("Hexane", "T", 400, "Dmass", 2, "L", 23.558e-3, 1e-4),
350 vel("Hexane", "T", 400, "Dmass", 650, "L", 129.28e-3, 2e-4),
351 vel("Hexane", "T", 510, "Dmass", 2, "L", 36.772e-3, 1e-4),
352
353 // From Assael, JPCRD, 2013
354 vel("Heptane", "T", 250, "Dmass", 720, "L", 137.09e-3, 1e-4),
355 vel("Heptane", "T", 400, "Dmass", 2, "L", 21.794e-3, 1e-4),
356 vel("Heptane", "T", 400, "Dmass", 650, "L", 120.75e-3, 1e-4),
357 vel("Heptane", "T", 535, "Dmass", 100, "L", 51.655e-3, 3e-3), // Relaxed tolerance because conductivity was fit using older viscosity correlation
358
359 // From Assael, JPCRD, 2013
360 vel("Ethanol", "T", 300, "Dmass", 850, "L", 209.68e-3, 1e-4),
361 vel("Ethanol", "T", 400, "Dmass", 2, "L", 26.108e-3, 1e-4),
362 vel("Ethanol", "T", 400, "Dmass", 690, "L", 149.21e-3, 1e-4),
363 vel("Ethanol", "T", 500, "Dmass", 10, "L", 39.594e-3, 1e-4),
364
366 //vel("Toluene", "T", 298.15, "Dmass", 1e-15, "L", 10.749e-3, 1e-4),
367 //vel("Toluene", "T", 298.15, "Dmass", 862.948, "L", 130.66e-3, 1e-4),
368 //vel("Toluene", "T", 298.15, "Dmass", 876.804, "L", 136.70e-3, 1e-4),
369 //vel("Toluene", "T", 595, "Dmass", 1e-15, "L", 40.538e-3, 1e-4),
370 //vel("Toluene", "T", 595, "Dmass", 46.512, "L", 41.549e-3, 1e-4),
371 //vel("Toluene", "T", 185, "Dmass", 1e-15, "L", 4.3758e-3, 1e-4),
372 //vel("Toluene", "T", 185, "Dmass", 968.821, "L", 158.24e-3, 1e-4),
373
374 // From Assael, JPCRD, 2012
375 vel("SF6", "T", 298.15, "Dmass", 1e-13, "L", 12.952e-3, 1e-4),
376 vel("SF6", "T", 298.15, "Dmass", 100, "L", 14.126e-3, 1e-4),
377 vel("SF6", "T", 298.15, "Dmass", 1600, "L", 69.729e-3, 1e-4),
378 vel("SF6", "T", 310, "Dmass", 1e-13, "L", 13.834e-3, 1e-4),
379 vel("SF6", "T", 310, "Dmass", 1200, "L", 48.705e-3, 1e-4),
380 vel("SF6", "T", 480, "Dmass", 100, "L", 28.847e-3, 1e-4),
381
383 //vel("Benzene", "T", 290, "Dmass", 890, "L", 147.66e-3, 1e-4),
384 //vel("Benzene", "T", 500, "Dmass", 2, "L", 30.174e-3, 1e-4),
385 //vel("Benzene", "T", 500, "Dmass", 32, "L", 32.175e-3, 1e-4),
386 //vel("Benzene", "T", 500, "Dmass", 800, "L", 141.24e-3, 1e-4),
387 //vel("Benzene", "T", 575, "Dmass", 1.7, "L", 37.763e-3, 1e-4),
388
389 // From Assael, JPCRD, 2011
390 vel("Hydrogen", "T", 298.15, "Dmass", 1e-13, "L", 185.67e-3, 1e-4),
391 vel("Hydrogen", "T", 298.15, "Dmass", 0.80844, "L", 186.97e-3, 1e-4),
392 vel("Hydrogen", "T", 298.15, "Dmass", 14.4813, "L", 201.35e-3, 1e-4),
393 vel("Hydrogen", "T", 35, "Dmass", 1e-13, "L", 26.988e-3, 1e-4),
394 vel("Hydrogen", "T", 35, "Dmass", 30, "L", 0.0770177, 1e-4), // Updated since Assael uses a different viscosity correlation
395 vel("Hydrogen", "T", 18, "Dmass", 1e-13, "L", 13.875e-3, 1e-4),
396 vel("Hydrogen", "T", 18, "Dmass", 75, "L", 104.48e-3, 1e-4),
397 /*vel("ParaHydrogen", "T", 298.15, "Dmass", 1e-13, "L", 192.38e-3, 1e-4),
398vel("ParaHydrogen", "T", 298.15, "Dmass", 0.80844, "L", 192.81e-3, 1e-4),
399vel("ParaHydrogen", "T", 298.15, "Dmass", 14.4813, "L", 207.85e-3, 1e-4),
400vel("ParaHydrogen", "T", 35, "Dmass", 1e-13, "L", 27.222e-3, 1e-4),
401vel("ParaHydrogen", "T", 35, "Dmass", 30, "L", 70.335e-3, 1e-4),
402vel("ParaHydrogen", "T", 18, "Dmass", 1e-13, "L", 13.643e-3, 1e-4),
403vel("ParaHydrogen", "T", 18, "Dmass", 75, "L", 100.52e-3, 1e-4),*/
404
405 // Some of these don't work
406 vel("R125", "T", 341, "Dmass", 600, "L", 0.0565642978494, 2e-4),
407 vel("R125", "T", 200, "Dmass", 1e-13, "L", 0.007036843623086, 2e-4),
408 vel("IsoButane", "T", 390, "Dmass", 387.09520158645068, "L", 0.063039, 2e-4),
409 vel("IsoButane", "T", 390, "Dmass", 85.76703973869482, "L", 0.036603, 2e-4),
410 vel("n-Butane", "T", 415, "Dmass", 360.01895129934866, "L", 0.067045, 2e-4),
411 vel("n-Butane", "T", 415, "Dmass", 110.3113177144, "L", 0.044449, 1e-4),
412
413 // From Huber, FPE, 2005
414 vel("n-Octane", "T", 300, "Dmolar", 6177.2, "L", 0.12836, 1e-4),
415 vel("n-Nonane", "T", 300, "Dmolar", 5619.4, "L", 0.13031, 1e-4),
416 //vel("n-Decane", "T", 300, "Dmass", 5150.4, "L", 0.13280, 1e-4), // no viscosity
417
418 // From Huber, EF, 2004
419 vel("n-Dodecane", "T", 300, "Dmolar", 4411.5, "L", 0.13829, 1e-4),
420 vel("n-Dodecane", "T", 500, "Dmolar", 3444.7, "L", 0.09384, 1e-4),
421 vel("n-Dodecane", "T", 660, "Dmolar", 1500.98, "L", 0.090346, 1e-4),
422
423 // From REFPROP 9.1 since no data provided in Marsh, 2002
424 vel("n-Propane", "T", 368, "Q", 0, "L", 0.07282154952457, 1e-3),
425 vel("n-Propane", "T", 368, "Dmolar", 1e-10, "L", 0.0266135388745317, 1e-4),
426
427 // From Perkins, JCED, 2011
428 //vel("R1234yf", "T", 250, "Dmass", 2.80006, "L", 0.0098481, 1e-4),
429 //vel("R1234yf", "T", 300, "Dmass", 4.671556, "L", 0.013996, 1e-4),
430 //vel("R1234yf", "T", 250, "Dmass", 1299.50, "L", 0.088574, 1e-4),
431 //vel("R1234yf", "T", 300, "Dmass", 1182.05, "L", 0.075245, 1e-4),
432 //vel("R1234ze(E)", "T", 250, "Dmass", 2.80451, "L", 0.0098503, 1e-4),
433 //vel("R1234ze(E)", "T", 300, "Dmass", 4.67948, "L", 0.013933, 1e-4),
434 //vel("R1234ze(E)", "T", 250, "Dmass", 1349.37, "L", 0.10066, 1e-4),
435 //vel("R1234ze(E)", "T", 300, "Dmass", 1233.82, "L", 0.085389, 1e-4),
436
437 // From Laesecke, IJR 1995
438 vel("R123", "T", 180, "Dmass", 1739, "L", 110.9e-3, 2e-4),
439 vel("R123", "T", 180, "Dmass", 0.2873e-2, "L", 2.473e-3, 1e-3),
440 vel("R123", "T", 430, "Dmass", 996.35, "L", 45.62e-3, 1e-3),
441 vel("R123", "T", 430, "Dmass", 166.9, "L", 21.03e-3, 1e-3),
442
443 // From Huber, JPCRD, 2016
444 vel("CO2", "T", 250.0, "Dmass", 1e-6, "L", 12.99e-3, 1e-3),
445 vel("CO2", "T", 250.0, "Dmass", 2.0, "L", 13.05e-3, 1e-3),
446 vel("CO2", "T", 250.0, "Dmass", 1058.0, "L", 140.00e-3, 1e-4),
447 vel("CO2", "T", 310.0, "Dmass", 400.0, "L", 73.04e-3, 1e-4),
448
449 // From Friend, JPCRD, 1991
450 vel("Ethane", "T", 100, "Dmass", 1e-13, "L", 3.46e-3, 1e-2),
451 vel("Ethane", "T", 230, "Dmolar", 16020, "L", 126.2e-3, 1e-2),
452 vel("Ethane", "T", 440, "Dmolar", 1520, "L", 45.9e-3, 1e-2),
453 vel("Ethane", "T", 310, "Dmolar", 4130, "L", 45.4e-3, 1e-2),
454
455 // From Lemmon and Jacobsen, JPCRD, 2004
456 vel("Nitrogen", "T", 100, "Dmolar", 1e-14, "L", 9.27749e-3, 1e-4),
457 vel("Nitrogen", "T", 300, "Dmolar", 1e-14, "L", 25.9361e-3, 1e-4),
458 vel("Nitrogen", "T", 100, "Dmolar", 25000, "L", 103.834e-3, 1e-4),
459 vel("Nitrogen", "T", 200, "Dmolar", 10000, "L", 36.0099e-3, 1e-4),
460 vel("Nitrogen", "T", 300, "Dmolar", 5000, "L", 32.7694e-3, 1e-4),
461 vel("Nitrogen", "T", 126.195, "Dmolar", 11180, "L", 675.800e-3, 1e-4),
462 vel("Argon", "T", 100, "Dmolar", 1e-14, "L", 6.36587e-3, 1e-4),
463 vel("Argon", "T", 300, "Dmolar", 1e-14, "L", 17.8042e-3, 1e-4),
464 vel("Argon", "T", 100, "Dmolar", 33000, "L", 111.266e-3, 1e-4),
465 vel("Argon", "T", 200, "Dmolar", 10000, "L", 26.1377e-3, 1e-4),
466 vel("Argon", "T", 300, "Dmolar", 5000, "L", 23.2302e-3, 1e-4),
467 vel("Argon", "T", 150.69, "Dmolar", 13400, "L", 856.793e-3, 1e-4),
468 vel("Oxygen", "T", 100, "Dmolar", 1e-14, "L", 8.94334e-3, 1e-4),
469 vel("Oxygen", "T", 300, "Dmolar", 1e-14, "L", 26.4403e-3, 1e-4),
470 vel("Oxygen", "T", 100, "Dmolar", 35000, "L", 146.044e-3, 1e-4),
471 vel("Oxygen", "T", 200, "Dmolar", 10000, "L", 34.6124e-3, 1e-4),
472 vel("Oxygen", "T", 300, "Dmolar", 5000, "L", 32.5491e-3, 1e-4),
473 vel("Oxygen", "T", 154.6, "Dmolar", 13600, "L", 377.476e-3, 1e-4),
474 vel("Air", "T", 100, "Dmolar", 1e-14, "L", 9.35902e-3, 1e-4),
475 vel("Air", "T", 300, "Dmolar", 1e-14, "L", 26.3529e-3, 1e-4),
476 vel("Air", "T", 100, "Dmolar", 28000, "L", 119.221e-3, 1e-4),
477 vel("Air", "T", 200, "Dmolar", 10000, "L", 35.3185e-3, 1e-4),
478 vel("Air", "T", 300, "Dmolar", 5000, "L", 32.6062e-3, 1e-4),
479 vel("Air", "T", 132.64, "Dmolar", 10400, "L", 75.6231e-3, 1e-4),
480
481 // Huber, JPCRD, 2012
482 vel("Water", "T", 298.15, "Dmass", 1e-14, "L", 18.4341883e-3, 1e-6),
483 vel("Water", "T", 298.15, "Dmass", 998, "L", 607.712868e-3, 1e-6),
484 vel("Water", "T", 298.15, "Dmass", 1200, "L", 799.038144e-3, 1e-6),
485 vel("Water", "T", 873.15, "Dmass", 1e-14, "L", 79.1034659e-3, 1e-6),
486 vel("Water", "T", 647.35, "Dmass", 1, "L", 51.9298924e-3, 1e-6),
487 vel("Water", "T", 647.35, "Dmass", 122, "L", 130.922885e-3, 2e-4),
488 vel("Water", "T", 647.35, "Dmass", 222, "L", 367.787459e-3, 2e-4),
489 vel("Water", "T", 647.35, "Dmass", 272, "L", 757.959776e-3, 2e-4),
490 vel("Water", "T", 647.35, "Dmass", 322, "L", 1443.75556e-3, 2e-4),
491 vel("Water", "T", 647.35, "Dmass", 372, "L", 650.319402e-3, 2e-4),
492 vel("Water", "T", 647.35, "Dmass", 422, "L", 448.883487e-3, 2e-4),
493 vel("Water", "T", 647.35, "Dmass", 750, "L", 600.961346e-3, 2e-4),
494
495 // From Shan, ASHRAE, 2000
496 vel("R23", "T", 180, "Dmolar", 21097, "L", 143.19e-3, 1e-4),
497 vel("R23", "T", 420, "Dmolar", 7564, "L", 50.19e-3, 2e-4),
498 vel("R23", "T", 370, "Dmolar", 32.62, "L", 17.455e-3, 1e-4),
499
500 // From REFPROP 9.1 since no sample data provided in Tufeu
501 vel("Ammonia", "T", 310, "Dmolar", 34320, "L", 0.45223303481784971, 1e-4),
502 vel("Ammonia", "T", 395, "Q", 0, "L", 0.2264480769301, 1e-4),
503
504 // From Hands, Cryogenics, 1981
505 vel("Helium", "T", 800, "P", 1e5, "L", 0.3085, 1e-2),
506 vel("Helium", "T", 300, "P", 1e5, "L", 0.1560, 1e-2),
507 vel("Helium", "T", 20, "P", 1e5, "L", 0.0262, 1e-2),
508 vel("Helium", "T", 8, "P", 1e5, "L", 0.0145, 1e-2),
509 vel("Helium", "T", 4, "P", 20e5, "L", 0.0255, 1e-2),
510 vel("Helium", "T", 8, "P", 20e5, "L", 0.0308, 1e-2),
511 vel("Helium", "T", 20, "P", 20e5, "L", 0.0328, 1e-2),
512 vel("Helium", "T", 4, "P", 100e5, "L", 0.0385, 3e-2),
513 vel("Helium", "T", 8, "P", 100e5, "L", 0.0566, 3e-2),
514 vel("Helium", "T", 20, "P", 100e5, "L", 0.0594, 1e-2),
515 vel("Helium", "T", 4, "P", 1e5, "L", 0.0186, 1e-2),
516 vel("Helium", "T", 4, "P", 2e5, "L", 0.0194, 1e-2),
517 vel("Helium", "T", 5.180, "P", 2.3e5, "L", 0.0195, 1e-1),
518 vel("Helium", "T", 5.2, "P", 2.3e5, "L", 0.0202, 1e-1),
519 vel("Helium", "T", 5.230, "P", 2.3e5, "L", 0.0181, 1e-1),
520 vel("Helium", "T", 5.260, "P", 2.3e5, "L", 0.0159, 1e-1),
521 vel("Helium", "T", 5.3, "P", 2.3e5, "L", 0.0149, 1e-1),
522
523 // Geller, IJT, 2001 - based on experimental data, no validation data provided
524 //vel("R404A", "T", 253.03, "P", 0.101e6, "L", 0.00991, 0.03),
525 //vel("R404A", "T", 334.38, "P", 2.176e6, "L", 19.93e-3, 0.03),
526 //vel("R407C", "T", 253.45, "P", 0.101e6, "L", 0.00970, 0.03),
527 //vel("R407C", "T", 314.39, "P", 0.458e6, "L", 14.87e-3, 0.03),
528 //vel("R410A", "T", 260.32, "P", 0.101e6, "L", 0.01043, 0.03),
529 //vel("R410A", "T", 332.09, "P", 3.690e6, "L", 22.76e-3, 0.03),
530 //vel("R507A", "T", 254.85, "P", 0.101e6, "L", 0.01007, 0.03),
531 //vel("R507A", "T", 333.18, "P", 2.644e6, "L", 21.31e-3, 0.03),
532
533 // From REFPROP 9.1 since no data provided
534 vel("R134a", "T", 240, "D", 1e-10, "L", 0.008698768, 1e-4),
535 vel("R134a", "T", 330, "D", 1e-10, "L", 0.015907606, 1e-4),
536 vel("R134a", "T", 330, "Q", 0, "L", 0.06746432253, 1e-4),
537 vel("R134a", "T", 240, "Q", 1, "L", 0.00873242359, 1e-4),
538
539 // Mylona, JPCRD, 2014
540 vel("o-Xylene", "T", 635, "D", 270, "L", 96.4e-3, 1e-2),
541 vel("m-Xylene", "T", 616, "D", 220, "L", 79.5232e-3, 1e-2), // CoolProp is correct, paper is incorrect (it seems)
542 vel("p-Xylene", "T", 620, "D", 287, "L", 107.7e-3, 1e-2),
543 vel("EthylBenzene", "T", 617, "D", 316, "L", 140.2e-3, 1e-2),
544 // dilute values
545 vel("o-Xylene", "T", 300, "D", 1e-12, "L", 13.68e-3, 1e-3),
546 vel("o-Xylene", "T", 600, "D", 1e-12, "L", 41.6e-3, 1e-3),
547 vel("m-Xylene", "T", 300, "D", 1e-12, "L", 9.45e-3, 1e-3),
548 vel("m-Xylene", "T", 600, "D", 1e-12, "L", 40.6e-3, 1e-3),
549 vel("p-Xylene", "T", 300, "D", 1e-12, "L", 10.57e-3, 1e-3),
550 vel("p-Xylene", "T", 600, "D", 1e-12, "L", 41.73e-3, 1e-3),
551 vel("EthylBenzene", "T", 300, "D", 1e-12, "L", 9.71e-3, 1e-3),
552 vel("EthylBenzene", "T", 600, "D", 1e-12, "L", 41.14e-3, 1e-3),
553
554 // Friend, JPCRD, 1989
555 vel("Methane", "T", 100, "D", 1e-12, "L", 9.83e-3, 1e-3),
556 vel("Methane", "T", 400, "D", 1e-12, "L", 49.96e-3, 1e-3),
557 vel("Methane", "T", 182, "Q", 0, "L", 82.5e-3, 5e-3),
558 vel("Methane", "T", 100, "Dmolar", 28.8e3, "L", 234e-3, 1e-2),
559
560 // Sykioti, JPCRD, 2013
561 vel("Methanol", "T", 300, "Dmass", 850, "L", 241.48e-3, 1e-2),
562 vel("Methanol", "T", 400, "Dmass", 2, "L", 25.803e-3, 1e-2),
563 vel("Methanol", "T", 400, "Dmass", 690, "L", 183.59e-3, 1e-2),
564 vel("Methanol", "T", 500, "Dmass", 10, "L", 40.495e-3, 1e-2),
565
566 // Heavy Water, IAPWS formulation
567 vel("HeavyWater", "T", 0.5000 * 643.847, "Dmass", 3.07 * 358, "V", 835.786416818 * 0.742128e-3, 1e-5),
568 vel("HeavyWater", "T", 0.9000 * 643.847, "Dmass", 2.16 * 358, "V", 627.777590127 * 0.742128e-3, 1e-5),
569 vel("HeavyWater", "T", 1.2000 * 643.847, "Dmass", 0.8 * 358, "V", 259.605241187 * 0.742128e-3, 1e-5),
570
571 // Vassiliou, JPCRD, 2015
572 vel("Cyclopentane", "T", 512, "Dmass", 1e-12, "L", 37.042e-3, 1e-5),
573 vel("Cyclopentane", "T", 512, "Dmass", 400, "L", 69.698e-3, 1e-1),
574 vel("Isopentane", "T", 460, "Dmass", 1e-12, "L", 35.883e-3, 1e-4),
575 vel("Isopentane", "T", 460, "Dmass", 329.914, "L", 59.649e-3, 1e-1),
576 vel("n-Pentane", "T", 460, "Dmass", 1e-12, "L", 34.048e-3, 1e-5),
577 vel("n-Pentane", "T", 460, "Dmass", 377.687, "L", 71.300e-3, 1e-1),
578};
579
580TEST_CASE_METHOD(TransportValidationFixture, "Compare thermal conductivities against published data", "[conductivity],[transport]") {
581 int inputsN = sizeof(conductivity_validation_data) / sizeof(conductivity_validation_data[0]);
582 for (int i = 0; i < inputsN; ++i) {
583 vel el = conductivity_validation_data[i];
584 CHECK_NOTHROW(set_backend("HEOS", el.fluid));
585 CAPTURE(el.fluid);
586 CAPTURE(el.in1);
587 CAPTURE(el.v1);
588 CAPTURE(el.in2);
589 CAPTURE(el.v2);
590 CHECK_NOTHROW(set_pair(el.in1, el.v1, el.in2, el.v2));
591 get_value(CoolProp::iconductivity);
592 CAPTURE(el.expected);
593 CAPTURE(actual);
594 CHECK(std::abs(actual / el.expected - 1) < el.tol);
595 }
596}
597
598}; /* namespace TransportValidation */
599
600static CoolProp::input_pairs inputs[] = {
602 //CoolProp::SmolarT_INPUTS,
603 //CoolProp::HmolarT_INPUTS,
604 //CoolProp::TUmolar_INPUTS,
605
606 // CoolProp::DmolarP_INPUTS,
607 // CoolProp::DmolarHmolar_INPUTS,
608 // CoolProp::DmolarSmolar_INPUTS,
609 // CoolProp::DmolarUmolar_INPUTS,
610 //
611 // CoolProp::HmolarP_INPUTS,
612 // CoolProp::PSmolar_INPUTS,
613 // CoolProp::PUmolar_INPUTS,
614 //
615 /*
616 CoolProp::HmolarSmolar_INPUTS,
617 CoolProp::HmolarUmolar_INPUTS,
618 CoolProp::SmolarUmolar_INPUTS
619 */
620};
621
622class ConsistencyFixture
623{
624 protected:
625 CoolPropDbl hmolar, pmolar, smolar, umolar, rhomolar, T, p, x1, x2;
626 shared_ptr<CoolProp::AbstractState> pState;
628
629 public:
630 ConsistencyFixture() {}
631 ~ConsistencyFixture() {}
632 void set_backend(std::string backend, std::string fluid_name) {
633 pState.reset(CoolProp::AbstractState::factory(backend, fluid_name));
634 }
635 void set_pair(CoolProp::input_pairs pair) {
636 this->pair = pair;
637 }
638 void set_TP(CoolPropDbl T, CoolPropDbl p) {
639 this->T = T;
640 this->p = p;
641 CoolProp::AbstractState& State = *pState;
642
643 // Start with T,P as inputs, cycle through all the other pairs that are supported
644 State.update(CoolProp::PT_INPUTS, p, T);
645
646 // Set the other state variables
647 rhomolar = State.rhomolar();
648 hmolar = State.hmolar();
649 smolar = State.smolar();
650 umolar = State.umolar();
651 }
652 void get_variables() {
653
654 switch (pair) {
657 x1 = hmolar;
658 x2 = T;
659 break;
661 x1 = smolar;
662 x2 = T;
663 break;
665 x1 = T;
666 x2 = umolar;
667 break;
669 x1 = rhomolar;
670 x2 = T;
671 break;
672
675 x1 = rhomolar;
676 x2 = hmolar;
677 break;
679 x1 = rhomolar;
680 x2 = smolar;
681 break;
683 x1 = rhomolar;
684 x2 = umolar;
685 break;
687 x1 = rhomolar;
688 x2 = p;
689 break;
690
693 x1 = hmolar;
694 x2 = p;
695 break;
697 x1 = p;
698 x2 = smolar;
699 break;
701 x1 = p;
702 x2 = umolar;
703 break;
704
706 x1 = hmolar;
707 x2 = smolar;
708 break;
710 x1 = smolar;
711 x2 = umolar;
712 break;
713
714 default:
715 throw CoolProp::ValueError();
716 }
717 }
718 void single_phase_consistency_check() {
719 CoolProp::AbstractState& State = *pState;
720 State.update(pair, x1, x2);
721
722 // Make sure we end up back at the same temperature and pressure we started out with
723 if (State.Q() < 1 && State.Q() > 0) throw CoolProp::ValueError(format("Q [%g] is between 0 and 1; two-phase solution", State.Q()));
724 if (std::abs(T - State.T()) > 1e-2) throw CoolProp::ValueError(format("Error on T [%Lg K] is greater than 1e-2", std::abs(State.T() - T)));
725 if (std::abs(p - State.p()) / p * 100 > 1e-2)
726 throw CoolProp::ValueError(format("Error on p [%Lg %%] is greater than 1e-2 %%", std::abs(p - State.p()) / p * 100));
727 }
728 void subcritical_pressure_liquid() {
729 // Subcritical pressure liquid
730 int inputsN = sizeof(inputs) / sizeof(inputs[0]);
731 for (double p = pState->p_triple() * 1.1; p < pState->p_critical(); p *= 3) {
732 double Ts = PropsSI("T", "P", p, "Q", 0, "Water");
733 double Tmelt = pState->melting_line(CoolProp::iT, CoolProp::iP, p);
734 for (double T = Tmelt; T < Ts - 0.1; T += 0.1) {
735 CHECK_NOTHROW(set_TP(T, p));
736
737 for (int i = 0; i < inputsN; ++i) {
738 CoolProp::input_pairs pair = inputs[i];
739 std::string pair_desc = CoolProp::get_input_pair_short_desc(pair);
740 set_pair(pair);
741 CAPTURE(pair_desc);
742 CAPTURE(T);
743 CAPTURE(p);
744 get_variables();
745 CAPTURE(x1);
746 CAPTURE(x2);
747 CAPTURE(Ts);
748 CHECK_NOTHROW(single_phase_consistency_check());
749 double rhomolar_RP = PropsSI("Dmolar", "P", p, "T", T, "REFPROP::Water");
750 if (ValidNumber(rhomolar_RP)) {
751 CAPTURE(rhomolar_RP);
752 CAPTURE(rhomolar);
753 CHECK(std::abs((rhomolar_RP - rhomolar) / rhomolar) < 1e-3);
754 }
755 }
756 }
757 }
758 }
759};
760
761TEST_CASE_METHOD(ConsistencyFixture, "Test all input pairs for Water using all valid backends", "[consistency]") {
762 CHECK_NOTHROW(set_backend("HEOS", "Water"));
763 subcritical_pressure_liquid();
764
765 // int inputsN = sizeof(inputs)/sizeof(inputs[0]);
766 // for (double p = 600000; p < pState->pmax(); p *= 3)
767 // {
768 // for (double T = 220; T < pState->Tmax(); T += 1)
769 // {
770 // CHECK_NOTHROW(set_TP(T, p));
771 //
772 // for (int i = 0; i < inputsN; ++i)
773 // {
774 // CoolProp::input_pairs pair = inputs[i];
775 // std::string pair_desc = CoolProp::get_input_pair_short_desc(pair);
776 // set_pair(pair);
777 // CAPTURE(pair_desc);
778 // CAPTURE(T);
779 // CAPTURE(p);
780 // get_variables();
781 // CAPTURE(x1);
782 // CAPTURE(x2);
783 // CHECK_NOTHROW(single_phase_consistency_check());
784 // }
785 // }
786 // }
787}
788
789TEST_CASE("Test saturation properties for a few fluids", "[saturation],[slow]") {
790 SECTION("sat_p") {
791 std::vector<double> pv = linspace(Props1SI("CO2", "ptriple"), Props1SI("CO2", "pcrit") - 1e-6, 5);
792
793 SECTION("All pressures are ok")
794 for (std::size_t i = 0; i < pv.size(); ++i) {
795 CAPTURE(pv[i]);
796 double T = CoolProp::PropsSI("T", "P", pv[i], "Q", 0, "CO2");
797 }
798 }
799}
800
801class HumidAirDewpointFixture
802{
803 public:
804 shared_ptr<CoolProp::AbstractState> AS;
805 std::vector<std::string> fluids;
806 std::vector<double> z;
807 void setup(double zH2O) {
808 double z_Air[4] = {0.7810, 0.2095, 0.0092, 0.0003}; // N2, O2, Ar, CO2
809 z.resize(5);
810 z[0] = zH2O;
811 for (int i = 0; i < 4; ++i) {
812 z[i + 1] = (1 - zH2O) * z_Air[i];
813 }
814 }
815 void run_p(double p) {
816 CAPTURE(p);
817 for (double zH2O = 0.999; zH2O > 0; zH2O -= 0.001) {
818 setup(zH2O);
819 AS->set_mole_fractions(z);
820 CAPTURE(zH2O);
821 CHECK_NOTHROW(AS->update(PQ_INPUTS, p, 1));
822 if (AS->T() < 273.15) {
823 break;
824 }
825 }
826 }
827 void run_checks() {
828 fluids = strsplit("Water&Nitrogen&Oxygen&Argon&CO2", '&');
829 AS.reset(AbstractState::factory("HEOS", fluids));
830 run_p(1e5);
831 run_p(1e6);
832 run_p(1e7);
833 }
834};
835TEST_CASE_METHOD(HumidAirDewpointFixture, "Humid air dewpoint calculations", "[humid_air_dewpoint]") {
836 run_checks();
837}
838
839TEST_CASE("Test consistency between Gernert models in CoolProp and Gernert models in REFPROP", "[Gernert]") {
840 // See https://groups.google.com/forum/?fromgroups#!topic/catch-forum/mRBKqtTrITU
841 std::string mixes[] = {"CO2[0.7]&Argon[0.3]", "CO2[0.7]&Water[0.3]", "CO2[0.7]&Nitrogen[0.3]"};
842 for (int i = 0; i < 3; ++i) {
843 const char* ykey = mixes[i].c_str();
844 std::ostringstream ss1;
845 ss1 << mixes[i];
846 SECTION(ss1.str(), "") {
847 double Tnbp_CP, Tnbp_RP, R_RP, R_CP, pchk_CP, pchk_RP;
848 CHECK_NOTHROW(R_CP = PropsSI("gas_constant", "P", 101325, "Q", 1, "HEOS::" + mixes[i]));
849 CAPTURE(R_CP);
850 CHECK_NOTHROW(R_RP = PropsSI("gas_constant", "P", 101325, "Q", 1, "REFPROP::" + mixes[i]));
851 CAPTURE(R_RP);
852 CHECK_NOTHROW(Tnbp_CP = PropsSI("T", "P", 101325, "Q", 1, "HEOS::" + mixes[i]));
853 CAPTURE(Tnbp_CP);
854 CHECK_NOTHROW(pchk_CP = PropsSI("P", "T", Tnbp_CP, "Q", 1, "HEOS::" + mixes[i]));
855 CAPTURE(pchk_CP);
856 CHECK_NOTHROW(Tnbp_RP = PropsSI("T", "P", 101325, "Q", 1, "REFPROP::" + mixes[i]));
857 CAPTURE(Tnbp_RP);
858 CHECK_NOTHROW(pchk_RP = PropsSI("P", "T", Tnbp_RP, "Q", 1, "REFPROP::" + mixes[i]));
859 CAPTURE(pchk_RP);
860 double diff = std::abs(Tnbp_CP / Tnbp_RP - 1);
861 CHECK(diff < 1e-2);
862 }
863 }
864}
865
866TEST_CASE("Tests for solvers in P,T flash using Water", "[flash],[PT]") {
867 SECTION("Check that T,P for saturated state yields error") {
868 double Ts, ps, rho;
869 CHECK_NOTHROW(Ts = PropsSI("T", "P", 101325, "Q", 0, "Water"));
870 CHECK(ValidNumber(Ts));
871 CHECK_NOTHROW(ps = PropsSI("P", "T", Ts, "Q", 0, "Water"));
872 CHECK(ValidNumber(ps));
873 CAPTURE(Ts);
874 CAPTURE(ps);
875 CHECK_NOTHROW(rho = PropsSI("D", "T", Ts, "P", ps, "Water"));
876 CAPTURE(rho);
877 CHECK(!ValidNumber(rho));
878 }
879 SECTION("Subcritical p slightly subcooled should be ok") {
880 double Ts, rho, dT = 1e-4;
881 CHECK_NOTHROW(Ts = PropsSI("T", "P", 101325, "Q", 0, "Water"));
882 CAPTURE(Ts);
883 CHECK(ValidNumber(Ts));
884 CAPTURE(dT);
885 CHECK_NOTHROW(rho = PropsSI("D", "T", Ts - dT, "P", 101325, "Water"));
886 CAPTURE(rho);
887 CHECK(ValidNumber(rho));
888 }
889 SECTION("Subcritical p slightly superheated should be ok") {
890 double Ts, rho, dT = 1e-4;
891 CHECK_NOTHROW(Ts = PropsSI("T", "P", 101325, "Q", 0, "Water"));
892 CAPTURE(Ts);
893 CHECK(ValidNumber(Ts));
894 CAPTURE(dT);
895 CHECK_NOTHROW(rho = PropsSI("D", "T", Ts + dT, "P", 101325, "Water"));
896 CAPTURE(rho);
897 CHECK(ValidNumber(rho));
898 }
899}
900
901TEST_CASE("Tests for solvers in P,Y flash using Water", "[flash],[PH],[PS],[PU]") {
902 double Ts, y, T2;
903 // See https://groups.google.com/forum/?fromgroups#!topic/catch-forum/mRBKqtTrITU
904 std::string Ykeys[] = {"H", "S", "U", "Hmass", "Smass", "Umass", "Hmolar", "Smolar", "Umolar"};
905 for (int i = 0; i < 9; ++i) {
906 const char* ykey = Ykeys[i].c_str();
907 std::ostringstream ss1;
908 ss1 << "Subcritical superheated P," << ykey;
909 SECTION(ss1.str(), "") {
910 double dT = 10;
911 CHECK_NOTHROW(Ts = PropsSI("T", "P", 101325, "Q", 0, "Water"));
912 CHECK(ValidNumber(Ts));
913 CAPTURE(Ts);
914 CHECK_NOTHROW(y = PropsSI(ykey, "T", Ts + dT, "P", 101325, "Water"));
915 CAPTURE(dT);
916 CAPTURE(y);
917 CHECK(ValidNumber(y));
918 CHECK_NOTHROW(T2 = PropsSI("T", ykey, y, "P", 101325, "Water"));
919 CAPTURE(CoolProp::get_global_param_string("errstring"));
920 CAPTURE(T2);
921 CHECK(ValidNumber(T2));
922 }
923 std::ostringstream ss2;
924 ss2 << "Subcritical barely superheated P," << ykey;
925 SECTION(ss2.str(), "") {
926 double dT = 1e-3;
927 CHECK_NOTHROW(Ts = PropsSI("T", "P", 101325, "Q", 0, "Water"));
928 CHECK(ValidNumber(Ts));
929 CAPTURE(Ts);
930 CHECK_NOTHROW(y = PropsSI(ykey, "T", Ts + dT, "P", 101325, "Water"));
931 CAPTURE(dT);
932 CAPTURE(y);
933 CHECK(ValidNumber(y));
934 CHECK_NOTHROW(T2 = PropsSI("T", ykey, y, "P", 101325, "Water"));
935 CAPTURE(CoolProp::get_global_param_string("errstring"));
936 CAPTURE(T2);
937 CHECK(ValidNumber(T2));
938 }
939 std::ostringstream ss3;
940 ss3 << "Subcritical subcooled P," << ykey;
941 SECTION(ss3.str(), "") {
942 double dT = -10;
943 CHECK_NOTHROW(Ts = PropsSI("T", "P", 101325, "Q", 0, "Water"));
944 CHECK(ValidNumber(Ts));
945 CAPTURE(Ts);
946 CHECK_NOTHROW(y = PropsSI(ykey, "T", Ts + dT, "P", 101325, "Water"));
947 CAPTURE(dT);
948 CAPTURE(y);
949 CHECK(ValidNumber(y));
950 CHECK_NOTHROW(T2 = PropsSI("T", ykey, y, "P", 101325, "Water"));
951 CAPTURE(CoolProp::get_global_param_string("errstring"));
952 CAPTURE(T2);
953 CHECK(ValidNumber(T2));
954 }
955 std::ostringstream ss4;
956 ss4 << "Subcritical barely subcooled P," << ykey;
957 SECTION(ss4.str(), "") {
958 double dT = -1e-3;
959 CHECK_NOTHROW(Ts = PropsSI("T", "P", 101325, "Q", 0, "Water"));
960 CHECK(ValidNumber(Ts));
961 CAPTURE(Ts);
962 CHECK_NOTHROW(y = PropsSI(ykey, "T", Ts + dT, "P", 101325, "Water"));
963 CAPTURE(dT);
964 CAPTURE(y);
965 CHECK(ValidNumber(y));
966 CHECK_NOTHROW(T2 = PropsSI("T", ykey, y, "P", 101325, "Water"));
967 CAPTURE(CoolProp::get_global_param_string("errstring"));
968 CAPTURE(T2);
969 CHECK(ValidNumber(T2));
970 }
971 std::ostringstream ss5;
972 ss5 << "Supercritical P," << ykey;
973 SECTION(ss5.str(), "") {
974 double Tc = Props1SI("Water", "Tcrit");
975 double pc = Props1SI("Water", "pcrit");
976 double p = pc * 1.3;
977 double T = Tc * 1.3;
978 CAPTURE(T);
979 CAPTURE(p);
980 CHECK(ValidNumber(T));
981 CHECK(ValidNumber(p));
982 CHECK_NOTHROW(y = PropsSI(ykey, "P", p, "T", T, "Water"));
983 CAPTURE(y);
984 CHECK(ValidNumber(y));
985 CHECK_NOTHROW(T2 = PropsSI("T", ykey, y, "P", p, "Water"));
986 CAPTURE(CoolProp::get_global_param_string("errstring"));
987 CAPTURE(T2);
988 CHECK(ValidNumber(T2));
989 }
990 std::ostringstream ss6;
991 ss6 << "Supercritical \"gas\" P," << ykey;
992 SECTION(ss6.str(), "") {
993 double Tc = Props1SI("Water", "Tcrit");
994 double pc = Props1SI("Water", "pcrit");
995 double p = pc * 0.7;
996 double T = Tc * 1.3;
997 CAPTURE(T);
998 CAPTURE(p);
999 CHECK(ValidNumber(T));
1000 CHECK(ValidNumber(p));
1001 CHECK_NOTHROW(y = PropsSI(ykey, "P", p, "T", T, "Water"));
1002 CAPTURE(y);
1003 CHECK(ValidNumber(y));
1004 CHECK_NOTHROW(T2 = PropsSI("T", ykey, y, "P", p, "Water"));
1005 CAPTURE(CoolProp::get_global_param_string("errstring"));
1006 CAPTURE(T2);
1007 CHECK(ValidNumber(T2));
1008 }
1009 std::ostringstream ss7;
1010 ss7 << "Supercritical \"liquid\" P," << ykey;
1011 SECTION(ss7.str(), "") {
1012 double Tc = Props1SI("Water", "Tcrit");
1013 double pc = Props1SI("Water", "pcrit");
1014 double p = pc * 2;
1015 double T = Tc * 0.5;
1016 CAPTURE(T);
1017 CAPTURE(p);
1018 CHECK(ValidNumber(T));
1019 CHECK(ValidNumber(p));
1020 CHECK_NOTHROW(y = PropsSI(ykey, "P", p, "T", T, "Water"));
1021 CAPTURE(y);
1022 CHECK(ValidNumber(y));
1023 CHECK_NOTHROW(T2 = PropsSI("T", ykey, y, "P", p, "Water"));
1024 CAPTURE(CoolProp::get_global_param_string("errstring"));
1025 CAPTURE(T2);
1026 CHECK(ValidNumber(T2));
1027 }
1028 }
1029}
1030
1031TEST_CASE("R134A saturation bug in dev", "[2545]"){
1032 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "R134A"));
1033 AS->update(QT_INPUTS, 1, 273);
1034 double p = AS->p();
1035 CHECK(p == Catch::Approx(291215));
1036}
1037
1038TEST_CASE("Tests for solvers in P,H flash using Propane", "[flashdups],[flash],[PH],[consistency]") {
1039 double hmolar, hmass;
1040 SECTION("5 times PH with HEOS AbstractState yields same results every time", "") {
1041 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "n-Propane"));
1042
1043 CHECK_NOTHROW(AS->update(CoolProp::PT_INPUTS, 101325, 300));
1044 hmolar = AS->hmolar();
1045 hmass = AS->hmass();
1046 CHECK_NOTHROW(AS->update(CoolProp::HmassP_INPUTS, hmass, 101325));
1047 CHECK_NOTHROW(AS->update(CoolProp::HmolarP_INPUTS, hmolar, 101325));
1048 hmolar = AS->hmolar();
1049 hmass = AS->hmass();
1050 CHECK_NOTHROW(AS->update(CoolProp::HmassP_INPUTS, hmass, 101325));
1051 CHECK_NOTHROW(AS->update(CoolProp::HmolarP_INPUTS, hmolar, 101325));
1052 hmolar = AS->hmolar();
1053 hmass = AS->hmass();
1054 CHECK_NOTHROW(AS->update(CoolProp::HmassP_INPUTS, hmass, 101325));
1055 CHECK_NOTHROW(AS->update(CoolProp::HmolarP_INPUTS, hmolar, 101325));
1056 hmolar = AS->hmolar();
1057 hmass = AS->hmass();
1058 CHECK_NOTHROW(AS->update(CoolProp::HmassP_INPUTS, hmass, 101325));
1059 CHECK_NOTHROW(AS->update(CoolProp::HmolarP_INPUTS, hmolar, 101325));
1060 hmolar = AS->hmolar();
1061 hmass = AS->hmass();
1062 CHECK_NOTHROW(AS->update(CoolProp::HmassP_INPUTS, hmass, 101325));
1063 CHECK_NOTHROW(AS->update(CoolProp::HmolarP_INPUTS, hmolar, 101325));
1064 }
1065}
1066
1067TEST_CASE("Multiple calls to state class are consistent", "[flashdups],[flash],[PH],[consistency]") {
1068 double hmolar, hmass;
1069 SECTION("3 times PH with HEOS AbstractState yields same results every time", "") {
1070 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "n-Propane"));
1071
1072 CHECK_NOTHROW(AS->update(CoolProp::PT_INPUTS, 101325, 300));
1073 hmolar = AS->hmolar();
1074 hmass = AS->hmass();
1075 CHECK_NOTHROW(AS->update(CoolProp::HmassP_INPUTS, hmass, 101325));
1076 CHECK_NOTHROW(AS->update(CoolProp::HmolarP_INPUTS, hmolar, 101325));
1077 hmolar = AS->hmolar();
1078 hmass = AS->hmass();
1079 CHECK_NOTHROW(AS->update(CoolProp::HmassP_INPUTS, hmass, 101325));
1080 CHECK_NOTHROW(AS->update(CoolProp::HmolarP_INPUTS, hmolar, 101325));
1081 hmolar = AS->hmolar();
1082 hmass = AS->hmass();
1083 CHECK_NOTHROW(AS->update(CoolProp::HmassP_INPUTS, hmass, 101325));
1084 CHECK_NOTHROW(AS->update(CoolProp::HmolarP_INPUTS, hmolar, 101325));
1085 }
1086}
1087
1088TEST_CASE("Test first partial derivatives using PropsSI", "[derivatives]") {
1089 double T = 300;
1090 SECTION("Check drhodp|T 3 ways", "") {
1091 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "n-Propane"));
1092 AS->update(CoolProp::PT_INPUTS, 101325, T);
1093
1094 double drhomolardp__T_AbstractState = AS->first_partial_deriv(CoolProp::iDmolar, CoolProp::iP, CoolProp::iT);
1095 double drhomolardp__T_PropsSI_num =
1096 (PropsSI("Dmolar", "T", T, "P", 101325 + 1e-3, "n-Propane") - PropsSI("Dmolar", "T", T, "P", 101325 - 1e-3, "n-Propane")) / (2 * 1e-3);
1097 double drhomolardp__T_PropsSI = PropsSI("d(Dmolar)/d(P)|T", "T", T, "P", 101325, "n-Propane");
1098
1099 CAPTURE(drhomolardp__T_AbstractState);
1100 CAPTURE(drhomolardp__T_PropsSI_num);
1101 CAPTURE(drhomolardp__T_PropsSI);
1102 double rel_err_exact = std::abs((drhomolardp__T_AbstractState - drhomolardp__T_PropsSI) / drhomolardp__T_PropsSI);
1103 double rel_err_approx = std::abs((drhomolardp__T_PropsSI_num - drhomolardp__T_PropsSI) / drhomolardp__T_PropsSI);
1104 CHECK(rel_err_exact < 1e-7);
1105 CHECK(rel_err_approx < 1e-7);
1106 }
1107 SECTION("Check drhodp|T 3 ways for water", "") {
1108 T = 80 + 273.15;
1109 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1110 AS->update(CoolProp::PT_INPUTS, 101325, T);
1111
1112 double drhomolardp__T_AbstractState = AS->first_partial_deriv(CoolProp::iDmolar, CoolProp::iP, CoolProp::iT);
1113 double drhomolardp__T_PropsSI_num =
1114 (PropsSI("Dmolar", "T", T, "P", 101325 + 1, "Water") - PropsSI("Dmolar", "T", T, "P", 101325 - 1, "Water")) / (2 * 1);
1115 double drhomolardp__T_PropsSI = PropsSI("d(Dmolar)/d(P)|T", "T", T, "P", 101325, "Water");
1116
1117 CAPTURE(drhomolardp__T_AbstractState);
1118 CAPTURE(drhomolardp__T_PropsSI_num);
1119 CAPTURE(drhomolardp__T_PropsSI);
1120 double rel_err_exact = std::abs((drhomolardp__T_AbstractState - drhomolardp__T_PropsSI) / drhomolardp__T_PropsSI);
1121 double rel_err_approx = std::abs((drhomolardp__T_PropsSI_num - drhomolardp__T_PropsSI) / drhomolardp__T_PropsSI);
1122 CHECK(rel_err_exact < 1e-4);
1123 CHECK(rel_err_approx < 1e-4);
1124 }
1125 SECTION("Check dpdrho|T 3 ways for water", "") {
1126 T = 80 + 273.15;
1127 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1128 AS->update(CoolProp::PT_INPUTS, 101325, T);
1129 CoolPropDbl rhomolar = AS->rhomolar();
1130 double dpdrhomolar__T_AbstractState = AS->first_partial_deriv(CoolProp::iP, CoolProp::iDmolar, CoolProp::iT);
1131 double dpdrhomolar__T_PropsSI_num =
1132 (PropsSI("P", "T", T, "Dmolar", rhomolar + 1e-3, "Water") - PropsSI("P", "T", T, "Dmolar", rhomolar - 1e-3, "Water")) / (2 * 1e-3);
1133 double dpdrhomolar__T_PropsSI = PropsSI("d(P)/d(Dmolar)|T", "T", T, "P", 101325, "Water");
1134 CAPTURE(rhomolar);
1135 CAPTURE(dpdrhomolar__T_AbstractState);
1136 CAPTURE(dpdrhomolar__T_PropsSI_num);
1137 CAPTURE(dpdrhomolar__T_PropsSI);
1138 double rel_err_exact = std::abs((dpdrhomolar__T_AbstractState - dpdrhomolar__T_PropsSI) / dpdrhomolar__T_PropsSI);
1139 double rel_err_approx = std::abs((dpdrhomolar__T_PropsSI_num - dpdrhomolar__T_PropsSI) / dpdrhomolar__T_PropsSI);
1140 CHECK(rel_err_exact < 1e-6);
1141 CHECK(rel_err_approx < 1e-6);
1142 }
1143 SECTION("Check dpdrho|T 3 ways for water using mass based", "") {
1144 T = 80 + 273.15;
1145 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1146 AS->update(CoolProp::PT_INPUTS, 101325, T);
1147 CoolPropDbl rhomass = AS->rhomass();
1148 double dpdrhomass__T_AbstractState = AS->first_partial_deriv(CoolProp::iP, CoolProp::iDmass, CoolProp::iT);
1149 double dpdrhomass__T_PropsSI_num =
1150 (PropsSI("P", "T", T, "Dmass", rhomass + 1e-3, "Water") - PropsSI("P", "T", T, "Dmass", rhomass - 1e-3, "Water")) / (2 * 1e-3);
1151 double dpdrhomass__T_PropsSI = PropsSI("d(P)/d(Dmass)|T", "T", T, "P", 101325, "Water");
1152 CAPTURE(rhomass);
1153 CAPTURE(dpdrhomass__T_AbstractState);
1154 CAPTURE(dpdrhomass__T_PropsSI_num);
1155 CAPTURE(dpdrhomass__T_PropsSI);
1156 double rel_err_exact = std::abs((dpdrhomass__T_AbstractState - dpdrhomass__T_PropsSI) / dpdrhomass__T_PropsSI);
1157 double rel_err_approx = std::abs((dpdrhomass__T_PropsSI_num - dpdrhomass__T_PropsSI) / dpdrhomass__T_PropsSI);
1158 CHECK(rel_err_exact < 1e-7);
1159 CHECK(rel_err_approx < 1e-7);
1160 }
1161 SECTION("Invalid first partial derivatives", "") {
1162 CHECK(!ValidNumber(PropsSI("d()/d(P)|T", "T", 300, "P", 101325, "n-Propane")));
1163 CHECK(!ValidNumber(PropsSI("d(Dmolar)/d()|T", "T", 300, "P", 101325, "n-Propane")));
1164 CHECK(!ValidNumber(PropsSI("d(Dmolar)/d(P)|", "T", 300, "P", 101325, "n-Propane")));
1165 CHECK(!ValidNumber(PropsSI("d(XXXX)/d(P)|T", "T", 300, "P", 101325, "n-Propane")));
1166 CHECK(!ValidNumber(PropsSI("d(Dmolar)d(P)|T", "T", 300, "P", 101325, "n-Propane")));
1167 CHECK(!ValidNumber(PropsSI("d(Dmolar)/d(P)T", "T", 300, "P", 101325, "n-Propane")));
1168 CHECK(!ValidNumber(PropsSI("d(Bvirial)/d(P)T", "T", 300, "P", 101325, "n-Propane")));
1169 CHECK(!ValidNumber(PropsSI("d(Tcrit)/d(P)T", "T", 300, "P", 101325, "n-Propane")));
1170 }
1171}
1172
1173TEST_CASE("Test second partial derivatives", "[derivatives]") {
1174 double T = 300;
1175 SECTION("Check d2pdrho2|T 3 ways", "") {
1176 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1177 double rhomolar = 60000;
1178 AS->update(CoolProp::DmolarT_INPUTS, rhomolar, T);
1179 double p = AS->p();
1180
1181 double d2pdrhomolar2__T_AbstractState =
1183 // Centered second derivative
1184 double del = 1e0;
1185 double d2pdrhomolar2__T_PropsSI_num =
1186 (PropsSI("P", "T", T, "Dmolar", rhomolar + del, "Water") - 2 * PropsSI("P", "T", T, "Dmolar", rhomolar, "Water")
1187 + PropsSI("P", "T", T, "Dmolar", rhomolar - del, "Water"))
1188 / pow(del, 2);
1189 double d2pdrhomolar2__T_PropsSI = PropsSI("d(d(P)/d(Dmolar)|T)/d(Dmolar)|T", "T", T, "Dmolar", rhomolar, "Water");
1190
1191 CAPTURE(d2pdrhomolar2__T_AbstractState);
1192 CAPTURE(d2pdrhomolar2__T_PropsSI_num);
1193 double rel_err_exact = std::abs((d2pdrhomolar2__T_AbstractState - d2pdrhomolar2__T_PropsSI) / d2pdrhomolar2__T_PropsSI);
1194 double rel_err_approx = std::abs((d2pdrhomolar2__T_PropsSI_num - d2pdrhomolar2__T_AbstractState) / d2pdrhomolar2__T_AbstractState);
1195 CHECK(rel_err_exact < 1e-5);
1196 CHECK(rel_err_approx < 1e-5);
1197 }
1198 SECTION("Valid second partial derivatives", "") {
1199 CHECK(ValidNumber(PropsSI("d(d(Hmolar)/d(P)|T)/d(T)|Dmolar", "T", 300, "P", 101325, "n-Propane")));
1200 }
1201 SECTION("Invalid second partial derivatives", "") {
1202 CHECK(!ValidNumber(PropsSI("d(d()/d(P)|T)/d()|", "T", 300, "P", 101325, "n-Propane")));
1203 CHECK(!ValidNumber(PropsSI("dd(Dmolar)/d()|T)|T", "T", 300, "P", 101325, "n-Propane")));
1204 }
1205 SECTION("Check derivatives with respect to T", "") {
1206 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Propane"));
1207 double rhomolar = 100, dT = 1e-1;
1208 AS->update(CoolProp::DmolarT_INPUTS, rhomolar, T);
1209
1210 // base state
1211 CoolPropDbl T0 = AS->T(), rhomolar0 = AS->rhomolar(), hmolar0 = AS->hmolar(), smolar0 = AS->smolar(), umolar0 = AS->umolar(), p0 = AS->p();
1212 CoolPropDbl dhdT_rho_ana = AS->first_partial_deriv(CoolProp::iHmolar, CoolProp::iT, CoolProp::iDmolar);
1213 CoolPropDbl d2hdT2_rho_ana = AS->second_partial_deriv(CoolProp::iHmolar, CoolProp::iT, CoolProp::iDmolar, CoolProp::iT, CoolProp::iDmolar);
1214 CoolPropDbl dsdT_rho_ana = AS->first_partial_deriv(CoolProp::iSmolar, CoolProp::iT, CoolProp::iDmolar);
1215 CoolPropDbl d2sdT2_rho_ana = AS->second_partial_deriv(CoolProp::iSmolar, CoolProp::iT, CoolProp::iDmolar, CoolProp::iT, CoolProp::iDmolar);
1216 CoolPropDbl dudT_rho_ana = AS->first_partial_deriv(CoolProp::iUmolar, CoolProp::iT, CoolProp::iDmolar);
1217 CoolPropDbl d2udT2_rho_ana = AS->second_partial_deriv(CoolProp::iUmolar, CoolProp::iT, CoolProp::iDmolar, CoolProp::iT, CoolProp::iDmolar);
1218 CoolPropDbl dpdT_rho_ana = AS->first_partial_deriv(CoolProp::iP, CoolProp::iT, CoolProp::iDmolar);
1219 CoolPropDbl d2pdT2_rho_ana = AS->second_partial_deriv(CoolProp::iP, CoolProp::iT, CoolProp::iDmolar, CoolProp::iT, CoolProp::iDmolar);
1220
1221 // increment T
1222 AS->update(CoolProp::DmolarT_INPUTS, rhomolar, T + dT);
1223 CoolPropDbl Tpt = AS->T(), rhomolarpt = AS->rhomolar(), hmolarpt = AS->hmolar(), smolarpt = AS->smolar(), umolarpt = AS->umolar(),
1224 ppt = AS->p();
1225 // decrement T
1226 AS->update(CoolProp::DmolarT_INPUTS, rhomolar, T - dT);
1227 CoolPropDbl Tmt = AS->T(), rhomolarmt = AS->rhomolar(), hmolarmt = AS->hmolar(), smolarmt = AS->smolar(), umolarmt = AS->umolar(),
1228 pmt = AS->p();
1229
1230 CoolPropDbl dhdT_rho_num = (hmolarpt - hmolarmt) / (2 * dT);
1231 CoolPropDbl d2hdT2_rho_num = (hmolarpt - 2 * hmolar0 + hmolarmt) / pow(dT, 2);
1232 CoolPropDbl dsdT_rho_num = (smolarpt - smolarmt) / (2 * dT);
1233 CoolPropDbl d2sdT2_rho_num = (smolarpt - 2 * smolar0 + smolarmt) / pow(dT, 2);
1234 CoolPropDbl dudT_rho_num = (umolarpt - umolarmt) / (2 * dT);
1235 CoolPropDbl d2udT2_rho_num = (umolarpt - 2 * umolar0 + umolarmt) / pow(dT, 2);
1236 CoolPropDbl dpdT_rho_num = (ppt - pmt) / (2 * dT);
1237 CoolPropDbl d2pdT2_rho_num = (ppt - 2 * p0 + pmt) / pow(dT, 2);
1238
1239 CAPTURE(format("%0.15Lg", d2pdT2_rho_ana).c_str());
1240
1241 double tol = 1e-4;
1242 CHECK(std::abs((dhdT_rho_num - dhdT_rho_ana) / dhdT_rho_ana) < tol);
1243 CHECK(std::abs((d2hdT2_rho_num - d2hdT2_rho_ana) / d2hdT2_rho_ana) < tol);
1244 CHECK(std::abs((dpdT_rho_num - dpdT_rho_ana) / dpdT_rho_ana) < tol);
1245 CHECK(std::abs((d2pdT2_rho_num - d2pdT2_rho_ana) / d2pdT2_rho_ana) < tol);
1246 CHECK(std::abs((dsdT_rho_num - dsdT_rho_ana) / dsdT_rho_ana) < tol);
1247 CHECK(std::abs((d2sdT2_rho_num - d2sdT2_rho_ana) / d2sdT2_rho_ana) < tol);
1248 CHECK(std::abs((dudT_rho_num - dudT_rho_ana) / dudT_rho_ana) < tol);
1249 CHECK(std::abs((d2udT2_rho_num - d2udT2_rho_ana) / d2udT2_rho_ana) < tol);
1250 }
1251
1252 SECTION("Check derivatives with respect to rho", "") {
1253 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Propane"));
1254 double rhomolar = 100, drho = 1e-1;
1255 AS->update(CoolProp::DmolarT_INPUTS, rhomolar, T);
1256
1257 // base state
1258 CoolPropDbl T0 = AS->T(), rhomolar0 = AS->rhomolar(), hmolar0 = AS->hmolar(), smolar0 = AS->smolar(), umolar0 = AS->umolar(), p0 = AS->p();
1259 CoolPropDbl dhdrho_T_ana = AS->first_partial_deriv(CoolProp::iHmolar, CoolProp::iDmolar, CoolProp::iT);
1260 CoolPropDbl d2hdrho2_T_ana = AS->second_partial_deriv(CoolProp::iHmolar, CoolProp::iDmolar, CoolProp::iT, CoolProp::iDmolar, CoolProp::iT);
1261 CoolPropDbl dsdrho_T_ana = AS->first_partial_deriv(CoolProp::iSmolar, CoolProp::iDmolar, CoolProp::iT);
1262 CoolPropDbl d2sdrho2_T_ana = AS->second_partial_deriv(CoolProp::iSmolar, CoolProp::iDmolar, CoolProp::iT, CoolProp::iDmolar, CoolProp::iT);
1263 CoolPropDbl dudrho_T_ana = AS->first_partial_deriv(CoolProp::iUmolar, CoolProp::iDmolar, CoolProp::iT);
1264 CoolPropDbl d2udrho2_T_ana = AS->second_partial_deriv(CoolProp::iUmolar, CoolProp::iDmolar, CoolProp::iT, CoolProp::iDmolar, CoolProp::iT);
1265 CoolPropDbl dpdrho_T_ana = AS->first_partial_deriv(CoolProp::iP, CoolProp::iDmolar, CoolProp::iT);
1266 CoolPropDbl d2pdrho2_T_ana = AS->second_partial_deriv(CoolProp::iP, CoolProp::iDmolar, CoolProp::iT, CoolProp::iDmolar, CoolProp::iT);
1267
1268 // increment rho
1269 AS->update(CoolProp::DmolarT_INPUTS, rhomolar + drho, T);
1270 CoolPropDbl Tpr = AS->T(), rhomolarpr = AS->rhomolar(), hmolarpr = AS->hmolar(), smolarpr = AS->smolar(), umolarpr = AS->umolar(),
1271 ppr = AS->p();
1272 // decrement rho
1273 AS->update(CoolProp::DmolarT_INPUTS, rhomolar - drho, T);
1274 CoolPropDbl Tmr = AS->T(), rhomolarmr = AS->rhomolar(), hmolarmr = AS->hmolar(), smolarmr = AS->smolar(), umolarmr = AS->umolar(),
1275 pmr = AS->p();
1276
1277 CoolPropDbl dhdrho_T_num = (hmolarpr - hmolarmr) / (2 * drho);
1278 CoolPropDbl d2hdrho2_T_num = (hmolarpr - 2 * hmolar0 + hmolarmr) / pow(drho, 2);
1279 CoolPropDbl dsdrho_T_num = (smolarpr - smolarmr) / (2 * drho);
1280 CoolPropDbl d2sdrho2_T_num = (smolarpr - 2 * smolar0 + smolarmr) / pow(drho, 2);
1281 CoolPropDbl dudrho_T_num = (umolarpr - umolarmr) / (2 * drho);
1282 CoolPropDbl d2udrho2_T_num = (umolarpr - 2 * umolar0 + umolarmr) / pow(drho, 2);
1283 CoolPropDbl dpdrho_T_num = (ppr - pmr) / (2 * drho);
1284 CoolPropDbl d2pdrho2_T_num = (ppr - 2 * p0 + pmr) / pow(drho, 2);
1285
1286 CAPTURE(format("%0.15Lg", d2pdrho2_T_ana).c_str());
1287
1288 double tol = 1e-4;
1289 CHECK(std::abs((dhdrho_T_num - dhdrho_T_ana) / dhdrho_T_ana) < tol);
1290 CHECK(std::abs((d2hdrho2_T_num - d2hdrho2_T_ana) / d2hdrho2_T_ana) < tol);
1291 CHECK(std::abs((dpdrho_T_num - dpdrho_T_ana) / dpdrho_T_ana) < tol);
1292 CHECK(std::abs((d2pdrho2_T_num - d2pdrho2_T_ana) / d2pdrho2_T_ana) < tol);
1293 CHECK(std::abs((dsdrho_T_num - dsdrho_T_ana) / dsdrho_T_ana) < tol);
1294 CHECK(std::abs((d2sdrho2_T_num - d2sdrho2_T_ana) / d2sdrho2_T_ana) < tol);
1295 CHECK(std::abs((dudrho_T_num - dudrho_T_ana) / dudrho_T_ana) < tol);
1296 CHECK(std::abs((d2udrho2_T_num - d2udrho2_T_ana) / d2udrho2_T_ana) < tol);
1297 }
1298 SECTION("Check second mixed partial(h,p) with respect to rho", "") {
1299 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Propane"));
1300 double dhmass = 1.0, T = 300;
1301 AS->update(CoolProp::QT_INPUTS, 0.0, T);
1302 double deriv1 = AS->first_partial_deriv(iDmass, iP, iHmass);
1303 double deriv_analyt = AS->second_partial_deriv(iDmass, iP, iHmass, iHmass, iP);
1304 double deriv_analyt2 = AS->second_partial_deriv(iDmass, iHmass, iP, iP, iHmass);
1305 AS->update(CoolProp::HmassP_INPUTS, AS->hmass() - 1, AS->p());
1306 double deriv2 = AS->first_partial_deriv(iDmass, iP, iHmass);
1307 double deriv_num = (deriv1 - deriv2) / dhmass;
1308 CAPTURE(deriv_num);
1309 CAPTURE(deriv_analyt);
1310
1311 double tol = 1e-4;
1312 CHECK(std::abs((deriv_num - deriv_analyt) / deriv_analyt) < tol);
1313 }
1314}
1315
1316TEST_CASE("REFPROP names for coolprop fluids", "[REFPROPName]") {
1317 std::vector<std::string> fluids = strsplit(CoolProp::get_global_param_string("fluids_list"), ',');
1318 for (std::size_t i = 0; i < fluids.size(); ++i) {
1319 std::ostringstream ss1;
1320 ss1 << "Check that REFPROP fluid name for fluid " << fluids[i] << " is valid";
1321 SECTION(ss1.str(), "") {
1322 std::string RPName = get_fluid_param_string(fluids[i], "REFPROPName");
1323 CHECK(!RPName.empty());
1324 CAPTURE(RPName);
1325 if (!RPName.compare("N/A")) {
1326 break;
1327 }
1328 CHECK(ValidNumber(Props1SI("REFPROP::" + RPName, "molemass")));
1329 CHECK(ValidNumber(Props1SI(RPName, "molemass")));
1330 }
1331 }
1332}
1333TEST_CASE("Backwards compatibility for REFPROP v4 fluid name convention", "[REFPROP_backwards_compatibility]") {
1334 SECTION("REFPROP-", "") {
1335 double val = Props1SI("REFPROP-Water", "Tcrit");
1336 std::string err = get_global_param_string("errstring");
1337 CAPTURE(val);
1338 CAPTURE(err);
1339 CHECK(ValidNumber(val));
1340 }
1341 SECTION("REFPROP-MIX:", "") {
1342 double val = PropsSI("T", "P", 101325, "Q", 0, "REFPROP-MIX:Methane[0.5]&Ethane[0.5]");
1343 std::string err = get_global_param_string("errstring");
1344 CAPTURE(val);
1345 CAPTURE(err);
1346 CHECK(ValidNumber(val));
1347 }
1348}
1349
1350class AncillaryFixture
1351{
1352 public:
1353 std::string name;
1354 void run_checks() {
1355 std::vector<std::string> fluids = strsplit(CoolProp::get_global_param_string("fluids_list"), ',');
1356 for (std::size_t i = 0; i < fluids.size(); ++i) {
1357 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", fluids[i]));
1358 auto* rHEOS = dynamic_cast<HelmholtzEOSMixtureBackend*>(AS.get());
1359 if (!rHEOS->is_pure()){
1360 continue;
1361 }
1362 do_sat(AS);
1363 }
1364 }
1365 void do_sat(shared_ptr<CoolProp::AbstractState>& AS) {
1366 for (double f = 0.1; f < 1; f += 0.4) {
1367 double Tc = AS->T_critical();
1368 double Tt = AS->Ttriple();
1369 double T = f * Tc + (1 - f) * Tt;
1370 name = strjoin(AS->fluid_names(), "&");
1371
1372 AS->update(CoolProp::QT_INPUTS, 0, T);
1373 check_rhoL(AS);
1374 check_pL(AS);
1375
1376 AS->update(CoolProp::QT_INPUTS, 1, T);
1377 check_rhoV(AS);
1378 check_pV(AS);
1379 }
1380 }
1381 void check_pL(const shared_ptr<CoolProp::AbstractState>& AS) {
1382 double p_EOS = AS->saturated_liquid_keyed_output(iP);
1383 double p_anc = AS->saturation_ancillary(CoolProp::iP, 0, CoolProp::iT, AS->T());
1384 double err = std::abs(p_EOS - p_anc) / p_anc;
1385 CAPTURE(name);
1386 CAPTURE("pL");
1387 CAPTURE(p_EOS);
1388 CAPTURE(p_anc);
1389 CAPTURE(AS->T());
1390 CHECK(err < 0.02);
1391 }
1392 void check_pV(const shared_ptr<CoolProp::AbstractState>& AS) {
1393 double p_EOS = AS->saturated_liquid_keyed_output(iP);
1394 double p_anc = AS->saturation_ancillary(CoolProp::iP, 1, CoolProp::iT, AS->T());
1395 double err = std::abs(p_EOS - p_anc) / p_anc;
1396 CAPTURE(name);
1397 CAPTURE("pV");
1398 CAPTURE(p_EOS);
1399 CAPTURE(p_anc);
1400 CAPTURE(AS->T());
1401 CHECK(err < 0.02);
1402 }
1403 void check_rhoL(const shared_ptr<CoolProp::AbstractState>& AS) {
1404 double rho_EOS = AS->saturated_liquid_keyed_output(iDmolar);
1405 double rho_anc = AS->saturation_ancillary(CoolProp::iDmolar, 0, CoolProp::iT, AS->T());
1406 double err = std::abs(rho_EOS - rho_anc) / rho_anc;
1407 CAPTURE("rhoL");
1408 CAPTURE(name);
1409 CAPTURE(rho_EOS);
1410 CAPTURE(rho_anc);
1411 CAPTURE(AS->T());
1412 CHECK(err < 0.03);
1413 }
1414 void check_rhoV(const shared_ptr<CoolProp::AbstractState>& AS) {
1415 double rho_EOS = AS->saturated_vapor_keyed_output(iDmolar);
1416 double rho_anc = AS->saturation_ancillary(CoolProp::iDmolar, 1, CoolProp::iT, AS->T());
1417 double err = std::abs(rho_EOS - rho_anc) / rho_anc;
1418 CAPTURE("rhoV");
1419 CAPTURE(name);
1420 CAPTURE(rho_EOS);
1421 CAPTURE(rho_anc);
1422 CAPTURE(AS->T());
1423 CHECK(err < 0.03);
1424 }
1425};
1426// Disabled because either they have a superancillary, and the ancillaries should not be used,
1427// or they are a pure fluid and superancillaries are not developed
1428//TEST_CASE_METHOD(AncillaryFixture, "Ancillary functions", "[ancillary]") {
1429// run_checks();
1430//};
1431
1432TEST_CASE("Triple point checks", "[triple_point]") {
1433 std::vector<std::string> fluids = strsplit(CoolProp::get_global_param_string("fluids_list"), ',');
1434 for (std::size_t i = 0; i < fluids.size(); ++i) {
1435 std::vector<std::string> names(1, fluids[i]);
1436 shared_ptr<CoolProp::HelmholtzEOSMixtureBackend> HEOS(new CoolProp::HelmholtzEOSMixtureBackend(names));
1437 // Skip pseudo-pure
1438 if (!HEOS->is_pure()) {
1439 continue;
1440 }
1441
1442 std::ostringstream ss1;
1443 ss1 << "Minimum saturation temperature state matches for liquid " << fluids[i];
1444 SECTION(ss1.str(), "") {
1445 REQUIRE_NOTHROW(HEOS->update(CoolProp::QT_INPUTS, 0, HEOS->Ttriple()));
1446 double p_EOS = HEOS->p();
1447 double p_sat_min_liquid = HEOS->get_components()[0].EOS().sat_min_liquid.p;
1448 double err_sat_min_liquid = std::abs(p_EOS - p_sat_min_liquid) / p_sat_min_liquid;
1449 CAPTURE(p_EOS);
1450 CAPTURE(p_sat_min_liquid);
1451 CAPTURE(err_sat_min_liquid);
1452 if (p_EOS < 1e-3) {
1453 continue;
1454 } // Skip very low pressure below 1 mPa
1455 CHECK(err_sat_min_liquid < 1e-3);
1456 }
1457 std::ostringstream ss2;
1458 ss2 << "Minimum saturation temperature state matches for vapor " << fluids[i];
1459 SECTION(ss2.str(), "") {
1460 REQUIRE_NOTHROW(HEOS->update(CoolProp::QT_INPUTS, 1, HEOS->Ttriple()));
1461
1462 double p_EOS = HEOS->p();
1463 double p_sat_min_vapor = HEOS->get_components()[0].EOS().sat_min_vapor.p;
1464 double err_sat_min_vapor = std::abs(p_EOS - p_sat_min_vapor) / p_sat_min_vapor;
1465 CAPTURE(p_EOS);
1466 CAPTURE(p_sat_min_vapor);
1467 CAPTURE(err_sat_min_vapor);
1468 if (p_EOS < 1e-3) {
1469 continue;
1470 } // Skip very low pressure below 1 mPa
1471 CHECK(err_sat_min_vapor < 1e-3);
1472 }
1473 std::ostringstream ss3;
1474 ss3 << "Minimum saturation temperature state matches for vapor " << fluids[i];
1475 SECTION(ss3.str(), "") {
1476 if (HEOS->p_triple() < 10) {
1477 continue;
1478 }
1479 REQUIRE_NOTHROW(HEOS->update(CoolProp::PQ_INPUTS, HEOS->p_triple(), 1));
1480
1481 double T_EOS = HEOS->T();
1482 double T_sat_min_vapor = HEOS->get_components()[0].EOS().sat_min_vapor.T;
1483 double err_sat_min_vapor = std::abs(T_EOS - T_sat_min_vapor);
1484 CAPTURE(T_EOS);
1485 CAPTURE(T_sat_min_vapor);
1486 CAPTURE(err_sat_min_vapor);
1487 CHECK(err_sat_min_vapor < 1e-3);
1488 }
1489 std::ostringstream ss4;
1490 ss4 << "Minimum saturation temperature state matches for liquid " << fluids[i];
1491 SECTION(ss4.str(), "") {
1492 if (HEOS->p_triple() < 10) {
1493 continue;
1494 }
1495 REQUIRE_NOTHROW(HEOS->update(CoolProp::PQ_INPUTS, HEOS->p_triple(), 0));
1496 double T_EOS = HEOS->T();
1497 double T_sat_min_vapor = HEOS->get_components()[0].EOS().sat_min_vapor.T;
1498 double err_sat_min_vapor = std::abs(T_EOS - T_sat_min_vapor);
1499 CAPTURE(T_EOS);
1500 CAPTURE(T_sat_min_vapor);
1501 CAPTURE(err_sat_min_vapor);
1502 CHECK(err_sat_min_vapor < 1e-3);
1503 }
1504 // std::ostringstream ss2;
1505 // ss2 << "Liquid density error < 3% for fluid " << fluids[i] << " at " << T << " K";
1506 // SECTION(ss2.str(), "")
1507 // {
1508 // double rho_EOS = AS->rhomolar();
1509 // double rho_anc = AS->saturation_ancillary(CoolProp::iDmolar, 0, CoolProp::iT, T);
1510 // double err = std::abs(rho_EOS-rho_anc)/rho_anc;
1511 // CAPTURE(rho_EOS);
1512 // CAPTURE(rho_anc);
1513 // CAPTURE(T);
1514 // CHECK(err < 0.03);
1515 // }
1516 // std::ostringstream ss3;
1517 // ss3 << "Vapor density error < 3% for fluid " << fluids[i] << " at " << T << " K";
1518 // SECTION(ss3.str(), "")
1519 // {
1520 // double rho_EOS = AS->rhomolar();
1521 // double rho_anc = AS->saturation_ancillary(CoolProp::iDmolar, 1, CoolProp::iT, T);
1522 // double err = std::abs(rho_EOS-rho_anc)/rho_anc;
1523 // CAPTURE(rho_EOS);
1524 // CAPTURE(rho_anc);
1525 // CAPTURE(T);
1526 // CHECK(err < 0.03);
1527 // }
1528 }
1529}
1530
1531class SatTFixture
1532{
1533 public:
1534 std::string name;
1535 double Tc;
1536 void run_checks() {
1537 std::vector<std::string> fluids = strsplit(CoolProp::get_global_param_string("fluids_list"), ',');
1538 for (std::size_t i = 0; i < fluids.size(); ++i) {
1539 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", fluids[i]));
1540 auto* rHEOS = dynamic_cast<HelmholtzEOSMixtureBackend*>(AS.get());
1541 if (!rHEOS->is_pure()){
1542 continue;
1543 }
1544 do_sat(AS);
1545 }
1546 }
1547 void do_sat(shared_ptr<CoolProp::AbstractState>& AS) {
1548 Tc = AS->T_critical();
1549 name = strjoin(AS->fluid_names(), "&");
1550 check_at_Tc(AS);
1551 double Tt = AS->Ttriple();
1552 if (AS->fluid_param_string("pure") == "true") {
1553 Tc = std::min(Tc, AS->T_reducing());
1554 }
1555 for (double j = 0.1; j > 1e-10; j /= 10) {
1556 check_QT(AS, Tc - j);
1557 }
1558 }
1559 void check_at_Tc(const shared_ptr<CoolProp::AbstractState>& AS) {
1560 CAPTURE("Check @ Tc");
1561 CAPTURE(name);
1562 CHECK_NOTHROW(AS->update(QT_INPUTS, 0, Tc));
1563 }
1564 void check_QT(const shared_ptr<CoolProp::AbstractState>& AS, double T) {
1565 std::string test_name = "Check --> Tc";
1566 CAPTURE(test_name);
1567 CAPTURE(name);
1568 CAPTURE(T);
1569 CHECK_NOTHROW(AS->update(QT_INPUTS, 0, T));
1570 }
1571};
1572TEST_CASE_METHOD(SatTFixture, "Test that saturation solvers solve all the way to T = Tc", "[sat_T_to_Tc]") {
1573 run_checks();
1574};
1575
1576TEST_CASE("Check mixtures with fluid name aliases", "[mixture_name_aliasing]") {
1577 shared_ptr<CoolProp::AbstractState> AS1, AS2;
1578 AS1.reset(CoolProp::AbstractState::factory("HEOS", "EBENZENE&P-XYLENE"));
1579 AS2.reset(CoolProp::AbstractState::factory("HEOS", "EthylBenzene&P-XYLENE"));
1580 REQUIRE(AS1->fluid_names().size() == AS2->fluid_names().size());
1581 std::size_t N = AS1->fluid_names().size();
1582 for (std::size_t i = 0; i < N; ++i) {
1583 CAPTURE(i);
1584 CHECK(AS1->fluid_names()[i] == AS2->fluid_names()[i]);
1585 }
1586}
1587
1588TEST_CASE("Predefined mixtures", "[predefined_mixtures]") {
1589 SECTION("PropsSI") {
1590 double val = PropsSI("Dmolar", "P", 101325, "T", 300, "Air.mix");
1591 std::string err = get_global_param_string("errstring");
1592 CAPTURE(val);
1593 CAPTURE(err);
1594 CHECK(ValidNumber(val));
1595 }
1596}
1597TEST_CASE("Test that reference states yield proper values using high-level interface", "[reference_states]") {
1598 struct ref_entry
1599 {
1600 std::string name;
1601 double hmass, smass;
1602 std::string in1;
1603 double val1;
1604 std::string in2;
1605 double val2;
1606 };
1607 std::string fluids[] = {"n-Propane", "R134a", "R124"};
1608 ref_entry entries[3] = {{"IIR", 200000, 1000, "T", 273.15, "Q", 0}, {"ASHRAE", 0, 0, "T", 233.15, "Q", 0}, {"NBP", 0, 0, "P", 101325, "Q", 0}};
1609 for (std::size_t i = 0; i < 3; ++i) {
1610 for (std::size_t j = 0; j < 3; ++j) {
1611 std::ostringstream ss1;
1612 ss1 << "Check state for " << fluids[i] << " for " + entries[j].name + " reference state ";
1613 SECTION(ss1.str(), "") {
1614 // First reset the reference state
1615 set_reference_stateS(fluids[i], "DEF");
1616 // Then set to desired reference state
1617 set_reference_stateS(fluids[i], entries[j].name);
1618 // Calculate the values
1619 double hmass = PropsSI("Hmass", entries[j].in1, entries[j].val1, entries[j].in2, entries[j].val2, fluids[i]);
1620 double smass = PropsSI("Smass", entries[j].in1, entries[j].val1, entries[j].in2, entries[j].val2, fluids[i]);
1621 CHECK(std::abs(hmass - entries[j].hmass) < 1e-8);
1622 CHECK(std::abs(smass - entries[j].smass) < 1e-8);
1623 // Then reset the reference state
1624 set_reference_stateS(fluids[i], "DEF");
1625 }
1626 }
1627 }
1628}
1629TEST_CASE("Test that reference states yield proper values using low-level interface", "[reference_states]") {
1630 struct ref_entry
1631 {
1632 std::string name;
1633 double hmass, smass;
1634 parameters in1;
1635 double val1;
1636 parameters in2;
1637 double val2;
1638 };
1639 std::string fluids[] = {"n-Propane", "R134a", "R124"};
1640 ref_entry entries[3] = {{"IIR", 200000, 1000, iT, 273.15, iQ, 0}, {"ASHRAE", 0, 0, iT, 233.15, iQ, 0}, {"NBP", 0, 0, iP, 101325, iQ, 0}};
1641 for (std::size_t i = 0; i < 3; ++i) {
1642 for (std::size_t j = 0; j < 3; ++j) {
1643 std::ostringstream ss1;
1644 ss1 << "Check state for " << fluids[i] << " for " + entries[j].name + " reference state ";
1645 SECTION(ss1.str(), "") {
1646 double val1, val2;
1647 input_pairs pair = generate_update_pair(entries[j].in1, entries[j].val1, entries[j].in2, entries[j].val2, val1, val2);
1648 // Generate a state instance
1649 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", fluids[i]));
1650 AS->update(pair, val1, val2);
1651 double hmass0 = AS->hmass();
1652 double smass0 = AS->smass();
1653 // First reset the reference state
1654 set_reference_stateS(fluids[i], "DEF");
1655 AS->update(pair, val1, val2);
1656 double hmass00 = AS->hmass();
1657 double smass00 = AS->smass();
1658 CHECK(std::abs(hmass00 - hmass0) < 1e-10);
1659 CHECK(std::abs(smass00 - smass0) < 1e-10);
1660
1661 // Then set to desired reference state
1662 set_reference_stateS(fluids[i], entries[j].name);
1663
1664 // Should not change existing instance
1665 AS->clear();
1666 AS->update(pair, val1, val2);
1667 double hmass1 = AS->hmass();
1668 double smass1 = AS->smass();
1669 CHECK(std::abs(hmass1 - hmass0) < 1e-10);
1670 CHECK(std::abs(smass1 - smass0) < 1e-10);
1671
1672 // New instance - should get updated reference state
1673 shared_ptr<CoolProp::AbstractState> AS2(CoolProp::AbstractState::factory("HEOS", fluids[i]));
1674 AS2->update(pair, val1, val2);
1675 double hmass2 = AS2->hmass();
1676 double smass2 = AS2->smass();
1677 CHECK(std::abs(hmass2 - entries[j].hmass) < 1e-8);
1678 CHECK(std::abs(smass2 - entries[j].smass) < 1e-8);
1679
1680 // Then reset the reference state
1681 set_reference_stateS(fluids[i], "DEF");
1682 }
1683 }
1684 }
1685}
1686
1687class FixedStateFixture
1688{
1689 public:
1690 void run_fluid(const std::string& fluid, const std::string& state, const std::string& ref_state) {
1691
1692 // Skip impossible reference states
1693 if (Props1SI("Ttriple", fluid) > 233.15 && ref_state == "ASHRAE") {
1694 return;
1695 }
1696 if (Props1SI("Tcrit", fluid) < 233.15 && ref_state == "ASHRAE") {
1697 return;
1698 }
1699 if (Props1SI("Tcrit", fluid) < 273.15 && ref_state == "IIR") {
1700 return;
1701 }
1702 if (Props1SI("Ttriple", fluid) > 273.15 && ref_state == "IIR") {
1703 return;
1704 }
1705 if (Props1SI("ptriple", fluid) > 101325 && ref_state == "NBP") {
1706 return;
1707 }
1708
1709 // First reset the reference state
1710 if (ref_state != "DEF") {
1711 set_reference_stateS(fluid, "DEF");
1712 try {
1713 // Then try to set to the specified reference state
1714 set_reference_stateS(fluid, ref_state);
1715 } catch (std::exception& e) {
1716 // Then set the reference state back to the default
1717 set_reference_stateS(fluid, "DEF");
1718 CAPTURE(e.what());
1719 REQUIRE(false);
1720 }
1721 }
1722
1723 std::ostringstream name;
1724 name << "Check state for " << state << " for " << fluid << " for reference state " << ref_state;
1725 CAPTURE(name.str());
1726
1727 std::vector<std::string> fl(1, fluid);
1728 shared_ptr<CoolProp::HelmholtzEOSMixtureBackend> HEOS(new CoolProp::HelmholtzEOSMixtureBackend(fl));
1729
1730 // Skip the saturation maxima states for pure fluids
1731 if (HEOS->is_pure() && (state == "max_sat_T" || state == "max_sat_p")) {
1732 return;
1733 }
1734
1735 // Get the state
1736 CoolProp::SimpleState _state = HEOS->calc_state(state);
1737 HEOS->specify_phase(iphase_gas); // something homogenous
1738 // Bump a tiny bit for EOS with non-analytic parts
1739 double f = 1.0;
1740 if ((fluid == "Water" || fluid == "CarbonDioxide") && (state == "reducing" || state == "critical")) {
1741 f = 1.00001;
1742 }
1743 HEOS->update(CoolProp::DmolarT_INPUTS, _state.rhomolar * f, _state.T * f);
1744 CAPTURE(_state.hmolar);
1745 CAPTURE(_state.smolar);
1746 CHECK(ValidNumber(_state.hmolar));
1747 CHECK(ValidNumber(_state.smolar));
1748 double EOS_hmolar = HEOS->hmolar();
1749 double EOS_smolar = HEOS->smolar();
1750 CAPTURE(EOS_hmolar);
1751 CAPTURE(EOS_smolar);
1752 CHECK(std::abs(EOS_hmolar - _state.hmolar) < 1e-2);
1753 CHECK(std::abs(EOS_smolar - _state.smolar) < 1e-2);
1754 // Then set the reference state back to the default
1755 set_reference_stateS(fluid, "DEF");
1756 };
1757 void run_checks() {
1758
1759 std::vector<std::string> fluids = strsplit(CoolProp::get_global_param_string("fluids_list"), ',');
1760 for (std::size_t i = 0; i < fluids.size(); ++i) {
1761 std::string ref_state[4] = {"DEF", "IIR", "ASHRAE", "NBP"};
1762 for (std::size_t j = 0; j < 4; ++j) {
1763 std::string states[] = {"hs_anchor", "reducing", "critical", "max_sat_T", "max_sat_p", "triple_liquid", "triple_vapor"};
1764 for (std::size_t k = 0; k < 7; ++k) {
1765 run_fluid(fluids[i], states[k], ref_state[j]);
1766 }
1767 }
1768 }
1769 }
1770};
1771TEST_CASE_METHOD(FixedStateFixture, "Test that enthalpies and entropies are correct for fixed states for all reference states", "[fixed_states]") {
1772 run_checks();
1773}; // !!!! check this
1774
1775TEST_CASE("Check the first partial derivatives", "[first_saturation_partial_deriv]") {
1776 const int number_of_pairs = 10;
1777 struct pair
1778 {
1779 parameters p1, p2;
1780 };
1781 pair pairs[number_of_pairs] = {{iP, iT}, {iDmolar, iT}, {iHmolar, iT}, {iSmolar, iT}, {iUmolar, iT},
1782 {iT, iP}, {iDmolar, iP}, {iHmolar, iP}, {iSmolar, iP}, {iUmolar, iP}};
1783 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "n-Propane"));
1784 for (std::size_t i = 0; i < number_of_pairs; ++i) {
1785 // See https://groups.google.com/forum/?fromgroups#!topic/catch-forum/mRBKqtTrITU
1786 std::ostringstream ss1;
1787 ss1 << "Check first partial derivative for d(" << get_parameter_information(pairs[i].p1, "short") << ")/d("
1788 << get_parameter_information(pairs[i].p2, "short") << ")|sat";
1789 SECTION(ss1.str(), "") {
1790 AS->update(QT_INPUTS, 1, 300);
1791 CoolPropDbl p = AS->p();
1792 CoolPropDbl analytical = AS->first_saturation_deriv(pairs[i].p1, pairs[i].p2);
1793 CAPTURE(analytical);
1794 CoolPropDbl numerical;
1795 if (pairs[i].p2 == iT) {
1796 AS->update(QT_INPUTS, 1, 300 + 1e-5);
1797 CoolPropDbl v1 = AS->keyed_output(pairs[i].p1);
1798 AS->update(QT_INPUTS, 1, 300 - 1e-5);
1799 CoolPropDbl v2 = AS->keyed_output(pairs[i].p1);
1800 numerical = (v1 - v2) / (2e-5);
1801 } else if (pairs[i].p2 == iP) {
1802 AS->update(PQ_INPUTS, p + 1e-2, 1);
1803 CoolPropDbl v1 = AS->keyed_output(pairs[i].p1);
1804 AS->update(PQ_INPUTS, p - 1e-2, 1);
1805 CoolPropDbl v2 = AS->keyed_output(pairs[i].p1);
1806 numerical = (v1 - v2) / (2e-2);
1807 } else {
1808 throw ValueError();
1809 }
1810 CAPTURE(numerical);
1811 CHECK(std::abs(numerical / analytical - 1) < 1e-4);
1812 }
1813 }
1814}
1815
1816TEST_CASE("Check the second saturation derivatives", "[second_saturation_partial_deriv]") {
1817 const int number_of_pairs = 5;
1818 struct pair
1819 {
1820 parameters p1, p2, p3;
1821 };
1822 pair pairs[number_of_pairs] = {{iT, iP, iP}, {iDmolar, iP, iP}, {iHmolar, iP, iP}, {iSmolar, iP, iP}, {iUmolar, iP, iP}};
1823 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "n-Propane"));
1824 for (std::size_t i = 0; i < number_of_pairs; ++i) {
1825 // See https://groups.google.com/forum/?fromgroups#!topic/catch-forum/mRBKqtTrITU
1826 std::ostringstream ss1;
1827 ss1 << "Check second saturation derivative for d2(" << get_parameter_information(pairs[i].p1, "short") << ")/d("
1828 << get_parameter_information(pairs[i].p2, "short") << ")2|sat";
1829 SECTION(ss1.str(), "") {
1830 AS->update(QT_INPUTS, 1, 300);
1831 CoolPropDbl p = AS->p();
1832 CoolPropDbl analytical = AS->second_saturation_deriv(pairs[i].p1, pairs[i].p2, pairs[i].p3);
1833 CAPTURE(analytical);
1834 CoolPropDbl numerical;
1835 if (pairs[i].p2 == iT) {
1836 throw NotImplementedError();
1837 } else if (pairs[i].p2 == iP) {
1838 AS->update(PQ_INPUTS, p + 1e-2, 1);
1839 CoolPropDbl v1 = AS->first_saturation_deriv(pairs[i].p1, pairs[i].p2);
1840 AS->update(PQ_INPUTS, p - 1e-2, 1);
1841 CoolPropDbl v2 = AS->first_saturation_deriv(pairs[i].p1, pairs[i].p2);
1842 numerical = (v1 - v2) / (2e-2);
1843 } else {
1844 throw ValueError();
1845 }
1846 CAPTURE(numerical);
1847 CHECK(std::abs(numerical / analytical - 1) < 1e-4);
1848 }
1849 }
1850}
1851
1852TEST_CASE("Check the first two-phase derivative", "[first_two_phase_deriv]") {
1853 const int number_of_pairs = 4;
1854 struct pair
1855 {
1856 parameters p1, p2, p3;
1857 };
1858 pair pairs[number_of_pairs] = {{iDmass, iP, iHmass}, {iDmolar, iP, iHmolar}, {iDmolar, iHmolar, iP}, {iDmass, iHmass, iP}};
1859 shared_ptr<CoolProp::HelmholtzEOSBackend> AS(new CoolProp::HelmholtzEOSBackend("n-Propane"));
1860 for (std::size_t i = 0; i < number_of_pairs; ++i) {
1861 // See https://groups.google.com/forum/?fromgroups#!topic/catch-forum/mRBKqtTrITU
1862 std::ostringstream ss1;
1863 ss1 << "for (" << get_parameter_information(pairs[i].p1, "short") << ", " << get_parameter_information(pairs[i].p2, "short") << ", "
1864 << get_parameter_information(pairs[i].p3, "short") << ")";
1865 SECTION(ss1.str(), "") {
1866 AS->update(QT_INPUTS, 0.3, 300);
1867 CoolPropDbl numerical;
1868 CoolPropDbl analytical = AS->first_two_phase_deriv(pairs[i].p1, pairs[i].p2, pairs[i].p3);
1869 CAPTURE(analytical);
1870
1871 CoolPropDbl out1, out2;
1872 CoolPropDbl v2base, v3base;
1873 v2base = AS->keyed_output(pairs[i].p2);
1874 v3base = AS->keyed_output(pairs[i].p3);
1875 CoolPropDbl v2plus = v2base * 1.001;
1876 CoolPropDbl v2minus = v2base * 0.999;
1877 CoolProp::input_pairs input_pair1 = generate_update_pair(pairs[i].p2, v2plus, pairs[i].p3, v3base, out1, out2);
1878 AS->update(input_pair1, out1, out2);
1879 CoolPropDbl v1 = AS->keyed_output(pairs[i].p1);
1880 CoolProp::input_pairs input_pair2 = generate_update_pair(pairs[i].p2, v2minus, pairs[i].p3, v3base, out1, out2);
1881 AS->update(input_pair2, out1, out2);
1882 CoolPropDbl v2 = AS->keyed_output(pairs[i].p1);
1883
1884 numerical = (v1 - v2) / (v2plus - v2minus);
1885 CAPTURE(numerical);
1886 CHECK(std::abs(numerical / analytical - 1) < 1e-4);
1887 }
1888 }
1889}
1890
1891TEST_CASE("Check the second two-phase derivative", "[second_two_phase_deriv]") {
1892 SECTION("d2rhodhdp", "") {
1893 shared_ptr<CoolProp::HelmholtzEOSBackend> AS(new CoolProp::HelmholtzEOSBackend("n-Propane"));
1894 AS->update(QT_INPUTS, 0.3, 300);
1895 CoolPropDbl analytical = AS->second_two_phase_deriv(iDmolar, iHmolar, iP, iP, iHmolar);
1896 CAPTURE(analytical);
1897 CoolPropDbl pplus = AS->p() * 1.001, pminus = AS->p() * 0.999, h = AS->hmolar();
1898 AS->update(HmolarP_INPUTS, h, pplus);
1899 CoolPropDbl v1 = AS->first_two_phase_deriv(iDmolar, iHmolar, iP);
1900 AS->update(HmolarP_INPUTS, h, pminus);
1901 CoolPropDbl v2 = AS->first_two_phase_deriv(iDmolar, iHmolar, iP);
1902 CoolPropDbl numerical = (v1 - v2) / (pplus - pminus);
1903 CAPTURE(numerical);
1904 CHECK(std::abs(numerical / analytical - 1) < 1e-6);
1905 }
1906 SECTION("d2rhodhdp using mass", "") {
1907 shared_ptr<CoolProp::HelmholtzEOSBackend> AS(new CoolProp::HelmholtzEOSBackend("n-Propane"));
1908 AS->update(QT_INPUTS, 0.3, 300);
1909 CoolPropDbl analytical = AS->second_two_phase_deriv(iDmass, iHmass, iP, iP, iHmass);
1910 CAPTURE(analytical);
1911 CoolPropDbl pplus = AS->p() * 1.001, pminus = AS->p() * 0.999, h = AS->hmass();
1912 AS->update(HmassP_INPUTS, h, pplus);
1913 CoolPropDbl v1 = AS->first_two_phase_deriv(iDmass, iHmass, iP);
1914 AS->update(HmassP_INPUTS, h, pminus);
1915 CoolPropDbl v2 = AS->first_two_phase_deriv(iDmass, iHmass, iP);
1916 CoolPropDbl numerical = (v1 - v2) / (pplus - pminus);
1917 CAPTURE(numerical);
1918 CHECK(std::abs(numerical / analytical - 1) < 1e-6);
1919 }
1920}
1921
1922TEST_CASE("Check the first two-phase derivative using splines", "[first_two_phase_deriv_splined]") {
1923 const int number_of_pairs = 4;
1924 struct pair
1925 {
1926 parameters p1, p2, p3;
1927 };
1928 pair pairs[number_of_pairs] = {{iDmass, iP, iHmass}, {iDmolar, iP, iHmolar}, {iDmolar, iHmolar, iP}, {iDmass, iHmass, iP}};
1929 shared_ptr<CoolProp::HelmholtzEOSBackend> AS(new CoolProp::HelmholtzEOSBackend("n-Propane"));
1930 for (std::size_t i = 0; i < number_of_pairs; ++i) {
1931 // See https://groups.google.com/forum/?fromgroups#!topic/catch-forum/mRBKqtTrITU
1932 std::ostringstream ss1;
1933 ss1 << "for (" << get_parameter_information(pairs[i].p1, "short") << ", " << get_parameter_information(pairs[i].p2, "short") << ", "
1934 << get_parameter_information(pairs[i].p3, "short") << ")";
1935 SECTION(ss1.str(), "") {
1936 AS->update(QT_INPUTS, 0.2, 300);
1937 CoolPropDbl numerical;
1938 CoolPropDbl analytical = AS->first_two_phase_deriv_splined(pairs[i].p1, pairs[i].p2, pairs[i].p3, 0.3);
1939 CAPTURE(analytical);
1940
1941 CoolPropDbl out1, out2;
1942 CoolPropDbl v2base, v3base;
1943 v2base = AS->keyed_output(pairs[i].p2);
1944 v3base = AS->keyed_output(pairs[i].p3);
1945 CoolPropDbl v2plus = v2base * 1.00001;
1946 CoolPropDbl v2minus = v2base * 0.99999;
1947
1948 CoolProp::input_pairs input_pair1 = generate_update_pair(pairs[i].p2, v2plus, pairs[i].p3, v3base, out1, out2);
1949 AS->update(input_pair1, out1, out2);
1950 CoolPropDbl v1 = AS->first_two_phase_deriv_splined(pairs[i].p1, pairs[i].p1, pairs[i].p1, 0.3);
1951
1952 CoolProp::input_pairs input_pair2 = generate_update_pair(pairs[i].p2, v2minus, pairs[i].p3, v3base, out1, out2);
1953 AS->update(input_pair2, out1, out2);
1954 CoolPropDbl v2 = AS->first_two_phase_deriv_splined(pairs[i].p1, pairs[i].p1, pairs[i].p1, 0.3);
1955
1956 numerical = (v1 - v2) / (v2plus - v2minus);
1957 CAPTURE(numerical);
1958 CHECK(std::abs(numerical / analytical - 1) < 1e-8);
1959 }
1960 }
1961}
1962
1963TEST_CASE("Check the phase flags", "[phase]") {
1964 SECTION("subcooled liquid") {
1965 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1966 AS->update(PT_INPUTS, 101325, 300);
1967 CHECK(AS->phase() == iphase_liquid);
1968 }
1969 SECTION("superheated gas") {
1970 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1971 AS->update(PT_INPUTS, 101325, 400);
1972 CHECK(AS->phase() == iphase_gas);
1973 }
1974 SECTION("supercritical gas") {
1975 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1976 AS->update(PT_INPUTS, 1e5, 800);
1977 CHECK(AS->phase() == iphase_supercritical_gas);
1978 }
1979 SECTION("supercritical liquid") {
1980 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1981 AS->update(PT_INPUTS, 1e8, 500);
1982 CHECK(AS->phase() == iphase_supercritical_liquid);
1983 }
1984 SECTION("supercritical") {
1985 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
1986 AS->update(PT_INPUTS, 1e8, 800);
1987 CHECK(AS->phase() == iphase_supercritical);
1988 }
1989}
1990
1991TEST_CASE("Check the changing of reducing function constants", "[reducing]") {
1992 double z0 = 0.2;
1993 std::vector<double> z(2);
1994 z[0] = z0;
1995 z[1] = 1 - z[0];
1996 shared_ptr<CoolProp::AbstractState> AS1(CoolProp::AbstractState::factory("HEOS", "Methane&Ethane"));
1997 shared_ptr<CoolProp::AbstractState> AS2(CoolProp::AbstractState::factory("HEOS", "Methane&Ethane"));
1998 AS1->set_mole_fractions(z);
1999 AS2->set_mole_fractions(z);
2000 std::vector<CoolProp::CriticalState> pts1 = AS1->all_critical_points();
2001 double gammaT = AS2->get_binary_interaction_double(0, 1, "gammaT");
2002 AS2->set_binary_interaction_double(0, 1, "gammaT", gammaT * 0.7);
2003 std::vector<CoolProp::CriticalState> pts2 = AS2->all_critical_points();
2004 double Tdiff = abs(pts2[0].T - pts1[0].T);
2005 CHECK(Tdiff > 1e-3); // Make sure that it actually got the change to the interaction parameters
2006}
2007
2008TEST_CASE("Check the PC-SAFT pressure function", "[pcsaft_pressure]") {
2009 double p = 101325.;
2010 double p_calc = CoolProp::PropsSI("P", "T", 320., "Dmolar", 9033.114359706229, "PCSAFT::TOLUENE");
2011 CHECK(abs((p_calc / p) - 1) < 1e-5);
2012
2013 p_calc = CoolProp::PropsSI("P", "T", 274., "Dmolar", 55530.40675319466, "PCSAFT::WATER");
2014 CHECK(abs((p_calc/p) - 1) < 1e-5);
2015
2016 p_calc = CoolProp::PropsSI("P", "T", 305., "Dmolar", 16965.6697209874,"PCSAFT::ACETIC ACID");
2017 CHECK(abs((p_calc/p) - 1) < 1e-5);
2018
2019 p_calc = CoolProp::PropsSI("P", "T", 240., "Dmolar", 15955.50941242, "PCSAFT::DIMETHYL ETHER");
2020 CHECK(abs((p_calc/p) - 1) < 1e-5);
2021
2022 p_calc = CoolProp::PropsSI("P", "T", 298.15, "Dmolar", 9368.903838750752, "PCSAFT::METHANOL[0.055]&CYCLOHEXANE[0.945]");
2023 CHECK(abs((p_calc/p) - 1) < 1e-5);
2024
2025 p_calc = CoolProp::PropsSI("P", "T", 298.15, "Dmolar", 55757.07260200306, "PCSAFT::Na+[0.010579869455908]&Cl-[0.010579869455908]&WATER[0.978840261088184]");
2026 CHECK(abs((p_calc/p) - 1) < 1e-5);
2027
2028 p = CoolProp::PropsSI("P", "T", 100., "Q", 0, "PCSAFT::PROPANE");
2029 double rho = 300;
2030 double phase = CoolProp::PropsSI("Phase", "T", 100., "Dmolar", rho, "PCSAFT::PROPANE");
2031 CHECK(phase == get_phase_index("phase_twophase"));
2032 p_calc = CoolProp::PropsSI("P", "T", 100, "Dmolar", rho, "PCSAFT::PROPANE");
2033 CHECK(abs((p_calc / p) - 1) < 1e-4);
2034}
2035
2036TEST_CASE("Check the PC-SAFT density function", "[pcsaft_density]") {
2037 double den = 9033.114209728405;
2038 double den_calc = CoolProp::PropsSI("Dmolar", "T|liquid", 320., "P", 101325., "PCSAFT::TOLUENE");
2039 CHECK(abs((den_calc / den) - 1) < 1e-5);
2040
2041 den = 55530.40512318346;
2042 den_calc = CoolProp::PropsSI("Dmolar", "T|liquid", 274., "P", 101325, "PCSAFT::WATER");
2043 CHECK(abs((den_calc / den) - 1) < 1e-5);
2044
2045 den = 17240.; // source: DIPPR correlation
2046 den_calc = CoolProp::PropsSI("Dmolar","T|liquid",305.,"P",101325,"PCSAFT::ACETIC ACID");
2047 CHECK(abs((den_calc/den) - 1) < 2e-2);
2048
2049 den = 15955.509146801696;
2050 den_calc = CoolProp::PropsSI("Dmolar","T|liquid",240.,"P",101325,"PCSAFT::DIMETHYL ETHER");
2051 CHECK(abs((den_calc/den) - 1) < 1e-5);
2052
2053 den = 9368.90368306872;
2054 den_calc = CoolProp::PropsSI("Dmolar", "T|liquid", 298.15, "P", 101325, "PCSAFT::METHANOL[0.055]&CYCLOHEXANE[0.945]");
2055 CHECK(abs((den_calc / den) - 1) < 1e-5);
2056
2057 den = 55740.157290833515;
2058 den_calc = CoolProp::PropsSI("Dmolar", "T|liquid", 298.15, "P", 101325, "PCSAFT::Na+[0.010579869455908]&Cl-[0.010579869455908]&WATER[0.978840261088184]");
2059 CHECK(abs((den_calc / den) - 1) < 1e-5);
2060
2061 den = 16621.0;
2062 den_calc = CoolProp::PropsSI("Dmolar", "T|liquid", 85.525, "P", 1.7551e-4, "PCSAFT::PROPANE");
2063 CHECK(abs((den_calc / den) - 1) < 1e-2);
2064
2065 den = 1.9547e-7;
2066 den_calc = CoolProp::PropsSI("Dmolar", "T|gas", 85.525, "P", 1.39e-4, "PCSAFT::PROPANE");
2067 CHECK(abs((den_calc / den) - 1) < 1e-2);
2068
2069 den = 11346.0;
2070 den_calc = CoolProp::PropsSI("Dmolar", "T|liquid", 293, "P", 833240, "PCSAFT::PROPANE");
2071 CHECK(abs((den_calc / den) - 1) < 1e-2);
2072
2073 den = 623.59;
2074 den_calc = CoolProp::PropsSI("Dmolar","T|liquid", 430,"P", 2000000, "PCSAFT::PROPANE");
2075 CHECK(abs((den_calc/den) - 1) < 1e-2);
2076}
2077
2078TEST_CASE("Check the PC-SAFT residual enthalpy function", "[pcsaft_enthalpy]") {
2079 double h = -36809.962122036086;
2080 double h_calc = CoolProp::PropsSI("Hmolar_residual", "T|liquid", 325., "Dmolar", 8983.377722763931, "PCSAFT::TOLUENE");
2081 CHECK(abs((h_calc / h) - 1) < 1e-5);
2082
2083 h = -362.6832840695562;
2084 h_calc = CoolProp::PropsSI("Hmolar_residual", "T|gas", 325., "Dmolar", 39.44490805826904, "PCSAFT::TOLUENE");
2085 CHECK(abs((h_calc / h) - 1) < 1e-5);
2086
2087 h = -38925.302571456035;
2088 h_calc = CoolProp::PropsSI("Hmolar_residual","T|liquid",325.,"Dmolar", 16655.853047419932,"PCSAFT::ACETIC ACID");
2089 CHECK(abs((h_calc/h) - 1) < 1e-5);
2090
2091 h = -15393.870073928741;
2092 h_calc = CoolProp::PropsSI("Hmolar_residual", "T|gas", 325., "Dmolar", 85.70199446609787, "PCSAFT::ACETIC ACID");
2093 CHECK(abs((h_calc / h) - 1) < 1e-5);
2094
2095 h = -18242.128097841978;
2096 h_calc = CoolProp::PropsSI("Hmolar_residual","T|liquid",325.,"Dmolar", 13141.475980937616,"PCSAFT::DIMETHYL ETHER");
2097 CHECK(abs((h_calc/h) - 1) < 1e-5);
2098
2099 h = -93.819615173017169;
2100 h_calc = CoolProp::PropsSI("Hmolar_residual","T|gas",325.,"Dmolar", 37.963459290365265,"PCSAFT::DIMETHYL ETHER");
2101 CHECK(abs((h_calc/h) - 1) < 1e-5);
2102
2103 // checks based on values from the HEOS backend
2104 h = CoolProp::PropsSI("Hmolar_residual", "T|liquid", 325., "Dmolar", 8983.377722763931, "HEOS::TOLUENE");
2105 h_calc = CoolProp::PropsSI("Hmolar_residual", "T|liquid", 325., "Dmolar", 8983.377722763931, "PCSAFT::TOLUENE");
2106 CHECK(abs(h_calc - h) < 600.);
2107
2108 h = CoolProp::PropsSI("Hmolar_residual", "T|gas", 325., "Dmolar", 39.44490805826904, "HEOS::TOLUENE");
2109 h_calc = CoolProp::PropsSI("Hmolar_residual", "T|gas", 325., "Dmolar", 39.44490805826904, "PCSAFT::TOLUENE");
2110 CHECK(abs(h_calc - h) < 600.);
2111
2112 h = CoolProp::PropsSI("Hmolar_residual", "T|liquid", 325., "Dmolar", 54794.1, "HEOS::WATER");
2113 h_calc = CoolProp::PropsSI("Hmolar_residual", "T|liquid", 325., "Dmolar", 54794.1, "PCSAFT::WATER");
2114 CHECK(abs(h_calc - h) < 600.);
2115
2116 h = CoolProp::PropsSI("Hmolar_residual", "T|gas", 325., "Dmolar", 0.370207, "HEOS::WATER");
2117 h_calc = CoolProp::PropsSI("Hmolar_residual", "T|gas", 325., "Dmolar", 0.370207, "PCSAFT::WATER");
2118 CHECK(abs(h_calc - h) < 600.);
2119}
2120
2121TEST_CASE("Check the PC-SAFT residual entropy function", "[pcsaft_entropy]") {
2122 // checks based on values from working PC-SAFT code
2123 double s = -50.81694890352192;
2124 double s_calc = CoolProp::PropsSI("Smolar_residual", "T|liquid", 325., "Dmolar", 8983.377722763931, "PCSAFT::TOLUENE");
2125 CHECK(abs((s_calc / s) - 1) < 1e-5);
2126
2127 s = -0.2929618646219797;
2128 s_calc = CoolProp::PropsSI("Smolar_residual", "T|gas", 325., "Dmolar", 39.44490805826904, "PCSAFT::TOLUENE");
2129 CHECK(abs((s_calc / s) - 1) < 1e-5);
2130
2131 s = -47.42736805661422;
2132 s_calc = CoolProp::PropsSI("Smolar_residual","T|liquid",325.,"Dmolar", 16655.853047419932,"PCSAFT::ACETIC ACID");
2133 CHECK(abs((s_calc/s) - 1) < 1e-5);
2134
2135 s = -34.0021996393859;
2136 s_calc = CoolProp::PropsSI("Smolar_residual", "T|gas", 325., "Dmolar", 85.70199446609787, "PCSAFT::ACETIC ACID");
2137 CHECK(abs((s_calc / s) - 1) < 1e-5);
2138
2139 s = -26.42525828195748;
2140 s_calc = CoolProp::PropsSI("Smolar_residual","T|liquid",325.,"Dmolar", 13141.475980937616,"PCSAFT::DIMETHYL ETHER");
2141 CHECK(abs((s_calc/s) - 1) < 1e-5);
2142
2143 s = -0.08427662199177874;
2144 s_calc = CoolProp::PropsSI("Smolar_residual","T|gas",325.,"Dmolar", 37.963459290365265,"PCSAFT::DIMETHYL ETHER");
2145 CHECK(abs((s_calc/s) - 1) < 1e-5);
2146
2147 // checks based on values from the HEOS backend
2148 s = CoolProp::PropsSI("Smolar_residual", "T|liquid", 325., "Dmolar", 8983.377722763931, "HEOS::TOLUENE");
2149 s_calc = CoolProp::PropsSI("Smolar_residual", "T|liquid", 325., "Dmolar", 8983.377722763931, "PCSAFT::TOLUENE");
2150 CHECK(abs(s_calc - s) < 3.);
2151
2152 s = CoolProp::PropsSI("Smolar_residual", "T|gas", 325., "Dmolar", 39.44490805826904, "HEOS::TOLUENE");
2153 s_calc = CoolProp::PropsSI("Smolar_residual", "T|gas", 325., "Dmolar", 39.44490805826904, "PCSAFT::TOLUENE");
2154 CHECK(abs(s_calc - s) < 3.);
2155
2156 s = CoolProp::PropsSI("Smolar_residual", "T|liquid", 325., "Dmolar", 54794.1, "HEOS::WATER");
2157 s_calc = CoolProp::PropsSI("Smolar_residual", "T|liquid", 325., "Dmolar", 54794.1, "PCSAFT::WATER");
2158 CHECK(abs(s_calc - s) < 3.);
2159
2160 s = CoolProp::PropsSI("Smolar_residual", "T|gas", 325., "Dmolar", 0.370207, "HEOS::WATER");
2161 s_calc = CoolProp::PropsSI("Smolar_residual", "T|gas", 325., "Dmolar", 0.370207, "PCSAFT::WATER");
2162 CHECK(abs(s_calc - s) < 3.);
2163}
2164
2165TEST_CASE("Check the PC-SAFT residual gibbs energy function", "[pcsaft_gibbs]") {
2166 double g = -5489.471870270737;
2167 double g_calc = CoolProp::PropsSI("Gmolar_residual", "T|liquid", 325., "Dmolar", 8983.377872003264, "PCSAFT::TOLUENE");
2168 CHECK(abs((g_calc / g) - 1) < 1e-5);
2169
2170 g = -130.63592030187894;
2171 g_calc = CoolProp::PropsSI("Gmolar_residual", "T|gas", 325., "Dmolar", 39.44491269148218, "PCSAFT::TOLUENE");
2172 CHECK(abs((g_calc / g) - 1) < 1e-5);
2173
2174 g = -7038.128334100866;
2175 g_calc = CoolProp::PropsSI("Gmolar_residual","T|liquid",325.,"Dmolar", 16655.853314424,"PCSAFT::ACETIC ACID");
2176 CHECK(abs((g_calc/g) - 1) < 1e-5);
2177
2178 g = -2109.4916554917604;
2179 g_calc = CoolProp::PropsSI("Gmolar_residual", "T|gas", 325., "Dmolar", 85.70199446609787, "PCSAFT::ACETIC ACID");
2180 CHECK(abs((g_calc / g) - 1) < 1e-5);
2181
2182 g = 6178.973332408309;
2183 g_calc = CoolProp::PropsSI("Gmolar_residual","T|liquid",325.,"Dmolar", 13141.47619110254,"PCSAFT::DIMETHYL ETHER");
2184 CHECK(abs((g_calc/g) - 1) < 1e-5);
2185
2186 g = -33.038791982589615;
2187 g_calc = CoolProp::PropsSI("Gmolar_residual","T|gas",325.,"Dmolar", 37.96344503293008,"PCSAFT::DIMETHYL ETHER");
2188 CHECK(abs((g_calc/g) - 1) < 1e-5);
2189}
2190
2191TEST_CASE("Check vapor pressures calculated using PC-SAFT", "[pcsaft_vapor_pressure]") {
2192 double vp = 3290651.18080112;
2193 double vp_calc = CoolProp::PropsSI("P", "T", 572.6667, "Q", 0, "PCSAFT::TOLUENE");
2194 CHECK(abs((vp_calc / vp) - 1) < 1e-3);
2195
2196 vp = 66917.67387203;
2197 vp_calc = CoolProp::PropsSI("P", "T", 362, "Q", 0, "PCSAFT::WATER");
2198 CHECK(abs((vp_calc / vp) - 1) < 1e-3);
2199
2200 vp = 190061.78088909;
2201 vp_calc = CoolProp::PropsSI("P", "T", 413.5385, "Q", 0, "PCSAFT::ACETIC ACID");
2202 CHECK(abs((vp_calc / vp) - 1) < 1e-3);
2203
2204 vp = 622763.506195;
2205 vp_calc = CoolProp::PropsSI("P","T", 300.,"Q", 0,"PCSAFT::DIMETHYL ETHER");
2206 CHECK(abs((vp_calc/vp) - 1) < 1e-3);
2207
2208 // This test doesn't pass yet. The flash algorithm for the PC-SAFT backend is not yet robust enough.
2209 // vp = 1.7551e-4;
2210 // vp_calc = CoolProp::PropsSI("P","T",85.525,"Q", 0, "PCSAFT::PROPANE");
2211 // CHECK(abs((vp_calc/vp) - 1) < 0.1);
2212
2213 vp = 8.3324e5;
2214 vp_calc = CoolProp::PropsSI("P", "T", 293, "Q", 0, "PCSAFT::PROPANE");
2215 CHECK(abs((vp_calc / vp) - 1) < 0.01);
2216
2217 vp = 42.477e5;
2218 vp_calc = CoolProp::PropsSI("P", "T", 369.82, "Q", 0, "PCSAFT::PROPANE");
2219 CHECK(abs((vp_calc / vp) - 1) < 0.01);
2220}
2221
2222TEST_CASE("Check PC-SAFT interaction parameter functions", "[pcsaft_binary_interaction]") {
2223 std::string CAS_water = get_fluid_param_string("WATER", "CAS");
2224 std::string CAS_aacid = "64-19-7";
2225 set_mixture_binary_pair_pcsaft(CAS_water, CAS_aacid, "kij", -0.127);
2226 CHECK(atof(get_mixture_binary_pair_pcsaft(CAS_water, CAS_aacid, "kij").c_str()) == -0.127);
2227}
2228
2229TEST_CASE("Check bubble pressures calculated using PC-SAFT", "[pcsaft_bubble_pressure]")
2230{
2231 double vp = 1816840.45112607; // source: H.-M. Lin, H. M. Sebastian, J. J. Simnick, and K.-C. Chao, “Gas-liquid equilibrium in binary mixtures of methane with N-decane, benzene, and toluene,” J. Chem. Eng. Data, vol. 24, no. 2, pp. 146–149, Apr. 1979.
2232 double vp_calc = CoolProp::PropsSI("P", "T", 421.05, "Q", 0, "PCSAFT::METHANE[0.0252]&BENZENE[0.9748]");
2233 CHECK(abs((vp_calc / vp) - 1) < 1e-3);
2234
2235 // This test doesn't pass yet. The flash algorithm for the PC-SAFT backend cannot yet get a good enough initial guess value for the k values (vapor-liquid distribution ratios)
2236 // vp = 6691000; // source: Hughes TJ, Kandil ME, Graham BF, Marsh KN, Huang SH, May EF. Phase equilibrium measurements of (methane+ benzene) and (methane+ methylbenzene) at temperatures from (188 to 348) K and pressures to 13 MPa. The Journal of Chemical Thermodynamics. 2015 Jun 1;85:141-7.
2237 // vp_calc = CoolProp::PropsSI("P", "T", 348.15, "Q", 0, "PCSAFT::METHANE[0.119]&BENZENE[0.881]");
2238 // CHECK(abs((vp_calc/vp) - 1) < 1e-3);
2239
2240 vp = 96634.2439079;
2241 vp_calc = CoolProp::PropsSI("P", "T", 327.48, "Q", 0, "PCSAFT::METHANOL[0.3]&CYCLOHEXANE[0.7]");
2242 CHECK(abs((vp_calc / vp) - 1) < 1e-3);
2243
2244 // set binary interaction parameter
2245 std::string CAS_water = get_fluid_param_string("WATER", "CAS");
2246 std::string CAS_aacid = "64-19-7";
2247 try {
2248 get_mixture_binary_pair_pcsaft(CAS_water, CAS_aacid, "kij");
2249 }
2250 catch (...) {
2251 set_mixture_binary_pair_pcsaft(CAS_water, CAS_aacid, "kij", -0.127);
2252 }
2253
2254 vp = 274890.39985918;
2255 vp_calc = CoolProp::PropsSI("P", "T", 403.574, "Q", 0, "PCSAFT::WATER[0.9898662364]&ACETIC ACID[0.0101337636]");
2256 CHECK(abs((vp_calc / vp) - 1) < 1e-2);
2257
2258 vp = 72915.92217342;
2259 vp_calc = CoolProp::PropsSI("P", "T", 372.774, "Q", 0, "PCSAFT::WATER[0.2691800943]&ACETIC ACID[0.7308199057]");
2260 CHECK(abs((vp_calc / vp) - 1) < 2e-2);
2261
2262 vp = 2387.42669687;
2263 vp_calc = CoolProp::PropsSI("P","T", 298.15,"Q", 0,"PCSAFT::Na+[0.0907304774758426]&Cl-[0.0907304774758426]&WATER[0.818539045048315]");
2264 CHECK(abs((vp_calc/vp) - 1) < 0.23);
2265}
2266
2267TEST_CASE("Check bubble temperatures calculated using PC-SAFT", "[pcsaft_bubble_temperature]") {
2268 double t = 572.6667;
2269 double t_calc = CoolProp::PropsSI("T", "P", 3290651.18080112, "Q", 0, "PCSAFT::TOLUENE");
2270 CHECK(abs((t_calc / t) - 1) < 1e-3);
2271
2272 t = 362;
2273 t_calc = CoolProp::PropsSI("T", "P", 66917.67387203, "Q", 0, "PCSAFT::WATER");
2274 CHECK(abs((t_calc / t) - 1) < 1e-3);
2275
2276 t = 413.5385;
2277 t_calc = CoolProp::PropsSI("T", "P", 190061.78088909, "Q", 0, "PCSAFT::ACETIC ACID");
2278 CHECK(abs((t_calc / t) - 1) < 1e-3);
2279
2280 t = 300.;
2281 t_calc = CoolProp::PropsSI("T", "P", 623027.07850612, "Q", 0, "PCSAFT::DIMETHYL ETHER");
2282 CHECK(abs((t_calc / t) - 1) < 1e-3);
2283
2284 // This test doesn't pass yet. The flash algorithm for the PC-SAFT backend cannot yet get a good enough initial guess value for the k values (vapor-liquid distribution ratios)
2285 // t = 421.05;
2286 // t_calc = CoolProp::PropsSI("T", "P", 1816840.45112607, "Q", 0, "PCSAFT::METHANE[0.0252]&BENZENE[0.9748]");
2287 // CHECK(abs((t_calc/t) - 1) < 1e-3);
2288
2289 t = 327.48;
2290 t_calc = CoolProp::PropsSI("T", "P", 96634.2439079, "Q", 0, "PCSAFT::METHANOL[0.3]&CYCLOHEXANE[0.7]");
2291 CHECK(abs((t_calc / t) - 1) < 1e-3);
2292
2293 // set binary interaction parameter, if not already set
2294 std::string CAS_water = get_fluid_param_string("WATER","CAS");
2295 std::string CAS_aacid = "64-19-7";
2296 try {
2297 get_mixture_binary_pair_pcsaft(CAS_water, CAS_aacid, "kij");
2298 }
2299 catch (...) {
2300 set_mixture_binary_pair_pcsaft(CAS_water, CAS_aacid, "kij", -0.127);
2301 }
2302
2303 t = 403.574;
2304 t_calc = CoolProp::PropsSI("T", "P", 274890.39985918, "Q", 0, "PCSAFT::WATER[0.9898662364]&ACETIC ACID[0.0101337636]");
2305 CHECK(abs((t_calc / t) - 1) < 1e-3);
2306
2307 t = 372.774;
2308 t_calc = CoolProp::PropsSI("T", "P", 72915.92217342, "Q", 0, "PCSAFT::WATER[0.2691800943]&ACETIC ACID[0.7308199057]");
2309 CHECK(abs((t_calc / t) - 1) < 2e-3);
2310
2311 t = 298.15;
2312 t_calc = CoolProp::PropsSI("T", "P", 2387.42669687, "Q", 0, "PCSAFT::Na+[0.0907304774758426]&Cl-[0.0907304774758426]&WATER[0.818539045048315]");
2313 CHECK(abs((t_calc / t) - 1) < 1e-2);
2314}
2315
2316TEST_CASE("Github issue #2470", "[pureflash]") {
2317 auto fluide = "Nitrogen";
2318 auto enthalpy = 67040.57857; //J / kg
2319 auto pressure = 3368965.046; //Pa
2320 std::shared_ptr<CoolProp::AbstractState> AS(AbstractState::factory("HEOS", fluide));
2321 AS->update(PQ_INPUTS, pressure, 1);
2322 auto Ts = AS->T();
2323 AS->specify_phase(iphase_gas);
2324 CHECK_NOTHROW(AS->update(PT_INPUTS, pressure, Ts));
2325 AS->unspecify_phase();
2326 CHECK_NOTHROW(AS->update(HmassP_INPUTS, enthalpy, pressure));
2327 auto Tfinal = AS->T();
2328 CHECK(Tfinal > AS->T_critical());
2329}
2330
2331TEST_CASE("Github issue #2467", "[pureflash]") {
2332 auto fluide = "Pentane";
2333 std::shared_ptr<CoolProp::AbstractState> AS(AbstractState::factory("HEOS", fluide));
2334 AS->update(CoolProp::QT_INPUTS, 1, 353.15);
2335 double p1 = AS->p();
2336 AS->update(CoolProp::QT_INPUTS, 1, 433.15);
2337 double p2 = AS->p();
2338 AS->update(CoolProp::PT_INPUTS, p1, 393.15);
2339 double s1 = AS->smass();
2340 CHECK_NOTHROW(AS->update(CoolProp::PSmass_INPUTS, p2, s1));
2341}
2342
2343TEST_CASE("Github issue #1870", "[pureflash]") {
2344 auto fluide = "Pentane";
2345 std::shared_ptr<CoolProp::AbstractState> AS(AbstractState::factory("HEOS", fluide));
2346 CHECK_NOTHROW(AS->update(CoolProp::PSmass_INPUTS, 1000000, 1500));
2347}
2348
2349TEST_CASE("Github issue #2447", "[2447]") {
2350 double pvap = PropsSI("P", "T", 360 + 273.15, "Q", 0, "INCOMP::S800");
2351 double err = std::abs(pvap / 961e3 - 1);
2352 CHECK(err < 0.05);
2353}
2354
2355TEST_CASE("Check methanol EOS matches REFPROP 10", "[2538]"){
2356 auto TNBP_RP = PropsSI("T", "P", 101325, "Q", 0, "REFPROP::METHANOL");
2357 auto TNBP_CP = PropsSI("T", "P", 101325, "Q", 0, "HEOS::METHANOL");
2358 CHECK(TNBP_RP == Catch::Approx(TNBP_CP).epsilon(1e-6));
2359
2360 auto rhoL_RP = PropsSI("D", "T", 400, "Q", 0, "REFPROP::METHANOL");
2361 auto rhoL_CP = PropsSI("D", "T", 400, "Q", 0, "HEOS::METHANOL");
2362 CHECK(rhoL_RP == Catch::Approx(rhoL_CP).epsilon(1e-12));
2363
2364 auto cp0_RP = PropsSI("CP0MOLAR", "T", 400, "Dmolar", 1e-5, "REFPROP::METHANOL");
2365 auto cp0_CP = PropsSI("CP0MOLAR", "T", 400, "Dmolar", 1e-5, "HEOS::METHANOL");
2366 CHECK(cp0_RP == Catch::Approx(cp0_CP).epsilon(1e-4));
2367
2368}
2369
2370
2371TEST_CASE("Check phase determination for PC-SAFT backend", "[pcsaft_phase]") {
2372 double den = 9033.114209728405;
2373 double den_calc = CoolProp::PropsSI("Dmolar", "T", 320., "P", 101325., "PCSAFT::TOLUENE");
2374 CHECK(abs((den_calc / den) - 1) < 1e-2);
2375 double phase = CoolProp::PropsSI("Phase", "T", 320., "P", 101325., "PCSAFT::TOLUENE");
2376 CHECK(phase == get_phase_index("phase_liquid"));
2377
2378 den = 0.376013;
2379 den_calc = CoolProp::PropsSI("Dmolar", "T", 320., "P", 1000., "PCSAFT::TOLUENE");
2380 CHECK(abs((den_calc / den) - 1) < 1e-2);
2381 phase = CoolProp::PropsSI("Phase", "T", 320., "P", 1000., "PCSAFT::TOLUENE");
2382 CHECK(phase == get_phase_index("phase_gas"));
2383}
2384
2385TEST_CASE("Check that indexes for mixtures are assigned correctly, especially for the association term", "[pcsaft_indexes]")
2386{
2387 // The tests are performed by adding parameters for extra compounds that actually
2388 // are not present in the system and ensuring that the properties of the fluid do not change.
2389
2390 // Binary mixture: water-acetic acid
2391 // set binary interaction parameter, if not already set
2392 std::string CAS_water = get_fluid_param_string("WATER","CAS");
2393 std::string CAS_aacid = "64-19-7";
2394 try {
2395 get_mixture_binary_pair_pcsaft(CAS_water, CAS_aacid, "kij");
2396 }
2397 catch (...) {
2398 set_mixture_binary_pair_pcsaft(CAS_water, CAS_aacid, "kij", -0.127);
2399 }
2400
2401 double t = 413.5385;
2402 double rho = 15107.481234283325;
2403 double p = CoolProp::PropsSI("P", "T", t, "Dmolar", rho, "PCSAFT::ACETIC ACID"); // only parameters for acetic acid
2404 double p_extra = CoolProp::PropsSI("P", "T", t, "Dmolar", rho, "PCSAFT::ACETIC ACID[1.0]&WATER[0]"); // same composition, but with mixture parameters
2405 CHECK(abs((p_extra - p)/ p * 100) < 1e-1);
2406
2407 // Binary mixture: water-furfural
2408 t = 400; // K
2409 // p = 34914.37778265716; // Pa
2410 rho = 10657.129498214763;
2411 p = CoolProp::PropsSI("P", "T", t, "Dmolar", rho, "PCSAFT::FURFURAL"); // only parameters for furfural
2412 p_extra = CoolProp::PropsSI("P", "T", t, "Dmolar", rho, "PCSAFT::WATER[0]&FURFURAL[1.0]"); // same composition, but with mixture of components
2413 CHECK(abs((p_extra - p)/ p * 100) < 1e-1);
2414
2415 // Mixture: NaCl in water with random 4th component
2416 t = 298.15; // K
2417 // p = 3153.417688548272; // Pa
2418 rho = 55320.89616248148;
2419 p = CoolProp::PropsSI("P", "T", t, "Dmolar", rho, "PCSAFT::WATER"); // only parameters for water
2420 p_extra = CoolProp::PropsSI("P", "T", t, "Dmolar", rho, "PCSAFT::Na+[0]&Cl-[0]&WATER[1.0]&DIMETHOXYMETHANE[0]"); // same composition, but with mixture of components
2421 CHECK(abs((p_extra - p)/ p * 100) < 1e-1);
2422}
2423
2425class SuperAncillaryOnFixture{
2426private:
2427 const configuration_keys m_key = ENABLE_SUPERANCILLARIES;
2428 const bool initial_value;
2429public:
2430 SuperAncillaryOnFixture() : initial_value(CoolProp::get_config_bool(m_key)) {
2431 CoolProp::set_config_bool(m_key, true);
2432 }
2433 ~SuperAncillaryOnFixture(){
2434 CoolProp::set_config_bool(m_key, initial_value);
2435 }
2436};
2437
2439class SuperAncillaryOffFixture{
2440private:
2441 const configuration_keys m_key = ENABLE_SUPERANCILLARIES;
2442 const bool initial_value;
2443public:
2444 SuperAncillaryOffFixture() : initial_value(CoolProp::get_config_bool(m_key)) {
2445 CoolProp::set_config_bool(m_key, false);
2446 }
2447 ~SuperAncillaryOffFixture(){
2448 CoolProp::set_config_bool(m_key, initial_value);
2449 }
2450};
2451
2452
2453TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check superancillary for water", "[superanc]") {
2454
2455 auto json = nlohmann::json::parse(get_fluid_param_string("WATER", "JSON"))[0].at("EOS")[0].at("SUPERANCILLARY");
2457 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
2458 shared_ptr<CoolProp::AbstractState> IF97(CoolProp::AbstractState::factory("IF97", "Water"));
2459 auto& rHEOS = *dynamic_cast<HelmholtzEOSMixtureBackend*>(AS.get());
2460 BENCHMARK("HEOS.clear()"){
2461 return rHEOS.clear();
2462 };
2463 BENCHMARK("HEOS rho(T)"){
2464 return AS->update(QT_INPUTS, 1.0, 300.0);
2465 };
2466 BENCHMARK("HEOS update_QT_pure_superanc(Q,T)"){
2467 return rHEOS.update_QT_pure_superanc(1.0, 300.0);
2468 };
2469 BENCHMARK("superanc rho(T)"){
2470 return anc.eval_sat(300.0, 'D', 1);
2471 };
2472 BENCHMARK("IF97 rho(T)"){
2473 return IF97->update(QT_INPUTS, 1.0, 300.0);
2474 };
2475
2476 double Tmin = AS->get_fluid_parameter_double(0, "SUPERANC::Tmin");
2477 double Tc = AS->get_fluid_parameter_double(0, "SUPERANC::Tcrit_num");
2478 double pmin = AS->get_fluid_parameter_double(0, "SUPERANC::pmin");
2479 double pmax = AS->get_fluid_parameter_double(0, "SUPERANC::pmax");
2480
2481 CHECK_THROWS(AS->get_fluid_parameter_double(1, "SUPERANC::pmax"));
2482
2483 BENCHMARK("HEOS rho(p)"){
2484 return AS->update(PQ_INPUTS, 101325, 1.0);
2485 };
2486 BENCHMARK("superanc T(p)"){
2487 return anc.get_T_from_p(101325);
2488 };
2489 BENCHMARK("IF97 rho(p)"){
2490 return IF97->update(PQ_INPUTS, 101325, 1.0);
2491 };
2492}
2493
2494TEST_CASE_METHOD(SuperAncillaryOffFixture, "Check superancillary-like calculations with superancillary disabled for water", "[superanc]") {
2495
2496 auto json = nlohmann::json::parse(get_fluid_param_string("WATER", "JSON"))[0].at("EOS")[0].at("SUPERANCILLARY");
2498 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
2499 shared_ptr<CoolProp::AbstractState> IF97(CoolProp::AbstractState::factory("IF97", "Water"));
2500 auto& approxrhoL = anc.get_approx1d('D', 0);
2501
2502 BENCHMARK("HEOS rho(T)"){
2503 return AS->update(QT_INPUTS, 1.0, 300.0);
2504 };
2505 BENCHMARK("superanc rho(T)"){
2506 return anc.eval_sat(300.0, 'D', 1);
2507 };
2508 BENCHMARK("superanc rho(T) with expansion directly"){
2509 return approxrhoL.eval(300.0);
2510 };
2511 BENCHMARK("superanc get_index rho(T)"){
2512 return approxrhoL.get_index(300.0);
2513 };
2514 BENCHMARK("IF97 rho(T)"){
2515 return IF97->update(QT_INPUTS, 1.0, 300.0);
2516 };
2517
2518 BENCHMARK("HEOS rho(p)"){
2519 return AS->update(PQ_INPUTS, 101325, 1.0);
2520 };
2521 BENCHMARK("superanc T(p)"){
2522 return anc.get_T_from_p(101325);
2523 };
2524 BENCHMARK("IF97 rho(p)"){
2525 return IF97->update(PQ_INPUTS, 101325, 1.0);
2526 };
2527}
2528
2529
2530TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check superancillary functions are available for all pure fluids", "[ancillary]") {
2531 for (auto & fluid : strsplit(CoolProp::get_global_param_string("fluids_list"), ',')){
2532 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", fluid));
2533 auto& rHEOS = *dynamic_cast<HelmholtzEOSMixtureBackend*>(AS.get());
2534 if (rHEOS.is_pure()){
2535 CAPTURE(fluid);
2536 CHECK_NOTHROW(rHEOS.update_QT_pure_superanc(1, rHEOS.T_critical()*0.9999));
2537 }
2538 }
2539};
2540
2541TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check out of bound for superancillary", "[superanc]") {
2542 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
2543 CHECK_THROWS(AS->update(PQ_INPUTS, 100000000001325, 1.0));
2544 CHECK_THROWS(AS->update(QT_INPUTS, 1.0, 1000000));
2545}
2546
2547TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check throws for R410A", "[superanc]") {
2548 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "R410A"));
2549 auto& rHEOS = *dynamic_cast<HelmholtzEOSMixtureBackend*>(AS.get());
2550 CHECK_THROWS(rHEOS.update_QT_pure_superanc(1.0, 300.0));
2551}
2552
2553TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check throws for REFPROP", "[superanc]") {
2554 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("REFPROP", "WATER"));
2555 CHECK_THROWS(AS->update_QT_pure_superanc(1.0, 300.0));
2556}
2557
2558TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check Tc & pc", "[superanccrit]") {
2559 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
2560 set_config_bool(ENABLE_SUPERANCILLARIES, true);
2561 auto TcSA = AS->T_critical();
2562 auto pcSA = AS->p_critical();
2563 auto rhocSA = AS->rhomolar_critical();
2564 set_config_bool(ENABLE_SUPERANCILLARIES, false);
2565 auto TcnonSA = AS->T_critical();
2566 auto pcnonSA = AS->p_critical();
2567 auto rhocnonSA = AS->rhomolar_critical();
2568 CHECK(TcSA != TcnonSA);
2569 CHECK(pcSA != pcnonSA);
2570 CHECK(rhocSA != rhocnonSA);
2571}
2572
2573TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check h_fg", "[superanc]") {
2574 shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
2575 CHECK_THROWS(AS->saturated_vapor_keyed_output(iHmolar) - AS->saturated_liquid_keyed_output(iHmolar));
2576 AS->update_QT_pure_superanc(1, 300);
2577 CHECK_NOTHROW(AS->saturated_vapor_keyed_output(iHmolar) - AS->saturated_liquid_keyed_output(iHmolar));
2578}
2579
2580TEST_CASE_METHOD(SuperAncillaryOnFixture, "Benchmarking caching options", "[caching]") {
2581 std::array<double, 16> buf15; buf15.fill(0.0);
2582 std::array<double, 100> buf100; buf100.fill(0.0);
2583 std::array<bool, 100> bool100; bool100.fill(false);
2584 std::vector<CachedElement> cache100(100);
2585 for (auto i = 0; i < cache100.size(); ++i){ cache100[i] = _HUGE; }
2586
2587 std::vector<std::optional<double>> opt100(100);
2588 for (auto i = 0; i < opt100.size(); ++i){ opt100[i] = _HUGE; }
2589
2590 BENCHMARK("memset array15 w/ 0"){
2591 std::memset(buf15.data(), 0, sizeof(buf15));
2592 return buf15;
2593 };
2594 BENCHMARK("std::fill_n array15"){
2595 std::fill_n(buf15.data(), 15, _HUGE);
2596 return buf15;
2597 };
2598 BENCHMARK("std::fill array15"){
2599 std::fill(buf15.begin(), buf15.end(), _HUGE);
2600 return buf15;
2601 };
2602 BENCHMARK("array15.fill()"){
2603 buf15.fill(_HUGE);
2604 return buf15;
2605 };
2606 BENCHMARK("memset array100 w/ 0"){
2607 memset(buf100.data(), 0, sizeof(buf100));
2608 return buf100;
2609 };
2610 BENCHMARK("memset bool100 w/ 0"){
2611 memset(bool100.data(), false, sizeof(bool100));
2612 return buf100;
2613 };
2614 BENCHMARK("std::fill_n array100"){
2615 std::fill_n(buf100.data(), 100, _HUGE);
2616 return buf100;
2617 };
2618 BENCHMARK("fill array100"){
2619 buf100.fill(_HUGE);
2620 return buf100;
2621 };
2622 BENCHMARK("fill cache100"){
2623 for (auto i = 0; i < cache100.size(); ++i){ cache100[i] = _HUGE; }
2624 return cache100;
2625 };
2626 BENCHMARK("fill opt100"){
2627 for (auto i = 0; i < opt100.size(); ++i){ opt100[i] = _HUGE; }
2628 return opt100;
2629 };
2630}
2631
2632/*
2633TEST_CASE("Test that HS solver works for a few fluids", "[HS_solver]")
2634{
2635 std::vector<std::string> fluids; fluids.push_back("Propane"); fluids.push_back("D4"); fluids.push_back("Water");
2636 for (std::size_t i = 0; i < fluids.size(); ++i)
2637 {
2638 std::vector<std::string> fl(1,fluids[i]);
2639 shared_ptr<CoolProp::HelmholtzEOSMixtureBackend> HEOS(new CoolProp::HelmholtzEOSMixtureBackend(fl));
2640 for (double p = HEOS->p_triple()*10; p < HEOS->pmax(); p *= 10)
2641 {
2642 double Tmin = HEOS->Ttriple();
2643 double Tmax = HEOS->Tmax();
2644 for (double T = Tmin + 1; T < Tmax-1; T += 10)
2645 {
2646 std::ostringstream ss;
2647 ss << "Check HS for " << fluids[i] << " for T=" << T << ", p=" << p;
2648 SECTION(ss.str(),"")
2649 {
2650 CHECK_NOTHROW(HEOS->update(PT_INPUTS, p, T));
2651 std::ostringstream ss1;
2652 ss1 << "h=" << HEOS->hmolar() << ", s=" << HEOS->smolar();
2653 SECTION(ss1.str(),"")
2654 {
2655 CAPTURE(T);
2656 CAPTURE(p);
2657 CAPTURE(HEOS->hmolar());
2658 CAPTURE(HEOS->smolar());
2659 CHECK_NOTHROW(HEOS->update(HmolarSmolar_INPUTS, HEOS->hmolar(), HEOS->smolar()));
2660 double Terr = HEOS->T()- T;
2661 CAPTURE(Terr);
2662 CHECK(std::abs(Terr) < 1e-6);
2663 }
2664 }
2665 }
2666 }
2667 }
2668}
2669*/
2670#endif