CoolProp  6.6.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  std::size_t mgi1 = m_sgi_to_mgi.find(*itisgi)->second;
10  std::size_t mgi2 = 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 
25 void 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 }
37 double 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 = this->interaction.find(std::pair<int, int>(mgi1, 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 
55 void 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 (std::set<std::size_t>::iterator itsgi = unique_groups.begin(); itsgi != unique_groups.end(); ++itsgi) {
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, *itsgi);
80  }
81  Xg.insert(std::pair<std::size_t, double>(*itsgi, X));
82  }
84  for (std::map<std::size_t, double>::iterator it = Xg.begin(); it != Xg.end(); ++it) {
85  it->second /= X_summer;
86  //printf("X_{%d}: %g\n", it->first, it->second);
87  }
88  double theta_summer = 0;
89  for (std::set<std::size_t>::iterator itsgi = unique_groups.begin(); itsgi != unique_groups.end(); ++itsgi) {
90  double cont = Xg.find(*itsgi)->second * m_Q.find(*itsgi)->second;
91  theta_summer += cont;
92  thetag.insert(std::pair<std::size_t, double>(*itsgi, cont));
93  }
95  for (std::map<std::size_t, double>::iterator it = thetag.begin(); it != thetag.end(); ++it) {
96  it->second /= theta_summer;
97  //printf("theta_{%d}: %g\n", it->first, it->second);
98  }
99 }
100 
101 double 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  std::map<std::pair<int, int>, UNIFACLibrary::InteractionParameters>::const_iterator it =
112  this->interaction.find(std::pair<int, int>(static_cast<int>(mgi1), static_cast<int>(mgi2)));
113  if (it != this->interaction.end()) {
114  return exp(-(it->second.a_ij / this->m_T + it->second.b_ij + it->second.c_ij * this->m_T));
115  } else {
116  throw CoolProp::ValueError(
117  format("Could not match mgi[%d]-mgi[%d] interaction in UNIFAC", static_cast<int>(mgi1), static_cast<int>(mgi2)));
118  }
119  }
120 }
121 
122 std::size_t UNIFAC::UNIFACMixture::group_count(std::size_t i, std::size_t sgi) const {
123  const UNIFACLibrary::Component& c = components[i];
124  for (std::vector<UNIFACLibrary::ComponentGroup>::const_iterator it = c.groups.begin(); it != c.groups.end(); ++it) {
125  if (it->group.sgi == sgi) {
126  return it->count;
127  }
128  }
129  return 0;
130 }
131 
132 double UNIFAC::UNIFACMixture::theta_pure(std::size_t i, std::size_t sgi) const {
133  return pure_data[i].theta.find(sgi)->second;
134 }
135 
137  // // Check whether you are using exactly the same temperature as last time
138  // if (static_cast<bool>(_T) && std::abs(static_cast<double>(_T) - T) < 1e-15 {
139  // //
140  // return;
141  // }
142  this->m_T = T;
143 
144  if (this->mole_fractions.empty()) {
145  throw CoolProp::ValueError("mole fractions must be set before calling set_temperature");
146  }
147 
148  // Compute Psi once for the different calls
149  for (std::set<std::size_t>::iterator itk = unique_groups.begin(); itk != unique_groups.end(); ++itk) {
150  for (std::set<std::size_t>::iterator itm = unique_groups.begin(); itm != unique_groups.end(); ++itm) {
151  Psi_[std::pair<std::size_t, std::size_t>(*itk, *itm)] = Psi(*itk, *itm);
152  }
153  }
154 
155  for (std::size_t i = 0; i < this->mole_fractions.size(); ++i) {
156  const UNIFACLibrary::Component& c = components[i];
157  for (std::size_t k = 0; k < c.groups.size(); ++k) {
158  double Q = c.groups[k].group.Q_k;
159  int sgik = c.groups[k].group.sgi;
160  double sum1 = 0;
161  for (std::size_t m = 0; m < c.groups.size(); ++m) {
162  int sgim = c.groups[m].group.sgi;
163  sum1 += theta_pure(i, sgim) * Psi_.find(std::pair<std::size_t, std::size_t>(sgim, sgik))->second;
164  }
165  double s = 1 - log(sum1);
166  for (std::size_t m = 0; m < c.groups.size(); ++m) {
167  int sgim = c.groups[m].group.sgi;
168  double sum2 = 0;
169  for (std::size_t n = 0; n < c.groups.size(); ++n) {
170  int sgin = c.groups[n].group.sgi;
171  sum2 += theta_pure(i, sgin) * Psi_.find(std::pair<std::size_t, std::size_t>(sgin, sgim))->second;
172  }
173  s -= theta_pure(i, sgim) * Psi_.find(std::pair<std::size_t, std::size_t>(sgik, sgim))->second / sum2;
174  }
175  pure_data[i].lnGamma[sgik] = Q * s;
176  //printf("ln(Gamma)^(%d)_{%d}: %g\n", static_cast<int>(i + 1), sgik, Q*s);
177  }
178  }
179 
180  std::map<std::size_t, double>&thetag = m_thetag, &lnGammag = m_lnGammag;
181  lnGammag.clear();
182 
183  for (std::set<std::size_t>::iterator itksgi = unique_groups.begin(); itksgi != unique_groups.end(); ++itksgi) {
184  double sum1 = 0;
185  for (std::set<std::size_t>::iterator itmsgi = unique_groups.begin(); itmsgi != unique_groups.end(); ++itmsgi) {
186  sum1 += thetag.find(*itmsgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(*itmsgi, *itksgi))->second;
187  }
188  double s = 1 - log(sum1);
189  for (std::set<std::size_t>::iterator itmsgi = unique_groups.begin(); itmsgi != unique_groups.end(); ++itmsgi) {
190  double sum3 = 0;
191  for (std::set<std::size_t>::iterator itnsgi = unique_groups.begin(); itnsgi != unique_groups.end(); ++itnsgi) {
192  sum3 += thetag.find(*itnsgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(*itnsgi, *itmsgi))->second;
193  }
194  s -= thetag.find(*itmsgi)->second * Psi_.find(std::pair<std::size_t, std::size_t>(*itksgi, *itmsgi))->second / sum3;
195  }
196  lnGammag.insert(std::pair<std::size_t, double>(*itksgi, m_Q.find(*itksgi)->second * s));
197  //printf("log(Gamma)_{%d}: %g\n", itk->sgi, itk->Q_k*s);
198  }
199  _T = m_T;
200 }
201 double UNIFAC::UNIFACMixture::ln_gamma_R(const double tau, std::size_t i, std::size_t itau) {
202  if (itau == 0) {
203  set_temperature(T_r / tau);
204  double summer = 0;
205  for (std::set<std::size_t>::const_iterator itsgi = unique_groups.begin(); itsgi != unique_groups.end(); ++itsgi) {
206  std::size_t count = group_count(i, *itsgi);
207  if (count > 0) {
208  summer += count * (m_lnGammag.find(*itsgi)->second - pure_data[i].lnGamma.find(*itsgi)->second);
209  }
210  }
211  //printf("log(gamma)_{%d}: %g\n", i+1, summer);
212  return summer;
213  } else {
214  double dtau = 0.01 * tau;
215  return (ln_gamma_R(tau + dtau, i, itau - 1) - ln_gamma_R(tau - dtau, i, itau - 1)) / (2 * dtau);
216  }
217 }
218 void UNIFAC::UNIFACMixture::activity_coefficients(double tau, const std::vector<double>& z, std::vector<double>& gamma) {
219  if (this->N != z.size()) {
220  throw CoolProp::ValueError("Size of molar fraction do not match number of components.");
221  }
222  std::vector<double> r(N), q(N), l(N), phi(N), theta(N), ln_Gamma_C(N);
223  double summerzr = 0, summerzq = 0, summerzl = 0;
224  for (std::size_t i = 0; i < N; ++i) {
225  double summerr = 0, summerq = 0;
226  const UNIFACLibrary::Component& c = components[i];
227  for (std::size_t j = 0; j < c.groups.size(); ++j) {
228  const UNIFACLibrary::ComponentGroup& cg = c.groups[j];
229  summerr += cg.count * cg.group.R_k;
230  summerq += cg.count * cg.group.Q_k;
231  }
232  r[i] = summerr;
233  q[i] = summerq;
234  summerzr += z[i] * r[i];
235  summerzq += z[i] * q[i];
236  }
237  for (std::size_t i = 0; i < N; ++i) {
238  phi[i] = z[i] * r[i] / summerzr;
239  theta[i] = z[i] * q[i] / summerzq;
240  l[i] = 10.0 / 2.0 * (r[i] - q[i]) - (r[i] - 1);
241  summerzl += z[i] * l[i];
242  }
243  for (std::size_t i = 0; i < N; ++i) {
244  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;
245  gamma[i] = exp(ln_gamma_R(tau, i, 0) + ln_Gamma_C[i]);
246  }
247 }
248 
251  components.push_back(comp);
252  for (std::vector<UNIFACLibrary::ComponentGroup>::const_iterator it = comp.groups.begin(); it != comp.groups.end(); ++it) {
253  m_sgi_to_mgi.insert(std::pair<std::size_t, std::size_t>(it->group.sgi, it->group.mgi));
254  }
255 }
256 
257 void UNIFAC::UNIFACMixture::set_components(const std::string& identifier_type, std::vector<std::string> identifiers) {
258  components.clear();
259  N = identifiers.size();
260  if (identifier_type == "name") {
261  // Iterate over the provided names
262  for (std::vector<std::string>::const_iterator it = identifiers.begin(); it != identifiers.end(); ++it) {
263  // Get and add the component
264  UNIFACLibrary::Component c = library.get_component("name", *it);
265  add_component(c);
266  }
267  } else {
268  throw CoolProp::ValueError("Cannot understand identifier_type");
269  }
271  set_pure_data();
272 }
273 
276  pure_data.clear();
277  unique_groups.clear();
278  m_Q.clear();
279  for (std::size_t i = 0; i < N; ++i) {
280  const UNIFACLibrary::Component& c = components[i];
281  ComponentData cd;
282  double summerxq = 0;
283  cd.group_count = 0;
284  for (std::size_t j = 0; j < c.groups.size(); ++j) {
285  const UNIFACLibrary::ComponentGroup& cg = c.groups[j];
286  double x = static_cast<double>(cg.count);
287  double theta = static_cast<double>(cg.count * cg.group.Q_k);
288  cd.X.insert(std::pair<int, double>(cg.group.sgi, x));
289  cd.theta.insert(std::pair<int, double>(cg.group.sgi, theta));
290  cd.group_count += cg.count;
291  summerxq += x * cg.group.Q_k;
292  unique_groups.insert(cg.group.sgi);
293  m_Q.insert(std::pair<std::size_t, double>(cg.group.sgi, cg.group.Q_k));
294  }
296  for (std::map<std::size_t, double>::iterator it = cd.X.begin(); it != cd.X.end(); ++it) {
297  it->second /= cd.group_count;
298  //printf("X^(%d)_{%d}: %g\n", static_cast<int>(i + 1), static_cast<int>(it->first), it->second);
299  }
301  for (std::map<std::size_t, double>::iterator it = cd.theta.begin(); it != cd.theta.end(); ++it) {
302  it->second /= summerxq;
303  //printf("theta^(%d)_{%d}: %g\n", static_cast<int>(i+1), static_cast<int>(it->first), it->second);
304  }
305  pure_data.push_back(cd);
306  }
307 }
308 
310 void UNIFAC::UNIFACMixture::set_Q_k(const size_t sgi, const double value) {
311  for (std::size_t i = 0; i < N; ++i) {
312  for (std::size_t j = 0; j < components[i].groups.size(); ++j) {
313  if (components[i].groups[j].group.sgi == sgi) {
314  components[i].groups[j].group.Q_k = value;
315  }
316  }
317  }
318 
320  set_pure_data();
321 }
322 
324 double UNIFAC::UNIFACMixture::get_Q_k(const size_t sgi) const {
325  for (std::size_t i = 0; i < N; ++i) {
326  for (std::size_t j = 0; j < components[i].groups.size(); ++j) {
327  if (components[i].groups[j].group.sgi == sgi) {
328  return components[i].groups[j].group.Q_k;
329  }
330  }
331  }
332  throw CoolProp::ValueError("Could not get Q_k");
333 }