CoolProp 8.0.0
An open-source fluid property and humid air property database
UNIFAC.cpp
Go to the documentation of this file.
1#include "UNIFAC.h"
2
4 for (const auto& isgi : unique_groups) {
5 for (const auto& jsgi : unique_groups) {
6 if (jsgi >= isgi) {
7 continue;
8 }
9 int mgi1 = static_cast<int>(m_sgi_to_mgi.find(isgi)->second);
10 int mgi2 = static_cast<int>(m_sgi_to_mgi.find(jsgi)->second);
11 // Insert in normal order
12 std::pair<std::pair<int, int>, UNIFACLibrary::InteractionParameters> m_pair(std::pair<int, int>(mgi1, mgi2),
13 library.get_interaction_parameters(mgi1, mgi2));
14 interaction.insert(m_pair);
15 // Insert in backwards order
16 if (mgi1 != mgi2) {
17 std::pair<std::pair<int, int>, UNIFACLibrary::InteractionParameters> m_pair(std::pair<int, int>(mgi2, mgi1),
18 library.get_interaction_parameters(mgi2, mgi1));
19 interaction.insert(m_pair);
20 }
21 }
22 }
23}
24
25void UNIFAC::UNIFACMixture::set_interaction_parameter(const std::size_t mgi1, const std::size_t mgi2, const std::string& parameter,
26 const double value) {
27 if (parameter == "aij") {
28 this->interaction[std::pair<int, int>(static_cast<int>(mgi1), static_cast<int>(mgi2))].a_ij = value;
29 } else if (parameter == "bij") {
30 this->interaction[std::pair<int, int>(static_cast<int>(mgi1), static_cast<int>(mgi2))].b_ij = value;
31 } else if (parameter == "cij") {
32 this->interaction[std::pair<int, int>(static_cast<int>(mgi1), static_cast<int>(mgi2))].c_ij = value;
33 } else {
34 throw CoolProp::ValueError(format("I don't know what to do with parameter [%s]", parameter.c_str()));
35 }
36}
37double UNIFAC::UNIFACMixture::get_interaction_parameter(const std::size_t mgi1, const std::size_t mgi2, const std::string& parameter) {
38 auto it = this->interaction.find(std::pair<int, int>(static_cast<int>(mgi1), static_cast<int>(mgi2)));
39 if (it == this->interaction.end()) {
40 throw CoolProp::ValueError(format("Unable to match mgi-mgi pair: [%d,%d]", static_cast<int>(mgi1), static_cast<int>(mgi1)));
41 } else {
42 if (parameter == "aij") {
43 return it->second.a_ij;
44 } else if (parameter == "bij") {
45 return it->second.b_ij;
46 } else if (parameter == "cij") {
47 return it->second.c_ij;
48 } else {
49 throw CoolProp::ValueError(format("I don't know what to do with parameter [%s]", parameter.c_str()));
50 }
51 }
52}
53
55void UNIFAC::UNIFACMixture::set_mole_fractions(const std::vector<double>& z) {
56 // // If the vector fractions are the same as last ones, don't do anything and return
57 // if (!mole_fractions.empty() && maxvectordiff(z, mole_fractions) < 1e-15){
58 // return;
59 // }
60 this->mole_fractions = z;
61 if (this->N != z.size()) {
62 throw CoolProp::ValueError("Size of molar fraction do not match number of components.");
63 }
64
65 std::map<std::size_t, double>&Xg = m_Xg, &thetag = m_thetag;
66 Xg.clear();
67 thetag.clear();
68
69 // Iterate over the fluids
70 double X_summer = 0;
71 for (std::size_t i = 0; i < this->mole_fractions.size(); ++i) {
72 X_summer += this->mole_fractions[i] * pure_data[i].group_count;
73 }
75 for (const auto& sgi : unique_groups) {
76 double X = 0;
77 // Iterate over the fluids
78 for (std::size_t i = 0; i < this->mole_fractions.size(); ++i) {
79 X += this->mole_fractions[i] * group_count(i, sgi);
80 }
81 Xg.emplace(sgi, X);
82 }
84 for (auto& [key, val] : Xg) {
85 val /= X_summer;
86 //printf("X_{%d}: %g\n", key, val);
87 }
88 double theta_summer = 0;
89 for (const auto& sgi : unique_groups) {
90 double cont = Xg.find(sgi)->second * m_Q.find(sgi)->second;
91 theta_summer += cont;
92 thetag.emplace(sgi, cont);
93 }
95 for (auto& [key, val] : thetag) {
96 val /= theta_summer;
97 //printf("theta_{%d}: %g\n", key, val);
98 }
99}
100
101double UNIFAC::UNIFACMixture::Psi(std::size_t sgi1, std::size_t sgi2) const {
102
103 if (this->interaction.size() == 0) {
104 throw CoolProp::ValueError("interaction parameters for UNIFAC not yet set");
105 }
106 std::size_t mgi1 = m_sgi_to_mgi.find(sgi1)->second;
107 std::size_t mgi2 = m_sgi_to_mgi.find(sgi2)->second;
108 if (mgi1 == mgi2) {
109 return 1;
110 } else {
111 auto it = this->interaction.find(std::pair<int, int>(static_cast<int>(mgi1), static_cast<int>(mgi2)));
112 if (it != this->interaction.end()) {
113 return exp(-(it->second.a_ij / this->m_T + it->second.b_ij + it->second.c_ij * this->m_T));
114 } else {
116 format("Could not match mgi[%d]-mgi[%d] interaction in UNIFAC", static_cast<int>(mgi1), static_cast<int>(mgi2)));
117 }
118 }
119}
120
121std::size_t UNIFAC::UNIFACMixture::group_count(std::size_t i, std::size_t sgi) const {
122 const UNIFACLibrary::Component& c = components[i];
123 for (const auto& cg : c.groups) {
124 if (cg.group.sgi == sgi) {
125 return cg.count;
126 }
127 }
128 return 0;
129}
130
131double UNIFAC::UNIFACMixture::theta_pure(std::size_t i, std::size_t sgi) const {
132 return pure_data[i].theta.find(sgi)->second;
133}
134
136 // // Check whether you are using exactly the same temperature as last time
137 // if (static_cast<bool>(_T) && std::abs(static_cast<double>(_T) - T) < 1e-15 {
138 // //
139 // return;
140 // }
141 this->m_T = T;
142
143 if (this->mole_fractions.empty()) {
144 throw CoolProp::ValueError("mole fractions must be set before calling set_temperature");
145 }
146
147 // Compute Psi once for the different calls
148 for (const auto& k : unique_groups) {
149 for (const auto& m : unique_groups) {
150 Psi_[std::pair<std::size_t, std::size_t>(k, m)] = Psi(k, m);
151 }
152 }
153
154 for (std::size_t i = 0; i < this->mole_fractions.size(); ++i) {
155 const UNIFACLibrary::Component& c = components[i];
156 for (std::size_t k = 0; k < c.groups.size(); ++k) {
157 double Q = c.groups[k].group.Q_k;
158 int sgik = c.groups[k].group.sgi;
159 double sum1 = 0;
160 for (const auto& group : c.groups) {
161 int sgim = group.group.sgi;
162 sum1 += theta_pure(i, sgim) * Psi_.find(std::pair<std::size_t, std::size_t>(sgim, sgik))->second;
163 }
164 // sum1 > 0: c.groups is the set of groups present in component i, theta_pure
165 // returns the (positive) surface-area fraction of each, and Psi = exp(-a/T) > 0.
166 // cppcheck cannot prove this symbolically.
167 // cppcheck-suppress invalidFunctionArg
168 double s = 1 - log(sum1);
169 for (std::size_t m = 0; m < c.groups.size(); ++m) {
170 int sgim = c.groups[m].group.sgi;
171 double sum2 = 0;
172 for (const auto& group : c.groups) {
173 int sgin = group.group.sgi;
174 sum2 += theta_pure(i, sgin) * Psi_.find(std::pair<std::size_t, std::size_t>(sgin, sgim))->second;
175 }
176 s -= theta_pure(i, sgim) * Psi_.find(std::pair<std::size_t, std::size_t>(sgik, sgim))->second / sum2;
177 }
178 pure_data[i].lnGamma[sgik] = Q * s;
179 //printf("ln(Gamma)^(%d)_{%d}: %g\n", static_cast<int>(i + 1), sgik, Q*s);
180 }
181 }
182
183 std::map<std::size_t, double>&thetag = m_thetag, &lnGammag = m_lnGammag;
184 lnGammag.clear();
185
186 for (const auto& ksgi : unique_groups) {
187 double sum1 = 0;
188 for (const auto& msgi : unique_groups) {
189 sum1 += thetag.find(msgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(msgi, ksgi))->second;
190 }
191 double s = 1 - log(sum1);
192 for (const auto& msgi : unique_groups) {
193 double sum3 = 0;
194 for (const auto& nsgi : unique_groups) {
195 sum3 += thetag.find(nsgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(nsgi, msgi))->second;
196 }
197 s -= thetag.find(msgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(ksgi, msgi))->second / sum3;
198 }
199 lnGammag.emplace(ksgi, m_Q.find(ksgi)->second * s);
200 //printf("log(Gamma)_{%d}: %g\n", itk->sgi, itk->Q_k*s);
201 }
202 _T = m_T;
203}
204double UNIFAC::UNIFACMixture::ln_gamma_R(const double tau, std::size_t i, std::size_t itau) {
205 if (itau == 0) {
206 set_temperature(T_r / tau);
207 double summer = 0;
208 for (const auto& sgi : unique_groups) {
209 std::size_t count = group_count(i, sgi);
210 if (count > 0) {
211 summer += count * (m_lnGammag.find(sgi)->second - pure_data[i].lnGamma.find(sgi)->second);
212 }
213 }
214 //printf("log(gamma)_{%d}: %g\n", i+1, summer);
215 return summer;
216 } else {
217 double dtau = 0.01 * tau;
218 return (ln_gamma_R(tau + dtau, i, itau - 1) - ln_gamma_R(tau - dtau, i, itau - 1)) / (2 * dtau);
219 }
220}
221void UNIFAC::UNIFACMixture::activity_coefficients(double tau, const std::vector<double>& z, std::vector<double>& gamma) {
222 if (this->N != z.size()) {
223 throw CoolProp::ValueError("Size of molar fraction do not match number of components.");
224 }
225 std::vector<double> r(N), q(N), l(N), phi(N), theta(N), ln_Gamma_C(N);
226 double summerzr = 0, summerzq = 0, summerzl = 0;
227 for (std::size_t i = 0; i < N; ++i) {
228 double summerr = 0, summerq = 0;
229 const UNIFACLibrary::Component& c = components[i];
230 for (const auto& cg : c.groups) {
231 summerr += cg.count * cg.group.R_k;
232 summerq += cg.count * cg.group.Q_k;
233 }
234 r[i] = summerr;
235 q[i] = summerq;
236 summerzr += z[i] * r[i];
237 summerzq += z[i] * q[i];
238 }
239 for (std::size_t i = 0; i < N; ++i) {
240 phi[i] = z[i] * r[i] / summerzr;
241 theta[i] = z[i] * q[i] / summerzq;
242 l[i] = 10.0 / 2.0 * (r[i] - q[i]) - (r[i] - 1);
243 summerzl += z[i] * l[i];
244 }
245 for (std::size_t i = 0; i < N; ++i) {
246 ln_Gamma_C[i] = log(phi[i] / z[i]) + 10.0 / 2.0 * q[i] * log(theta[i] / phi[i]) + l[i] - phi[i] / z[i] * summerzl;
247 gamma[i] = exp(ln_gamma_R(tau, i, 0) + ln_Gamma_C[i]);
248 }
249}
250
253 components.push_back(comp);
254 for (const auto& cg : comp.groups) {
255 m_sgi_to_mgi.emplace(cg.group.sgi, cg.group.mgi);
256 }
257}
258
259void UNIFAC::UNIFACMixture::set_components(const std::string& identifier_type, const std::vector<std::string>& identifiers) {
260 components.clear();
261 N = identifiers.size();
262 if (identifier_type == "name") {
263 // Iterate over the provided names
264 for (const auto& id : identifiers) {
265 // Get and add the component
266 UNIFACLibrary::Component c = library.get_component("name", id);
267 add_component(c);
268 }
269 } else {
270 throw CoolProp::ValueError("Cannot understand identifier_type");
271 }
273 set_pure_data();
274}
275
278 pure_data.clear();
279 unique_groups.clear();
280 m_Q.clear();
281 for (std::size_t i = 0; i < N; ++i) {
282 const UNIFACLibrary::Component& c = components[i];
283 ComponentData cd;
284 double summerxq = 0;
285 cd.group_count = 0;
286 for (const auto& cg : c.groups) {
287 auto x = static_cast<double>(cg.count);
288 auto theta = static_cast<double>(cg.count * cg.group.Q_k);
289 cd.X.emplace(cg.group.sgi, x);
290 cd.theta.emplace(cg.group.sgi, theta);
291 cd.group_count += cg.count;
292 summerxq += x * cg.group.Q_k;
293 unique_groups.insert(cg.group.sgi);
294 m_Q.emplace(cg.group.sgi, cg.group.Q_k);
295 }
297 for (auto& [key, val] : cd.X) {
298 val /= cd.group_count;
299 //printf("X^(%d)_{%d}: %g\n", static_cast<int>(i + 1), static_cast<int>(key), val);
300 }
302 for (auto& [key, val] : cd.theta) {
303 val /= summerxq;
304 //printf("theta^(%d)_{%d}: %g\n", static_cast<int>(i+1), static_cast<int>(key), val);
305 }
306 pure_data.push_back(cd);
307 }
308}
309
311void UNIFAC::UNIFACMixture::set_Q_k(const size_t sgi, const double value) {
312 for (std::size_t i = 0; i < N; ++i) {
313 for (auto& group : components[i].groups) {
314 if (group.group.sgi == sgi) {
315 group.group.Q_k = value;
316 }
317 }
318 }
319
321 set_pure_data();
322}
323
325double UNIFAC::UNIFACMixture::get_Q_k(const size_t sgi) const {
326 for (std::size_t i = 0; i < N; ++i) {
327 for (const auto& group : components[i].groups) {
328 if (group.group.sgi == sgi) {
329 return group.group.Q_k;
330 }
331 }
332 }
333 throw CoolProp::ValueError("Could not get Q_k");
334}