CoolProp 7.2.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 (std::set<std::size_t>::const_iterator itisgi = unique_groups.begin(); itisgi != unique_groups.end(); ++itisgi) {
5 for (std::set<std::size_t>::const_iterator itjsgi = unique_groups.begin(); itjsgi != unique_groups.end(); ++itjsgi) {
6 if (*itjsgi >= *itisgi) {
7 continue;
8 }
9 int mgi1 = static_cast<int>(m_sgi_to_mgi.find(*itisgi)->second);
10 int mgi2 = static_cast<int>(m_sgi_to_mgi.find(*itjsgi)->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 std::map<std::pair<int, int>, UNIFACLibrary::InteractionParameters>::iterator it =
39 this->interaction.find(std::pair<int, int>(static_cast<int>(mgi1), static_cast<int>(mgi2)));
40 if (it == this->interaction.end()) {
41 throw CoolProp::ValueError(format("Unable to match mgi-mgi pair: [%d,%d]", static_cast<int>(mgi1), static_cast<int>(mgi1)));
42 } else {
43 if (parameter == "aij") {
44 return it->second.a_ij;
45 } else if (parameter == "bij") {
46 return it->second.b_ij;
47 } else if (parameter == "cij") {
48 return it->second.c_ij;
49 } else {
50 throw CoolProp::ValueError(format("I don't know what to do with parameter [%s]", parameter.c_str()));
51 }
52 }
53}
54
56void UNIFAC::UNIFACMixture::set_mole_fractions(const std::vector<double>& z) {
57 // // If the vector fractions are the same as last ones, don't do anything and return
58 // if (!mole_fractions.empty() && maxvectordiff(z, mole_fractions) < 1e-15){
59 // return;
60 // }
61 this->mole_fractions = z;
62 if (this->N != z.size()) {
63 throw CoolProp::ValueError("Size of molar fraction do not match number of components.");
64 }
65
66 std::map<std::size_t, double>&Xg = m_Xg, &thetag = m_thetag;
67 Xg.clear();
68 thetag.clear();
69
70 // Iterate over the fluids
71 double X_summer = 0;
72 for (std::size_t i = 0; i < this->mole_fractions.size(); ++i) {
73 X_summer += this->mole_fractions[i] * pure_data[i].group_count;
74 }
76 for (std::set<std::size_t>::iterator itsgi = unique_groups.begin(); itsgi != unique_groups.end(); ++itsgi) {
77 double X = 0;
78 // Iterate over the fluids
79 for (std::size_t i = 0; i < this->mole_fractions.size(); ++i) {
80 X += this->mole_fractions[i] * group_count(i, *itsgi);
81 }
82 Xg.insert(std::pair<std::size_t, double>(*itsgi, X));
83 }
85 for (std::map<std::size_t, double>::iterator it = Xg.begin(); it != Xg.end(); ++it) {
86 it->second /= X_summer;
87 //printf("X_{%d}: %g\n", it->first, it->second);
88 }
89 double theta_summer = 0;
90 for (std::set<std::size_t>::iterator itsgi = unique_groups.begin(); itsgi != unique_groups.end(); ++itsgi) {
91 double cont = Xg.find(*itsgi)->second * m_Q.find(*itsgi)->second;
92 theta_summer += cont;
93 thetag.insert(std::pair<std::size_t, double>(*itsgi, cont));
94 }
96 for (std::map<std::size_t, double>::iterator it = thetag.begin(); it != thetag.end(); ++it) {
97 it->second /= theta_summer;
98 //printf("theta_{%d}: %g\n", it->first, it->second);
99 }
100}
101
102double UNIFAC::UNIFACMixture::Psi(std::size_t sgi1, std::size_t sgi2) const {
103
104 if (this->interaction.size() == 0) {
105 throw CoolProp::ValueError("interaction parameters for UNIFAC not yet set");
106 }
107 std::size_t mgi1 = m_sgi_to_mgi.find(sgi1)->second;
108 std::size_t mgi2 = m_sgi_to_mgi.find(sgi2)->second;
109 if (mgi1 == mgi2) {
110 return 1;
111 } else {
112 std::map<std::pair<int, int>, UNIFACLibrary::InteractionParameters>::const_iterator it =
113 this->interaction.find(std::pair<int, int>(static_cast<int>(mgi1), static_cast<int>(mgi2)));
114 if (it != this->interaction.end()) {
115 return exp(-(it->second.a_ij / this->m_T + it->second.b_ij + it->second.c_ij * this->m_T));
116 } else {
118 format("Could not match mgi[%d]-mgi[%d] interaction in UNIFAC", static_cast<int>(mgi1), static_cast<int>(mgi2)));
119 }
120 }
121}
122
123std::size_t UNIFAC::UNIFACMixture::group_count(std::size_t i, std::size_t sgi) const {
124 const UNIFACLibrary::Component& c = components[i];
125 for (std::vector<UNIFACLibrary::ComponentGroup>::const_iterator it = c.groups.begin(); it != c.groups.end(); ++it) {
126 if (it->group.sgi == sgi) {
127 return it->count;
128 }
129 }
130 return 0;
131}
132
133double UNIFAC::UNIFACMixture::theta_pure(std::size_t i, std::size_t sgi) const {
134 return pure_data[i].theta.find(sgi)->second;
135}
136
138 // // Check whether you are using exactly the same temperature as last time
139 // if (static_cast<bool>(_T) && std::abs(static_cast<double>(_T) - T) < 1e-15 {
140 // //
141 // return;
142 // }
143 this->m_T = T;
144
145 if (this->mole_fractions.empty()) {
146 throw CoolProp::ValueError("mole fractions must be set before calling set_temperature");
147 }
148
149 // Compute Psi once for the different calls
150 for (std::set<std::size_t>::iterator itk = unique_groups.begin(); itk != unique_groups.end(); ++itk) {
151 for (std::set<std::size_t>::iterator itm = unique_groups.begin(); itm != unique_groups.end(); ++itm) {
152 Psi_[std::pair<std::size_t, std::size_t>(*itk, *itm)] = Psi(*itk, *itm);
153 }
154 }
155
156 for (std::size_t i = 0; i < this->mole_fractions.size(); ++i) {
157 const UNIFACLibrary::Component& c = components[i];
158 for (std::size_t k = 0; k < c.groups.size(); ++k) {
159 double Q = c.groups[k].group.Q_k;
160 int sgik = c.groups[k].group.sgi;
161 double sum1 = 0;
162 for (std::size_t m = 0; m < c.groups.size(); ++m) {
163 int sgim = c.groups[m].group.sgi;
164 sum1 += theta_pure(i, sgim) * Psi_.find(std::pair<std::size_t, std::size_t>(sgim, sgik))->second;
165 }
166 double s = 1 - log(sum1);
167 for (std::size_t m = 0; m < c.groups.size(); ++m) {
168 int sgim = c.groups[m].group.sgi;
169 double sum2 = 0;
170 for (std::size_t n = 0; n < c.groups.size(); ++n) {
171 int sgin = c.groups[n].group.sgi;
172 sum2 += theta_pure(i, sgin) * Psi_.find(std::pair<std::size_t, std::size_t>(sgin, sgim))->second;
173 }
174 s -= theta_pure(i, sgim) * Psi_.find(std::pair<std::size_t, std::size_t>(sgik, sgim))->second / sum2;
175 }
176 pure_data[i].lnGamma[sgik] = Q * s;
177 //printf("ln(Gamma)^(%d)_{%d}: %g\n", static_cast<int>(i + 1), sgik, Q*s);
178 }
179 }
180
181 std::map<std::size_t, double>&thetag = m_thetag, &lnGammag = m_lnGammag;
182 lnGammag.clear();
183
184 for (std::set<std::size_t>::iterator itksgi = unique_groups.begin(); itksgi != unique_groups.end(); ++itksgi) {
185 double sum1 = 0;
186 for (std::set<std::size_t>::iterator itmsgi = unique_groups.begin(); itmsgi != unique_groups.end(); ++itmsgi) {
187 sum1 += thetag.find(*itmsgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(*itmsgi, *itksgi))->second;
188 }
189 double s = 1 - log(sum1);
190 for (std::set<std::size_t>::iterator itmsgi = unique_groups.begin(); itmsgi != unique_groups.end(); ++itmsgi) {
191 double sum3 = 0;
192 for (std::set<std::size_t>::iterator itnsgi = unique_groups.begin(); itnsgi != unique_groups.end(); ++itnsgi) {
193 sum3 += thetag.find(*itnsgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(*itnsgi, *itmsgi))->second;
194 }
195 s -= thetag.find(*itmsgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(*itksgi, *itmsgi))->second / sum3;
196 }
197 lnGammag.insert(std::pair<std::size_t, double>(*itksgi, m_Q.find(*itksgi)->second * s));
198 //printf("log(Gamma)_{%d}: %g\n", itk->sgi, itk->Q_k*s);
199 }
200 _T = m_T;
201}
202double UNIFAC::UNIFACMixture::ln_gamma_R(const double tau, std::size_t i, std::size_t itau) {
203 if (itau == 0) {
204 set_temperature(T_r / tau);
205 double summer = 0;
206 for (std::set<std::size_t>::const_iterator itsgi = unique_groups.begin(); itsgi != unique_groups.end(); ++itsgi) {
207 std::size_t count = group_count(i, *itsgi);
208 if (count > 0) {
209 summer += count * (m_lnGammag.find(*itsgi)->second - pure_data[i].lnGamma.find(*itsgi)->second);
210 }
211 }
212 //printf("log(gamma)_{%d}: %g\n", i+1, summer);
213 return summer;
214 } else {
215 double dtau = 0.01 * tau;
216 return (ln_gamma_R(tau + dtau, i, itau - 1) - ln_gamma_R(tau - dtau, i, itau - 1)) / (2 * dtau);
217 }
218}
219void UNIFAC::UNIFACMixture::activity_coefficients(double tau, const std::vector<double>& z, std::vector<double>& gamma) {
220 if (this->N != z.size()) {
221 throw CoolProp::ValueError("Size of molar fraction do not match number of components.");
222 }
223 std::vector<double> r(N), q(N), l(N), phi(N), theta(N), ln_Gamma_C(N);
224 double summerzr = 0, summerzq = 0, summerzl = 0;
225 for (std::size_t i = 0; i < N; ++i) {
226 double summerr = 0, summerq = 0;
227 const UNIFACLibrary::Component& c = components[i];
228 for (std::size_t j = 0; j < c.groups.size(); ++j) {
229 const UNIFACLibrary::ComponentGroup& cg = c.groups[j];
230 summerr += cg.count * cg.group.R_k;
231 summerq += cg.count * cg.group.Q_k;
232 }
233 r[i] = summerr;
234 q[i] = summerq;
235 summerzr += z[i] * r[i];
236 summerzq += z[i] * q[i];
237 }
238 for (std::size_t i = 0; i < N; ++i) {
239 phi[i] = z[i] * r[i] / summerzr;
240 theta[i] = z[i] * q[i] / summerzq;
241 l[i] = 10.0 / 2.0 * (r[i] - q[i]) - (r[i] - 1);
242 summerzl += z[i] * l[i];
243 }
244 for (std::size_t i = 0; i < N; ++i) {
245 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;
246 gamma[i] = exp(ln_gamma_R(tau, i, 0) + ln_Gamma_C[i]);
247 }
248}
249
252 components.push_back(comp);
253 for (std::vector<UNIFACLibrary::ComponentGroup>::const_iterator it = comp.groups.begin(); it != comp.groups.end(); ++it) {
254 m_sgi_to_mgi.insert(std::pair<std::size_t, std::size_t>(it->group.sgi, it->group.mgi));
255 }
256}
257
258void UNIFAC::UNIFACMixture::set_components(const std::string& identifier_type, std::vector<std::string> identifiers) {
259 components.clear();
260 N = identifiers.size();
261 if (identifier_type == "name") {
262 // Iterate over the provided names
263 for (std::vector<std::string>::const_iterator it = identifiers.begin(); it != identifiers.end(); ++it) {
264 // Get and add the component
265 UNIFACLibrary::Component c = library.get_component("name", *it);
266 add_component(c);
267 }
268 } else {
269 throw CoolProp::ValueError("Cannot understand identifier_type");
270 }
272 set_pure_data();
273}
274
277 pure_data.clear();
278 unique_groups.clear();
279 m_Q.clear();
280 for (std::size_t i = 0; i < N; ++i) {
281 const UNIFACLibrary::Component& c = components[i];
282 ComponentData cd;
283 double summerxq = 0;
284 cd.group_count = 0;
285 for (std::size_t j = 0; j < c.groups.size(); ++j) {
286 const UNIFACLibrary::ComponentGroup& cg = c.groups[j];
287 double x = static_cast<double>(cg.count);
288 double theta = static_cast<double>(cg.count * cg.group.Q_k);
289 cd.X.insert(std::pair<int, double>(cg.group.sgi, x));
290 cd.theta.insert(std::pair<int, double>(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.insert(std::pair<std::size_t, double>(cg.group.sgi, cg.group.Q_k));
295 }
297 for (std::map<std::size_t, double>::iterator it = cd.X.begin(); it != cd.X.end(); ++it) {
298 it->second /= cd.group_count;
299 //printf("X^(%d)_{%d}: %g\n", static_cast<int>(i + 1), static_cast<int>(it->first), it->second);
300 }
302 for (std::map<std::size_t, double>::iterator it = cd.theta.begin(); it != cd.theta.end(); ++it) {
303 it->second /= summerxq;
304 //printf("theta^(%d)_{%d}: %g\n", static_cast<int>(i+1), static_cast<int>(it->first), it->second);
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 (std::size_t j = 0; j < components[i].groups.size(); ++j) {
314 if (components[i].groups[j].group.sgi == sgi) {
315 components[i].groups[j].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 (std::size_t j = 0; j < components[i].groups.size(); ++j) {
328 if (components[i].groups[j].group.sgi == sgi) {
329 return components[i].groups[j].group.Q_k;
330 }
331 }
332 }
333 throw CoolProp::ValueError("Could not get Q_k");
334}