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