Cantera  2.2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RedlichKwongMFTP.cpp
Go to the documentation of this file.
1 /**
2  * @file RedlichKwongMFTP.cpp
3  * Definition file for a derived class of ThermoPhase that assumes either
4  * an ideal gas or ideal solution approximation and handles
5  * variable pressure standard state methods for calculating
6  * thermodynamic properties (see \ref thermoprops and
7  * class \link Cantera::RedlichKwongMFTP RedlichKwongMFTP\endlink).
8  */
9 /*
10  * Copyright (2005) Sandia Corporation. Under the terms of
11  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
12  * U.S. Government retains certain rights in this software.
13  */
14 
16 
21 #include "cantera/base/ctml.h"
22 
23 using namespace std;
24 
25 namespace Cantera
26 {
27 
28 const doublereal RedlichKwongMFTP::omega_a = 4.27480233540E-01;
29 const doublereal RedlichKwongMFTP::omega_b = 8.66403499650E-02;
30 const doublereal RedlichKwongMFTP::omega_vc = 3.33333333333333E-01;
31 
32 RedlichKwongMFTP::RedlichKwongMFTP() :
33  m_standardMixingRules(0),
34  m_formTempParam(0),
35  m_b_current(0.0),
36  m_a_current(0.0),
37  NSolns_(0),
38  dpdV_(0.0),
39  dpdT_(0.0)
40 {
41  Vroot_[0] = 0.0;
42  Vroot_[1] = 0.0;
43  Vroot_[2] = 0.0;
44 }
45 
46 RedlichKwongMFTP::RedlichKwongMFTP(const std::string& infile, std::string id_) :
47  m_standardMixingRules(0),
48  m_formTempParam(0),
49  m_b_current(0.0),
50  m_a_current(0.0),
51  NSolns_(0),
52  dpdV_(0.0),
53  dpdT_(0.0)
54 {
55  Vroot_[0] = 0.0;
56  Vroot_[1] = 0.0;
57  Vroot_[2] = 0.0;
58  XML_Node* root = get_XML_File(infile);
59  if (id_ == "-") {
60  id_ = "";
61  }
62  XML_Node* xphase = get_XML_NameID("phase", std::string("#")+id_, root);
63  if (!xphase) {
64  throw CanteraError("newPhase",
65  "Couldn't find phase named \"" + id_ + "\" in file, " + infile);
66  }
67  importPhase(*xphase, this);
68 }
69 
70 RedlichKwongMFTP::RedlichKwongMFTP(XML_Node& phaseRefRoot, const std::string& id_) :
71  m_standardMixingRules(0),
72  m_formTempParam(0),
73  m_b_current(0.0),
74  m_a_current(0.0),
75  NSolns_(0),
76  dpdV_(0.0),
77  dpdT_(0.0)
78 {
79  Vroot_[0] = 0.0;
80  Vroot_[1] = 0.0;
81  Vroot_[2] = 0.0;
82  XML_Node* xphase = get_XML_NameID("phase", std::string("#")+id_, &phaseRefRoot);
83  if (!xphase) {
84  throw CanteraError("RedlichKwongMFTP::RedlichKwongMFTP()","Couldn't find phase named \"" + id_ + "\" in XML node");
85  }
86  importPhase(*xphase, this);
87 }
88 
90  m_standardMixingRules(0),
91  m_formTempParam(0),
92  m_b_current(0.0),
93  m_a_current(0.0),
94  NSolns_(0),
95  dpdV_(0.0),
96  dpdT_(0.0)
97 {
98  warn_deprecated("RedlichKwongMFTP::RedlichKwongMFTP(int testProb)",
99  "To be removed after Cantera 2.2");
100  std::string infile = "co2_redlichkwong.xml";
101  std::string id_;
102  if (testProb == 1) {
103  infile = "co2_redlichkwong.xml";
104  id_ = "carbondioxide";
105  } else {
106  throw CanteraError("RedlichKwongMFTP::RedlichKwongMFTP(int testProb)",
107  "test prob = 1 only");
108  }
109  XML_Node* root = get_XML_File(infile);
110  if (id_ == "-") {
111  id_ = "";
112  }
113  XML_Node* xphase = get_XML_NameID("phase", std::string("#")+id_, root);
114  if (!xphase) {
115  throw CanteraError("newPhase", "Couldn't find phase named \"" + id_ + "\" in file, " + infile);
116  }
117  importPhase(*xphase, this);
118 }
119 
121  m_standardMixingRules(0),
122  m_formTempParam(0),
123  m_b_current(0.0),
124  m_a_current(0.0),
125  NSolns_(0),
126  dpdV_(0.0),
127  dpdT_(0.0)
128 {
129  *this = b;
130 }
131 
133 {
134  if (&b != this) {
135  /*
136  * Mostly, this is a passthrough to the underlying
137  * assignment operator for the ThermoPhae parent object.
138  */
140  /*
141  * However, we have to handle data that we own.
142  */
147  a_vec_Curr_ = b.a_vec_Curr_;
148  b_vec_Curr_ = b.b_vec_Curr_;
149  a_coeff_vec = b.a_coeff_vec;
150 
151  m_pc_Species = b.m_pc_Species;
152  m_tc_Species = b.m_tc_Species;
153  m_vc_Species = b.m_vc_Species;
154  NSolns_ = b.NSolns_;
155  Vroot_[0] = b.Vroot_[0];
156  Vroot_[1] = b.Vroot_[1];
157  Vroot_[2] = b.Vroot_[2];
158  m_pp = b.m_pp;
159  m_tmpV = b.m_tmpV;
160  m_partialMolarVolumes = b.m_partialMolarVolumes;
161  dpdV_ = b.dpdV_;
162  dpdT_ = b.dpdT_;
163  dpdni_ = b.dpdni_;
164  }
165  return *this;
166 }
167 
169 {
170  return new RedlichKwongMFTP(*this);
171 }
172 
174 {
175  return cRedlichKwongMFTP;
176 }
177 
178 /*
179  * ------------Molar Thermodynamic Properties -------------------------
180  */
181 
183 {
185  doublereal h_ideal = _RT() * mean_X(m_h0_RT);
186  doublereal h_nonideal = hresid();
187  return h_ideal + h_nonideal;
188 }
189 
191 {
193  doublereal sr_ideal = GasConstant * (mean_X(m_s0_R)
194  - sum_xlogx() - std::log(pressure()/m_spthermo->refPressure()));
195  doublereal sr_nonideal = sresid();
196  return sr_ideal + sr_nonideal;
197 }
198 
199 doublereal RedlichKwongMFTP::cp_mole() const
200 {
202  doublereal TKelvin = temperature();
203  doublereal sqt = sqrt(TKelvin);
204  doublereal mv = molarVolume();
205  doublereal vpb = mv + m_b_current;
207  doublereal cpref = GasConstant * mean_X(m_cp0_R);
208  doublereal dadt = da_dt();
209  doublereal fac = TKelvin * dadt - 3.0 * m_a_current / 2.0;
210  doublereal dHdT_V = (cpref + mv * dpdT_ - GasConstant - 1.0 / (2.0 * m_b_current * TKelvin * sqt) * log(vpb/mv) * fac
211  +1.0/(m_b_current * sqt) * log(vpb/mv) * (-0.5 * dadt));
212  return dHdT_V - (mv + TKelvin * dpdT_ / dpdV_) * dpdT_;
213 }
214 
215 doublereal RedlichKwongMFTP::cv_mole() const
216 {
217  throw CanteraError("RedlichKwongMFTP::cv_mole", "unimplemented");
218  return cp_mole() - GasConstant;
219 }
220 
221 doublereal RedlichKwongMFTP::pressure() const
222 {
223 #ifdef DEBUG_MODE
225 
226  // Get a copy of the private variables stored in the State object
227  doublereal T = temperature();
228  double molarV = meanMolecularWeight() / density();
229 
230  double pp = GasConstant * T/(molarV - m_b_current) - m_a_current/(sqrt(T) * molarV * (molarV + m_b_current));
231 
232  if (fabs(pp -m_Pcurrent) > 1.0E-5 * fabs(m_Pcurrent)) {
233  throw CanteraError(" RedlichKwongMFTP::pressure()", "setState broken down, maybe");
234  }
235 #endif
236  return m_Pcurrent;
237 }
238 
240 {
241  /*
242  * Calculate the molarVolume of the solution (m**3 kmol-1)
243  */
244 
245  const doublereal* const dtmp = moleFractdivMMW();
247  double invDens = dot(m_tmpV.begin(), m_tmpV.end(), dtmp);
248  /*
249  * Set the density in the parent State object directly,
250  * by calling the Phase::setDensity() function.
251  */
252  Phase::setDensity(1.0/invDens);
253 }
254 
255 void RedlichKwongMFTP::setTemperature(const doublereal temp)
256 {
257  Phase::setTemperature(temp);
259  updateAB();
260 }
261 
262 void RedlichKwongMFTP::setMassFractions(const doublereal* const x)
263 {
265  updateAB();
266 }
267 
268 void RedlichKwongMFTP::setMassFractions_NoNorm(const doublereal* const x)
269 {
271  updateAB();
272 }
273 
274 void RedlichKwongMFTP::setMoleFractions(const doublereal* const x)
275 {
277  updateAB();
278 }
279 
280 void RedlichKwongMFTP::setMoleFractions_NoNorm(const doublereal* const x)
281 {
283  updateAB();
284 }
285 
286 void RedlichKwongMFTP::setConcentrations(const doublereal* const c)
287 {
289  updateAB();
290 }
291 
293 {
294  getPartialMolarVolumes(DATA_PTR(m_partialMolarVolumes));
295  for (size_t k = 0; k < m_kk; k++) {
296  c[k] = moleFraction(k) / m_partialMolarVolumes[k];
297  }
298 }
299 
300 doublereal RedlichKwongMFTP::standardConcentration(size_t k) const
301 {
303  return 1.0 / m_tmpV[k];
304 }
305 
306 void RedlichKwongMFTP::getUnitsStandardConc(double* uA, int, int sizeUA) const
307 {
308  warn_deprecated("RedlichKwongMFTP::getUnitsStandardConc",
309  "To be removed after Cantera 2.2.");
310 
311  //int eos = eosType();
312 
313  for (int i = 0; i < sizeUA; i++) {
314  if (i == 0) {
315  uA[0] = 1.0;
316  }
317  if (i == 1) {
318  uA[1] = -static_cast<int>(nDim());
319  }
320  if (i == 2) {
321  uA[2] = 0.0;
322  }
323  if (i == 3) {
324  uA[3] = 0.0;
325  }
326  if (i == 4) {
327  uA[4] = 0.0;
328  }
329  if (i == 5) {
330  uA[5] = 0.0;
331  }
332  }
333 
334 }
335 
337 {
338  doublereal TKelvin = temperature();
339  doublereal rt = TKelvin * GasConstant;
340  doublereal mv = molarVolume();
341  doublereal sqt = sqrt(TKelvin);
342  doublereal vpb = mv + m_b_current;
343  doublereal vmb = mv - m_b_current;
344 
345  for (size_t k = 0; k < m_kk; k++) {
346  m_pp[k] = 0.0;
347  for (size_t i = 0; i < m_kk; i++) {
348  size_t counter = k + m_kk*i;
349  m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
350  }
351  }
352  doublereal pres = pressure();
353 
354  for (size_t k = 0; k < m_kk; k++) {
355  ac[k] = (- rt * log(pres * mv / rt)
356  + rt * log(mv / vmb)
357  + rt * b_vec_Curr_[k] / vmb
358  - 2.0 * m_pp[k] / (m_b_current * sqt) * log(vpb/mv)
359  + m_a_current * b_vec_Curr_[k] / (m_b_current * m_b_current * sqt) * log(vpb/mv)
360  - m_a_current / (m_b_current * sqt) * (b_vec_Curr_[k]/vpb)
361  );
362  }
363  for (size_t k = 0; k < m_kk; k++) {
364  ac[k] = exp(ac[k]/rt);
365  }
366 }
367 
368 /*
369  * ---- Partial Molar Properties of the Solution -----------------
370  */
371 
372 void RedlichKwongMFTP::getChemPotentials_RT(doublereal* muRT) const
373 {
374  getChemPotentials(muRT);
375  doublereal invRT = 1.0 / _RT();
376  for (size_t k = 0; k < m_kk; k++) {
377  muRT[k] *= invRT;
378  }
379 }
380 
381 void RedlichKwongMFTP::getChemPotentials(doublereal* mu) const
382 {
383  getGibbs_ref(mu);
384  doublereal rt = temperature() * GasConstant;
385  for (size_t k = 0; k < m_kk; k++) {
386  double xx = std::max(SmallNumber, moleFraction(k));
387  mu[k] += rt*(log(xx));
388  }
389 
390  doublereal TKelvin = temperature();
391  doublereal mv = molarVolume();
392  doublereal sqt = sqrt(TKelvin);
393  doublereal vpb = mv + m_b_current;
394  doublereal vmb = mv - m_b_current;
395 
396  for (size_t k = 0; k < m_kk; k++) {
397  m_pp[k] = 0.0;
398  for (size_t i = 0; i < m_kk; i++) {
399  size_t counter = k + m_kk*i;
400  m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
401  }
402  }
403  doublereal pres = pressure();
404  doublereal refP = refPressure();
405 
406  for (size_t k = 0; k < m_kk; k++) {
407  mu[k] += (rt * log(pres/refP) - rt * log(pres * mv / rt)
408  + rt * log(mv / vmb)
409  + rt * b_vec_Curr_[k] / vmb
410  - 2.0 * m_pp[k] / (m_b_current * sqt) * log(vpb/mv)
411  + m_a_current * b_vec_Curr_[k] / (m_b_current * m_b_current * sqt) * log(vpb/mv)
412  - m_a_current / (m_b_current * sqt) * (b_vec_Curr_[k]/vpb)
413  );
414  }
415 }
416 
418 {
419  /*
420  * First we get the reference state contributions
421  */
422  getEnthalpy_RT_ref(hbar);
423  doublereal rt = GasConstant * temperature();
424  scale(hbar, hbar+m_kk, hbar, rt);
425 
426  /*
427  * We calculate dpdni_
428  */
429  doublereal TKelvin = temperature();
430  doublereal mv = molarVolume();
431  doublereal sqt = sqrt(TKelvin);
432 
433  doublereal vpb = mv + m_b_current;
434  doublereal vmb = mv - m_b_current;
435 
436  for (size_t k = 0; k < m_kk; k++) {
437  m_pp[k] = 0.0;
438  for (size_t i = 0; i < m_kk; i++) {
439  size_t counter = k + m_kk*i;
440  m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
441  }
442  }
443 
444 
445 
446  for (size_t k = 0; k < m_kk; k++) {
447  dpdni_[k] = rt/vmb + rt * b_vec_Curr_[k] / (vmb * vmb) - 2.0 * m_pp[k] / (sqt * mv * vpb)
448  + m_a_current * b_vec_Curr_[k]/(sqt * mv * vpb * vpb);
449  }
450  doublereal dadt = da_dt();
451  doublereal fac = TKelvin * dadt - 3.0 * m_a_current / 2.0;
452 
453  for (size_t k = 0; k < m_kk; k++) {
454  m_tmpV[k] = 0.0;
455  for (size_t i = 0; i < m_kk; i++) {
456  size_t counter = k + m_kk*i;
457  m_tmpV[k] += 2.0 * moleFractions_[i] * TKelvin * a_coeff_vec(1,counter) - 3.0 * moleFractions_[i] * a_vec_Curr_[counter];
458  }
459  }
460 
462  doublereal fac2 = mv + TKelvin * dpdT_ / dpdV_;
463 
464  for (size_t k = 0; k < m_kk; k++) {
465  double hE_v = (mv * dpdni_[k] - rt - b_vec_Curr_[k]/ (m_b_current * m_b_current * sqt) * log(vpb/mv)*fac
466  + 1.0 / (m_b_current * sqt) * log(vpb/mv) * m_tmpV[k]
467  + b_vec_Curr_[k] / vpb / (m_b_current * sqt) * fac);
468  hbar[k] = hbar[k] + hE_v;
469 
470 
471  hbar[k] -= fac2 * dpdni_[k];
472  }
473 
474 }
475 
476 void RedlichKwongMFTP::getPartialMolarEntropies(doublereal* sbar) const
477 {
478  getEntropy_R_ref(sbar);
479  doublereal r = GasConstant;
480  scale(sbar, sbar+m_kk, sbar, r);
481  doublereal TKelvin = temperature();
482  doublereal sqt = sqrt(TKelvin);
483  doublereal mv = molarVolume();
484  doublereal refP = refPressure();
485 
486  for (size_t k = 0; k < m_kk; k++) {
487  doublereal xx = std::max(SmallNumber, moleFraction(k));
488  sbar[k] += r * (- log(xx));
489  }
490 
491  for (size_t k = 0; k < m_kk; k++) {
492  m_pp[k] = 0.0;
493  for (size_t i = 0; i < m_kk; i++) {
494  size_t counter = k + m_kk*i;
495  m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
496  }
497  }
498 
499  for (size_t k = 0; k < m_kk; k++) {
500  m_tmpV[k] = 0.0;
501  for (size_t i = 0; i < m_kk; i++) {
502  size_t counter = k + m_kk*i;
503  m_tmpV[k] += moleFractions_[i] * a_coeff_vec(1,counter);
504  }
505  }
506 
507 
508  doublereal dadt = da_dt();
509  doublereal fac = dadt - m_a_current / (2.0 * TKelvin);
510  doublereal vmb = mv - m_b_current;
511  doublereal vpb = mv + m_b_current;
512 
513 
514  for (size_t k = 0; k < m_kk; k++) {
515  sbar[k] -=(GasConstant * log(GasConstant * TKelvin / (refP * mv))
516  + GasConstant
517  + GasConstant * log(mv/vmb)
518  + GasConstant * b_vec_Curr_[k]/vmb
519  + m_pp[k]/(m_b_current * TKelvin * sqt) * log(vpb/mv)
520  - 2.0 * m_tmpV[k]/(m_b_current * sqt) * log(vpb/mv)
521  + b_vec_Curr_[k] / (m_b_current * m_b_current * sqt) * log(vpb/mv) * fac
522  - 1.0 / (m_b_current * sqt) * b_vec_Curr_[k] / vpb * fac
523  ) ;
524  }
525 
527  getPartialMolarVolumes(DATA_PTR(m_partialMolarVolumes));
528  for (size_t k = 0; k < m_kk; k++) {
529  sbar[k] -= -m_partialMolarVolumes[k] * dpdT_;
530  }
531 }
532 
534 {
535  getIntEnergy_RT(ubar);
536  doublereal rt = GasConstant * temperature();
537  scale(ubar, ubar+m_kk, ubar, rt);
538 }
539 
540 void RedlichKwongMFTP::getPartialMolarCp(doublereal* cpbar) const
541 {
542  getCp_R(cpbar);
543  doublereal r = GasConstant;
544  scale(cpbar, cpbar+m_kk, cpbar, r);
545 }
546 
547 void RedlichKwongMFTP::getPartialMolarVolumes(doublereal* vbar) const
548 {
549  for (size_t k = 0; k < m_kk; k++) {
550  m_pp[k] = 0.0;
551  for (size_t i = 0; i < m_kk; i++) {
552  size_t counter = k + m_kk*i;
553  m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
554  }
555  }
556 
557  for (size_t k = 0; k < m_kk; k++) {
558  m_tmpV[k] = 0.0;
559  for (size_t i = 0; i < m_kk; i++) {
560  size_t counter = k + m_kk*i;
561  m_tmpV[k] += moleFractions_[i] * a_coeff_vec(1,counter);
562  }
563  }
564 
565  doublereal TKelvin = temperature();
566  doublereal sqt = sqrt(TKelvin);
567  doublereal mv = molarVolume();
568 
569  doublereal rt = GasConstant * TKelvin;
570 
571  doublereal vmb = mv - m_b_current;
572  doublereal vpb = mv + m_b_current;
573 
574  for (size_t k = 0; k < m_kk; k++) {
575 
576  doublereal num = (rt + rt * m_b_current/ vmb + rt * b_vec_Curr_[k] / vmb
577  + rt * m_b_current * b_vec_Curr_[k] /(vmb * vmb)
578  - 2.0 * m_pp[k] / (sqt * vpb)
579  + m_a_current * b_vec_Curr_[k] / (sqt * vpb * vpb)
580  );
581 
582  doublereal denom = (m_Pcurrent + rt * m_b_current/(vmb * vmb) - m_a_current / (sqt * vpb * vpb)
583  );
584 
585  vbar[k] = num / denom;
586  }
587 
588 }
589 
591 {
592  double pc, tc, vc;
593  double a0 = 0.0;
594  double aT = 0.0;
595  for (size_t i = 0; i < m_kk; i++) {
596  for (size_t j = 0; j <m_kk; j++) {
597  size_t counter = i + m_kk * j;
598  a0 += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(0, counter);
599  aT += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(1, counter);
600  }
601  }
602  calcCriticalConditions(m_a_current, m_b_current, a0, aT, pc, tc, vc);
603  return tc;
604 }
605 
607 {
608  double pc, tc, vc;
609  double a0 = 0.0;
610  double aT = 0.0;
611  for (size_t i = 0; i < m_kk; i++) {
612  for (size_t j = 0; j <m_kk; j++) {
613  size_t counter = i + m_kk * j;
614  a0 += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(0, counter);
615  aT += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(1, counter);
616  }
617  }
618  calcCriticalConditions(m_a_current, m_b_current, a0, aT, pc, tc, vc);
619 
620  return pc;
621 }
622 
624 {
625  double pc, tc, vc;
626  double a0 = 0.0;
627  double aT = 0.0;
628  for (size_t i = 0; i < m_kk; i++) {
629  for (size_t j = 0; j <m_kk; j++) {
630  size_t counter = i + m_kk * j;
631  a0 += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(0, counter);
632  aT += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(1, counter);
633  }
634  }
635  calcCriticalConditions(m_a_current, m_b_current, a0, aT, pc, tc, vc);
636  return vc;
637 }
638 
640 {
641  double pc, tc, vc;
642  double a0 = 0.0;
643  double aT = 0.0;
644  for (size_t i = 0; i < m_kk; i++) {
645  for (size_t j = 0; j <m_kk; j++) {
646  size_t counter = i + m_kk * j;
647  a0 += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(0, counter);
648  aT += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(1, counter);
649  }
650  }
651  calcCriticalConditions(m_a_current, m_b_current, a0, aT, pc, tc, vc);
652  return pc*vc/tc/GasConstant;
653 }
654 
656 {
657  double pc, tc, vc;
658  double a0 = 0.0;
659  double aT = 0.0;
660  for (size_t i = 0; i < m_kk; i++) {
661  for (size_t j = 0; j <m_kk; j++) {
662  size_t counter = i + m_kk * j;
663  a0 += moleFractions_[i] * moleFractions_[j] *a_coeff_vec(0, counter);
664  aT += moleFractions_[i] * moleFractions_[j] *a_coeff_vec(1, counter);
665  }
666  }
667  calcCriticalConditions(m_a_current, m_b_current, a0, aT, pc, tc, vc);
668 
669  double mmw = meanMolecularWeight();
670  return mmw / vc;
671 }
672 
674 {
675  initLengths();
677 }
678 
679 void RedlichKwongMFTP::setToEquilState(const doublereal* mu_RT)
680 {
681  double tmp, tmp2;
683 
685 
686 
687  /*
688  * Within the method, we protect against inf results if the
689  * exponent is too high.
690  *
691  * If it is too low, we set
692  * the partial pressure to zero. This capability is needed
693  * by the elemental potential method.
694  */
695  doublereal pres = 0.0;
696  double m_p0 = refPressure();
697  for (size_t k = 0; k < m_kk; k++) {
698  tmp = -m_tmpV[k] + mu_RT[k];
699  if (tmp < -600.) {
700  m_pp[k] = 0.0;
701  } else if (tmp > 500.0) {
702  tmp2 = tmp / 500.;
703  tmp2 *= tmp2;
704  m_pp[k] = m_p0 * exp(500.) * tmp2;
705  } else {
706  m_pp[k] = m_p0 * exp(tmp);
707  }
708  pres += m_pp[k];
709  }
710  // set state
711  setState_PX(pres, &m_pp[0]);
712 }
713 
715 {
716  a_vec_Curr_.resize(m_kk * m_kk, 0.0);
717  b_vec_Curr_.resize(m_kk, 0.0);
718 
719  a_coeff_vec.resize(2, m_kk * m_kk, 0.0);
720 
721  m_pc_Species.resize(m_kk, 0.0);
722  m_tc_Species.resize(m_kk, 0.0);
723  m_vc_Species.resize(m_kk, 0.0);
724 
725  m_pp.resize(m_kk, 0.0);
726  m_tmpV.resize(m_kk, 0.0);
727  m_partialMolarVolumes.resize(m_kk, 0.0);
728  dpdni_.resize(m_kk, 0.0);
729 }
730 
731 void RedlichKwongMFTP::initThermoXML(XML_Node& phaseNode, const std::string& id)
732 {
734 
735  /*
736  * Check the model parameter for the Redlich-Kwong equation of state
737  * two are allowed
738  * RedlichKwong mixture of species, each of which are RK fluids
739  * RedlichKwongMFTP mixture of species with cross term coefficients
740  */
741  if (phaseNode.hasChild("thermo")) {
742  XML_Node& thermoNode = phaseNode.child("thermo");
743  std::string model = thermoNode["model"];
744  if (model == "RedlichKwong") {
746  } else if (model == "RedlichKwongMFTP") {
748  } else {
749  throw CanteraError("RedlichKwongMFTP::initThermoXML",
750  "Unknown thermo model : " + model);
751  }
752 
753  /*
754  * Go get all of the coefficients and factors in the
755  * activityCoefficients XML block
756  */
757  XML_Node* acNodePtr = 0;
758  if (thermoNode.hasChild("activityCoefficients")) {
759  XML_Node& acNode = thermoNode.child("activityCoefficients");
760  acNodePtr = &acNode;
761  size_t nC = acNode.nChildren();
762 
763  /*
764  * Loop through the children getting multiple instances of
765  * parameters
766  */
767  for (size_t i = 0; i < nC; i++) {
768  XML_Node& xmlACChild = acNodePtr->child(i);
769  string stemp = xmlACChild.name();
770  string nodeName = lowercase(stemp);
771  /*
772  * Process a binary salt field, or any of the other XML fields
773  * that make up the Pitzer Database. Entries will be ignored
774  * if any of the species in the entry isn't in the solution.
775  */
776  if (nodeName == "purefluidparameters") {
777  readXMLPureFluid(xmlACChild);
778  }
779  }
780  if (m_standardMixingRules == 1) {
782  }
783  /*
784  * Loop through the children getting multiple instances of
785  * parameters
786  */
787  for (size_t i = 0; i < nC; i++) {
788  XML_Node& xmlACChild = acNodePtr->child(i);
789  string stemp = xmlACChild.name();
790  string nodeName = lowercase(stemp);
791  /*
792  * Process a binary salt field, or any of the other XML fields
793  * that make up the Pitzer Database. Entries will be ignored
794  * if any of the species in the entry isn't in the solution.
795  */
796  if (nodeName == "crossfluidparameters") {
797  readXMLCrossFluid(xmlACChild);
798  }
799  }
800 
801  }
802  }
803 
804  for (size_t i = 0; i < m_kk; i++) {
805  double a0coeff = a_coeff_vec(0, i*m_kk + i);
806  double aTcoeff = a_coeff_vec(1, i*m_kk + i);
807  double ai = a0coeff + aTcoeff * 500.;
808  double bi = b_vec_Curr_[i];
809  calcCriticalConditions(ai, bi, a0coeff, aTcoeff, m_pc_Species[i], m_tc_Species[i], m_vc_Species[i]);
810  }
811 
812  MixtureFugacityTP::initThermoXML(phaseNode, id);
813 }
814 
816 {
817  vector_fp vParams;
818  string xname = pureFluidParam.name();
819  if (xname != "pureFluidParameters") {
820  throw CanteraError("RedlichKwongMFTP::readXMLPureFluid",
821  "Incorrect name for processing this routine: " + xname);
822  }
823 
824  /*
825  * Read the species
826  * Find the index of the species in the current phase. It's not an error to not find the species
827  */
828  string iName = pureFluidParam.attrib("species");
829  if (iName == "") {
830  throw CanteraError("RedlichKwongMFTP::readXMLPureFluid", "no species attribute");
831  }
832  size_t iSpecies = speciesIndex(iName);
833  if (iSpecies == npos) {
834  return;
835  }
836  size_t counter = iSpecies + m_kk * iSpecies;
837  size_t nParamsExpected, nParamsFound;
838  size_t num = pureFluidParam.nChildren();
839  for (size_t iChild = 0; iChild < num; iChild++) {
840  XML_Node& xmlChild = pureFluidParam.child(iChild);
841  string stemp = xmlChild.name();
842  string nodeName = lowercase(stemp);
843 
844  if (nodeName == "a_coeff") {
845  string iModel = lowercase(xmlChild.attrib("model"));
846  if (iModel == "constant") {
847  nParamsExpected = 1;
848  } else if (iModel == "linear_a") {
849  nParamsExpected = 2;
850  if (m_formTempParam == 0) {
851  m_formTempParam = 1;
852  }
853  } else {
854  throw CanteraError("RedlichKwongMFTP::readXMLPureFluid", "unknown model");
855  }
856 
857  getFloatArray(xmlChild, vParams, true, "Pascal-m6/kmol2", "a_coeff");
858  nParamsFound = vParams.size();
859  if (nParamsFound != nParamsExpected) {
860  throw CanteraError("RedlichKwongMFTP::readXMLPureFluid(for a_coeff" + iName + ")",
861  "wrong number of params found");
862  }
863 
864  for (size_t i = 0; i < nParamsFound; i++) {
865  a_coeff_vec(i, counter) = vParams[i];
866  }
867  } else if (nodeName == "b_coeff") {
868  getFloatArray(xmlChild, vParams, true, "m3/kmol", "b_coeff");
869  nParamsFound = vParams.size();
870  if (nParamsFound != 1) {
871  throw CanteraError("RedlichKwongMFTP::readXMLPureFluid(for b_coeff" + iName + ")",
872  "wrong number of params found");
873  }
874  b_vec_Curr_[iSpecies] = vParams[0];
875  }
876  }
877 }
878 
880 {
881  int nParam = 2;
882  for (size_t i = 0; i < m_kk; i++) {
883  size_t icounter = i + m_kk * i;
884  for (size_t j = 0; j < m_kk; j++) {
885  if (i != j) {
886  size_t counter = i + m_kk * j;
887  size_t jcounter = j + m_kk * j;
888  for (int n = 0; n < nParam; n++) {
889  a_coeff_vec(n, counter) = sqrt(a_coeff_vec(n, icounter) * a_coeff_vec(n, jcounter));
890  }
891  }
892  }
893  }
894 }
895 
897 {
898  vector_fp vParams;
899  string xname = CrossFluidParam.name();
900  if (xname != "crossFluidParameters") {
901  throw CanteraError("RedlichKwongMFTP::readXMLCrossFluid",
902  "Incorrect name for processing this routine: " + xname);
903  }
904 
905  /*
906  * Read the species
907  * Find the index of the species in the current phase. It's not an error to not find the species
908  */
909  string iName = CrossFluidParam.attrib("species1");
910  if (iName == "") {
911  throw CanteraError("RedlichKwongMFTP::readXMLCrossFluid", "no species1 attribute");
912  }
913  size_t iSpecies = speciesIndex(iName);
914  if (iSpecies == npos) {
915  return;
916  }
917  string jName = CrossFluidParam.attrib("species2");
918  if (iName == "") {
919  throw CanteraError("RedlichKwongMFTP::readXMLCrossFluid", "no species2 attribute");
920  }
921  size_t jSpecies = speciesIndex(jName);
922  if (jSpecies == npos) {
923  return;
924  }
925 
926  size_t counter = iSpecies + m_kk * jSpecies;
927  size_t counter0 = jSpecies + m_kk * iSpecies;
928  size_t nParamsExpected, nParamsFound;
929  size_t num = CrossFluidParam.nChildren();
930  for (size_t iChild = 0; iChild < num; iChild++) {
931  XML_Node& xmlChild = CrossFluidParam.child(iChild);
932  string stemp = xmlChild.name();
933  string nodeName = lowercase(stemp);
934 
935  if (nodeName == "a_coeff") {
936  string iModel = lowercase(xmlChild.attrib("model"));
937  if (iModel == "constant") {
938  nParamsExpected = 1;
939  } else if (iModel == "linear_a") {
940  nParamsExpected = 2;
941  if (m_formTempParam == 0) {
942  m_formTempParam = 1;
943  }
944  } else {
945  throw CanteraError("RedlichKwongMFTP::readXMLCrossFluid", "unknown model");
946  }
947 
948  getFloatArray(xmlChild, vParams, true, "Pascal-m6/kmol2", "a_coeff");
949  nParamsFound = vParams.size();
950  if (nParamsFound != nParamsExpected) {
951  throw CanteraError("RedlichKwongMFTP::readXMLCrossFluid(for a_coeff" + iName + ")",
952  "wrong number of params found");
953  }
954 
955  for (size_t i = 0; i < nParamsFound; i++) {
956  a_coeff_vec(i, counter) = vParams[i];
957  a_coeff_vec(i, counter0) = vParams[i];
958  }
959  }
960  }
961 }
962 
964 {
966  std::string model = thermoNode["model"];
967 }
968 
969 doublereal RedlichKwongMFTP::sresid() const
970 {
971  // note this agrees with tpx
972  doublereal rho = density();
973  doublereal mmw = meanMolecularWeight();
974  doublereal molarV = mmw / rho;
975  double hh = m_b_current / molarV;
976  doublereal zz = z();
977  doublereal dadt = da_dt();
978  doublereal T = temperature();
979  doublereal sqT = sqrt(T);
980  doublereal fac = dadt - m_a_current / (2.0 * T);
981  double sresid_mol_R = log(zz*(1.0 - hh)) + log(1.0 + hh) * fac / (sqT * GasConstant * m_b_current);
982  return GasConstant * sresid_mol_R;
983 }
984 
985 doublereal RedlichKwongMFTP::hresid() const
986 {
987  // note this agrees with tpx
988  doublereal rho = density();
989  doublereal mmw = meanMolecularWeight();
990  doublereal molarV = mmw / rho;
991  double hh = m_b_current / molarV;
992  doublereal zz = z();
993  doublereal dadt = da_dt();
994  doublereal T = temperature();
995  doublereal sqT = sqrt(T);
996  doublereal fac = T * dadt - 3.0 *m_a_current / (2.0);
997  return GasConstant * T * (zz - 1.0) + fac * log(1.0 + hh) / (sqT * m_b_current);
998 }
999 
1000 doublereal RedlichKwongMFTP::liquidVolEst(doublereal TKelvin, doublereal& presGuess) const
1001 {
1002  double v = m_b_current * 1.1;
1003  double atmp;
1004  double btmp;
1005  calculateAB(TKelvin, atmp, btmp);
1006 
1007  doublereal pres = std::max(psatEst(TKelvin), presGuess);
1008  double Vroot[3];
1009 
1010  bool foundLiq = false;
1011  int m = 0;
1012  do {
1013 
1014  int nsol = NicholsSolve(TKelvin, pres, atmp, btmp, Vroot);
1015 
1016  if (nsol == 1 || nsol == 2) {
1017  double pc = critPressure();
1018  if (pres > pc) {
1019  foundLiq = true;
1020  }
1021  pres *= 1.04;
1022 
1023  } else {
1024  foundLiq = true;
1025  }
1026  } while ((m < 100) && (!foundLiq));
1027 
1028  if (foundLiq) {
1029  v = Vroot[0];
1030  presGuess = pres;
1031  } else {
1032  v = -1.0;
1033  }
1034  return v;
1035 }
1036 
1037 doublereal RedlichKwongMFTP::densityCalc(doublereal TKelvin, doublereal presPa, int phaseRequested, doublereal rhoguess)
1038 {
1039 
1040  /*
1041  * It's necessary to set the temperature so that m_a_current is set correctly.
1042  */
1043  setTemperature(TKelvin);
1044  double tcrit = critTemperature();
1045  doublereal mmw = meanMolecularWeight();
1046  if (rhoguess == -1.0) {
1047  if (phaseRequested != FLUID_GAS) {
1048  if (TKelvin > tcrit) {
1049  rhoguess = presPa * mmw / (GasConstant * TKelvin);
1050  } else {
1051  if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
1052  rhoguess = presPa * mmw / (GasConstant * TKelvin);
1053  } else if (phaseRequested >= FLUID_LIQUID_0) {
1054  double lqvol = liquidVolEst(TKelvin, presPa);
1055  rhoguess = mmw / lqvol;
1056  }
1057  }
1058  } else {
1059  /*
1060  * Assume the Gas phase initial guess, if nothing is
1061  * specified to the routine
1062  */
1063  rhoguess = presPa * mmw / (GasConstant * TKelvin);
1064  }
1065 
1066  }
1067 
1068  doublereal volguess = mmw / rhoguess;
1069  NSolns_ = NicholsSolve(TKelvin, presPa, m_a_current, m_b_current, Vroot_);
1070 
1071  doublereal molarVolLast = Vroot_[0];
1072  if (NSolns_ >= 2) {
1073  if (phaseRequested >= FLUID_LIQUID_0) {
1074  molarVolLast = Vroot_[0];
1075  } else if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
1076  molarVolLast = Vroot_[2];
1077  } else {
1078  if (volguess > Vroot_[1]) {
1079  molarVolLast = Vroot_[2];
1080  } else {
1081  molarVolLast = Vroot_[0];
1082  }
1083  }
1084  } else if (NSolns_ == 1) {
1085  if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT || phaseRequested == FLUID_UNDEFINED) {
1086  molarVolLast = Vroot_[0];
1087  } else {
1088  return -2.0;
1089  }
1090  } else if (NSolns_ == -1) {
1091  if (phaseRequested >= FLUID_LIQUID_0 || phaseRequested == FLUID_UNDEFINED || phaseRequested == FLUID_SUPERCRIT) {
1092  molarVolLast = Vroot_[0];
1093  } else if (TKelvin > tcrit) {
1094  molarVolLast = Vroot_[0];
1095  } else {
1096  return -2.0;
1097  }
1098  } else {
1099  molarVolLast = Vroot_[0];
1100  return -1.0;
1101  }
1102  return mmw / molarVolLast;
1103 }
1104 
1106 {
1107  if (NSolns_ != 3) {
1108  return critDensity();
1109  }
1110  double vmax = Vroot_[1];
1111  double vmin = Vroot_[0];
1112  RootFind rf(fdpdv_);
1113  rf.setPrintLvl(10);
1114  rf.setTol(1.0E-5, 1.0E-10);
1116 
1117  double vbest = 0.5 * (Vroot_[0]+Vroot_[1]);
1118  double funcNeeded = 0.0;
1119 
1120  int status = rf.solve(vmin, vmax, 100, funcNeeded, &vbest);
1121  if (status != ROOTFIND_SUCCESS) {
1122  throw CanteraError(" RedlichKwongMFTP::densSpinodalLiquid() ", "didn't converge");
1123  }
1124  doublereal mmw = meanMolecularWeight();
1125  return mmw / vbest;
1126 }
1127 
1129 {
1130  if (NSolns_ != 3) {
1131  return critDensity();
1132  }
1133  double vmax = Vroot_[2];
1134  double vmin = Vroot_[1];
1135  RootFind rf(fdpdv_);
1136  rf.setPrintLvl(10);
1137  rf.setTol(1.0E-5, 1.0E-10);
1139 
1140  double vbest = 0.5 * (Vroot_[1]+Vroot_[2]);
1141  double funcNeeded = 0.0;
1142 
1143  int status = rf.solve(vmin, vmax, 100, funcNeeded, &vbest);
1144  if (status != ROOTFIND_SUCCESS) {
1145  throw CanteraError(" RedlichKwongMFTP::densSpinodalGas() ", "didn't converge");
1146  }
1147  doublereal mmw = meanMolecularWeight();
1148  return mmw / vbest;
1149 }
1150 
1151 doublereal RedlichKwongMFTP::pressureCalc(doublereal TKelvin, doublereal molarVol) const
1152 {
1153  doublereal sqt = sqrt(TKelvin);
1154  double pres = GasConstant * TKelvin / (molarVol - m_b_current)
1155  - m_a_current / (sqt * molarVol * (molarVol + m_b_current));
1156  return pres;
1157 }
1158 
1159 doublereal RedlichKwongMFTP::dpdVCalc(doublereal TKelvin, doublereal molarVol, doublereal& presCalc) const
1160 {
1161  doublereal sqt = sqrt(TKelvin);
1162  presCalc = GasConstant * TKelvin / (molarVol - m_b_current)
1163  - m_a_current / (sqt * molarVol * (molarVol + m_b_current));
1164 
1165  doublereal vpb = molarVol + m_b_current;
1166  doublereal vmb = molarVol - m_b_current;
1167  doublereal dpdv = (- GasConstant * TKelvin / (vmb * vmb)
1168  + m_a_current * (2 * molarVol + m_b_current) / (sqt * molarVol * molarVol * vpb * vpb));
1169  return dpdv;
1170 }
1171 
1173 {
1174  doublereal TKelvin = temperature();
1175  doublereal mv = molarVolume();
1176  doublereal pres;
1177 
1178  dpdV_ = dpdVCalc(TKelvin, mv, pres);
1179 
1180  doublereal sqt = sqrt(TKelvin);
1181  doublereal vpb = mv + m_b_current;
1182  doublereal vmb = mv - m_b_current;
1183  doublereal dadt = da_dt();
1184  doublereal fac = dadt - m_a_current/(2.0 * TKelvin);
1185 
1186  dpdT_ = (GasConstant / (vmb) - fac / (sqt * mv * vpb));
1187 }
1188 
1189 void RedlichKwongMFTP::updateMixingExpressions()
1190 {
1191  updateAB();
1192 }
1193 
1195 {
1196  double temp = temperature();
1197  if (m_formTempParam == 1) {
1198  for (size_t i = 0; i < m_kk; i++) {
1199  for (size_t j = 0; j < m_kk; j++) {
1200  size_t counter = i * m_kk + j;
1201  a_vec_Curr_[counter] = a_coeff_vec(0,counter) + a_coeff_vec(1,counter) * temp;
1202  }
1203  }
1204  }
1205 
1206  m_b_current = 0.0;
1207  m_a_current = 0.0;
1208  for (size_t i = 0; i < m_kk; i++) {
1209  m_b_current += moleFractions_[i] * b_vec_Curr_[i];
1210  for (size_t j = 0; j < m_kk; j++) {
1211  m_a_current += a_vec_Curr_[i * m_kk + j] * moleFractions_[i] * moleFractions_[j];
1212  }
1213  }
1214 }
1215 
1216 void RedlichKwongMFTP::calculateAB(doublereal temp, doublereal& aCalc, doublereal& bCalc) const
1217 {
1218  bCalc = 0.0;
1219  aCalc = 0.0;
1220  if (m_formTempParam == 1) {
1221  for (size_t i = 0; i < m_kk; i++) {
1222  bCalc += moleFractions_[i] * b_vec_Curr_[i];
1223  for (size_t j = 0; j < m_kk; j++) {
1224  size_t counter = i * m_kk + j;
1225  doublereal a_vec_Curr = a_coeff_vec(0,counter) + a_coeff_vec(1,counter) * temp;
1226  aCalc += a_vec_Curr * moleFractions_[i] * moleFractions_[j];
1227  }
1228  }
1229  } else {
1230  for (size_t i = 0; i < m_kk; i++) {
1231  bCalc += moleFractions_[i] * b_vec_Curr_[i];
1232  for (size_t j = 0; j < m_kk; j++) {
1233  size_t counter = i * m_kk + j;
1234  doublereal a_vec_Curr = a_coeff_vec(0,counter);
1235  aCalc += a_vec_Curr * moleFractions_[i] * moleFractions_[j];
1236  }
1237  }
1238  }
1239 }
1240 
1241 doublereal RedlichKwongMFTP::da_dt() const
1242 {
1243  doublereal dadT = 0.0;
1244  if (m_formTempParam == 1) {
1245  for (size_t i = 0; i < m_kk; i++) {
1246  for (size_t j = 0; j < m_kk; j++) {
1247  size_t counter = i * m_kk + j;
1248  dadT+= a_coeff_vec(1,counter) * moleFractions_[i] * moleFractions_[j];
1249  }
1250  }
1251  }
1252  return dadT;
1253 }
1254 
1255 void RedlichKwongMFTP::calcCriticalConditions(doublereal a, doublereal b, doublereal a0_coeff, doublereal aT_coeff,
1256  doublereal& pc, doublereal& tc, doublereal& vc) const
1257 {
1258  if (m_formTempParam != 0) {
1259  a = a0_coeff;
1260  }
1261  if (b <= 0.0) {
1262  tc = 1000000.;
1263  pc = 1.0E13;
1264  vc = omega_vc * GasConstant * tc / pc;
1265  return;
1266  }
1267  if (a <= 0.0) {
1268  tc = 0.0;
1269  pc = 0.0;
1270  vc = 2.0 * b;
1271  return;
1272  }
1273  double tmp = a * omega_b / (b * omega_a * GasConstant);
1274  double pp = 2./3.;
1275  doublereal sqrttc, f, dfdt, deltatc;
1276 
1277  if (m_formTempParam == 0) {
1278 
1279  tc = pow(tmp, pp);
1280  } else {
1281  tc = pow(tmp, pp);
1282  for (int j = 0; j < 10; j++) {
1283  sqrttc = sqrt(tc);
1284  f = omega_a * b * GasConstant * tc * sqrttc / omega_b - aT_coeff * tc - a0_coeff;
1285  dfdt = 1.5 * omega_a * b * GasConstant * sqrttc / omega_b - aT_coeff;
1286  deltatc = - f / dfdt;
1287  tc += deltatc;
1288  }
1289  if (deltatc > 0.1) {
1290  throw CanteraError("RedlichKwongMFTP::calcCriticalConditions", "didn't converge");
1291  }
1292  }
1293 
1294  pc = omega_b * GasConstant * tc / b;
1295  vc = omega_vc * GasConstant * tc / pc;
1296 }
1297 
1298 int RedlichKwongMFTP::NicholsSolve(double TKelvin, double pres, doublereal a, doublereal b,
1299  doublereal Vroot[3]) const
1300 {
1301  Vroot[0] = 0.0;
1302  Vroot[1] = 0.0;
1303  Vroot[2] = 0.0;
1304  if (TKelvin <= 0.0) {
1305  throw CanteraError("RedlichKwongMFTP::NicholsSolve()", "neg temperature");
1306  }
1307  /*
1308  * Derive the coefficients of the cubic polynomial to solve.
1309  */
1310  doublereal an = 1.0;
1311  doublereal bn = - GasConstant * TKelvin / pres;
1312  doublereal sqt = sqrt(TKelvin);
1313  doublereal cn = - (GasConstant * TKelvin * b / pres - a/(pres * sqt) + b * b);
1314  doublereal dn = - (a * b / (pres * sqt));
1315 
1316  double tmp = a * omega_b / (b * omega_a * GasConstant);
1317  double pp = 2./3.;
1318  double tc = pow(tmp, pp);
1319  double pc = omega_b * GasConstant * tc / b;
1320  double vc = omega_vc * GasConstant * tc / pc;
1321  // Derive the center of the cubic, x_N
1322  doublereal xN = - bn /(3 * an);
1323 
1324 
1325  // Derive the value of delta**2. This is a key quantity that determines the number of turning points
1326  doublereal delta2 = (bn * bn - 3 * an * cn) / (9 * an * an);
1327  doublereal delta = 0.0;
1328 
1329  // Calculate a couple of ratios
1330  doublereal ratio1 = 3.0 * an * cn / (bn * bn);
1331  doublereal ratio2 = pres * b / (GasConstant * TKelvin);
1332  if (fabs(ratio1) < 1.0E-7) {
1333  //printf("NicholsSolve(): Alternative solution (p = %g T = %g)\n", pres, TKelvin);
1334  doublereal ratio3 = a / (GasConstant * sqt) * pres / (GasConstant * TKelvin);
1335  if (fabs(ratio2) < 1.0E-5 && fabs(ratio3) < 1.0E-5) {
1336  doublereal zz = 1.0;
1337  for (int i = 0; i < 10; i++) {
1338  doublereal znew = zz / (zz - ratio2) - ratio3 / (zz + ratio1);
1339  doublereal deltaz = znew - zz;
1340  zz = znew;
1341  if (fabs(deltaz) < 1.0E-14) {
1342  break;
1343  }
1344  }
1345  doublereal v = zz * GasConstant * TKelvin / pres;
1346  Vroot[0] = v;
1347  return 1;
1348  }
1349  }
1350 
1351  int nSolnValues;
1352 
1353  double h2 = 4. * an * an * delta2 * delta2 * delta2;
1354  if (delta2 > 0.0) {
1355  delta = sqrt(delta2);
1356  }
1357 
1358  doublereal h = 2.0 * an * delta * delta2;
1359 
1360  doublereal yN = 2.0 * bn * bn * bn / (27.0 * an * an) - bn * cn / (3.0 * an) + dn;
1361 
1362  doublereal desc = yN * yN - h2;
1363 
1364  if (fabs(fabs(h) - fabs(yN)) < 1.0E-10) {
1365  if (desc != 0.0) {
1366  // this is for getting to other cases
1367  printf("NicholsSolve(): numerical issues\n");
1368  throw CanteraError("NicholsSolve()", "numerical issues");
1369  }
1370  desc = 0.0;
1371  }
1372 
1373  if (desc < 0.0) {
1374  nSolnValues = 3;
1375  } else if (desc == 0.0) {
1376  nSolnValues = 2;
1377  // We are here as p goes to zero.
1378  } else if (desc > 0.0) {
1379  nSolnValues = 1;
1380  }
1381 
1382  /*
1383  * One real root -> have to determine whether gas or liquid is the root
1384  */
1385  if (desc > 0.0) {
1386  doublereal tmpD = sqrt(desc);
1387  doublereal tmp1 = (- yN + tmpD) / (2.0 * an);
1388  doublereal sgn1 = 1.0;
1389  if (tmp1 < 0.0) {
1390  sgn1 = -1.0;
1391  tmp1 = -tmp1;
1392  }
1393  doublereal tmp2 = (- yN - tmpD) / (2.0 * an);
1394  doublereal sgn2 = 1.0;
1395  if (tmp2 < 0.0) {
1396  sgn2 = -1.0;
1397  tmp2 = -tmp2;
1398  }
1399  doublereal p1 = pow(tmp1, 1./3.);
1400  doublereal p2 = pow(tmp2, 1./3.);
1401 
1402  doublereal alpha = xN + sgn1 * p1 + sgn2 * p2;
1403  Vroot[0] = alpha;
1404  Vroot[1] = 0.0;
1405  Vroot[2] = 0.0;
1406 
1407  tmp = an * Vroot[0] * Vroot[0] * Vroot[0] + bn * Vroot[0] * Vroot[0] + cn * Vroot[0] + dn;
1408 
1409  } else if (desc < 0.0) {
1410  doublereal tmp = - yN/h;
1411 
1412  doublereal val = acos(tmp);
1413  doublereal theta = val / 3.0;
1414 
1415  doublereal oo = 2. * Cantera::Pi / 3.;
1416  doublereal alpha = xN + 2. * delta * cos(theta);
1417 
1418  doublereal beta = xN + 2. * delta * cos(theta + oo);
1419 
1420  doublereal gamma = xN + 2. * delta * cos(theta + 2.0 * oo);
1421 
1422 
1423  Vroot[0] = beta;
1424  Vroot[1] = gamma;
1425  Vroot[2] = alpha;
1426 
1427  for (int i = 0; i < 3; i++) {
1428  tmp = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
1429  if (fabs(tmp) > 1.0E-4) {
1430  for (int j = 0; j < 3; j++) {
1431  if (j != i) {
1432  if (fabs(Vroot[i] - Vroot[j]) < 1.0E-4 * (fabs(Vroot[i]) + fabs(Vroot[j]))) {
1433  writelog("RedlichKwongMFTP::NicholsSolve(T = " + fp2str(TKelvin) + ", p = " +
1434  fp2str(pres) + "): WARNING roots have merged: " +
1435  fp2str(Vroot[i]) + ", " + fp2str(Vroot[j]));
1436  writelogendl();
1437  }
1438  }
1439  }
1440  }
1441  }
1442  } else if (desc == 0.0) {
1443  if (yN == 0.0 && h == 0.0) {
1444  Vroot[0] = xN;
1445  Vroot[1] = xN;
1446  Vroot[2] = xN;
1447  } else {
1448  // need to figure out whether delta is pos or neg
1449  if (yN > 0.0) {
1450  tmp = pow(yN/(2*an), 1./3.);
1451  if (fabs(tmp - delta) > 1.0E-9) {
1452  throw CanteraError("RedlichKwongMFTP::NicholsSolve()", "unexpected");
1453  }
1454  Vroot[1] = xN + delta;
1455  Vroot[0] = xN - 2.0*delta; // liquid phase root
1456  } else {
1457  tmp = pow(yN/(2*an), 1./3.);
1458  if (fabs(tmp - delta) > 1.0E-9) {
1459  throw CanteraError("RedlichKwongMFTP::NicholsSolve()", "unexpected");
1460  }
1461  delta = -delta;
1462  Vroot[0] = xN + delta;
1463  Vroot[1] = xN - 2.0*delta; // gas phase root
1464  }
1465  }
1466  for (int i = 0; i < 2; i++) {
1467  tmp = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
1468  }
1469  }
1470 
1471  /*
1472  * Unfortunately, there is a heavy amount of roundoff error due to bad conditioning in this
1473  */
1474  double res, dresdV = 0.0;
1475  for (int i = 0; i < nSolnValues; i++) {
1476  for (int n = 0; n < 20; n++) {
1477  res = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
1478  if (fabs(res) < 1.0E-14) {
1479  break;
1480  }
1481  dresdV = 3.0 * an * Vroot[i] * Vroot[i] + 2.0 * bn * Vroot[i] + cn;
1482  double del = - res / dresdV;
1483 
1484  Vroot[i] += del;
1485  if (fabs(del) / (fabs(Vroot[i]) + fabs(del)) < 1.0E-14) {
1486  break;
1487  }
1488  double res2 = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
1489  if (fabs(res2) < fabs(res)) {
1490  continue;
1491  } else {
1492  Vroot[i] -= del;
1493  Vroot[i] += 0.1 * del;
1494  }
1495  }
1496  if ((fabs(res) > 1.0E-14) && (fabs(res) > 1.0E-14 * fabs(dresdV) * fabs(Vroot[i]))) {
1497  writelog("RedlichKwongMFTP::NicholsSolve(T = " + fp2str(TKelvin) + ", p = " +
1498  fp2str(pres) + "): WARNING root didn't converge V = " + fp2str(Vroot[i]));
1499  writelogendl();
1500  }
1501  }
1502 
1503  if (nSolnValues == 1) {
1504  if (TKelvin > tc) {
1505  if (Vroot[0] < vc) {
1506  nSolnValues = -1;
1507  }
1508  } else {
1509  if (Vroot[0] < xN) {
1510  nSolnValues = -1;
1511  }
1512  }
1513 
1514  } else {
1515  if (nSolnValues == 2) {
1516  if (delta > 0.0) {
1517  nSolnValues = -2;
1518  }
1519  }
1520  }
1521  return nSolnValues;
1522 }
1523 
1524 }
int NicholsSolve(double TKelvin, double pres, doublereal a, doublereal b, doublereal Vroot[3]) const
Solve the cubic equation of state.
RedlichKwongMFTP & operator=(const RedlichKwongMFTP &right)
Assignment operator.
virtual doublereal density() const
Density (kg/m^3).
Definition: Phase.h:608
This class can handle either an ideal solution or an ideal gas approximation of a phase...
doublereal m_b_current
Value of b in the equation of state.
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Initialize a ThermoPhase object, potentially reading activity coefficient information from an XML dat...
doublereal dpdV_
The derivative of the pressure wrt the volume.
XML_Node * get_XML_File(const std::string &file, int debug)
Return a pointer to the XML tree for a Cantera input file.
Definition: global.cpp:105
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
virtual void getPartialMolarIntEnergies(doublereal *ubar) const
Get the species partial molar enthalpies. Units: J/kmol.
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:527
void resize(size_t n, size_t m, doublereal v=0.0)
Resize the array, and fill the new entries with 'v'.
Definition: Array.h:121
virtual doublereal pressure() const
Return the thermodynamic pressure (Pa).
vector_fp m_tmpV
Temporary storage - length = m_kk.
virtual void getGibbs_ref(doublereal *g) const
virtual doublereal entropy_mole() const
Molar entropy. Units: J/kmol/K.
doublereal _RT() const
Return the Gas Constant multiplied by the current temperature.
Definition: ThermoPhase.h:936
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:165
RedlichKwongMFTP()
Base constructor.
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
doublereal z() const
Calculate the value of z.
virtual void getCp_R(doublereal *cpr) const
Get the nondimensional Heat Capacities at constant pressure for the standard state of the species at ...
virtual doublereal densityCalc(doublereal TKelvin, doublereal pressure, int phase, doublereal rhoguess)
Calculates the density given the temperature and the pressure and a guess at the density.
virtual void setMoleFractions(const doublereal *const x)
Set the mole fractions to the specified values, and then normalize them so that they sum to 1...
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:100
virtual ThermoPhase * duplMyselfAsThermoPhase() const
Duplicator from the ThermoPhase parent class.
virtual doublereal enthalpy_mole() const
Molar enthalpy. Units: J/kmol.
MixtureFugacityTP & operator=(const MixtureFugacityTP &b)
Assignment operator.
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Definition: global.cpp:78
Header file for implicit nonlinear solver of a one dimensional function (see Numerical Utilities with...
void readXMLPureFluid(XML_Node &pureFluidParam)
Read the pure species RedlichKwong input parameters.
virtual void getPartialMolarCp(doublereal *cpbar) const
Get the partial molar heat capacities Units: J/kmol/K.
virtual int eosType() const
Equation of state type flag.
void setFuncIsGenerallyDecreasing(bool value)
Set the function behavior flag.
Definition: RootFind.cpp:1114
virtual void getGibbs_RT_ref(doublereal *grt) const
Returns the vector of nondimensional Gibbs free energies of the reference state at the current temper...
const doublereal Pi
Pi.
Definition: ct_defs.h:51
std::string lowercase(const std::string &s)
Cast a copy of a string to lower case.
Definition: stringUtils.cpp:73
virtual void setMassFractions(const doublereal *const y)
Set the mass fractions to the specified values, and then normalize them so that they sum to 1...
virtual void getEnthalpy_RT_ref(doublereal *hrt) const
Returns the vector of nondimensional enthalpies of the reference state at the current temperature of ...
virtual void getPartialMolarVolumes(doublereal *vbar) const
Get the species partial molar volumes. Units: m^3/kmol.
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Initialize a ThermoPhase object, potentially reading activity coefficient information from an XML dat...
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:573
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:97
void setToEquilState(const doublereal *lambda_RT)
This method is used by the ChemEquil equilibrium solver.
vector_fp m_s0_R
Temporary storage for dimensionless reference state entropies.
virtual void getStandardVolumes(doublereal *vol) const
Get the molar volumes of each species in their standard states at the current T and P of the solution...
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition: Phase.cpp:687
doublereal m_Pcurrent
Current value of the pressures.
int m_standardMixingRules
boolean indicating whether standard mixing rules are applied
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:257
static const doublereal omega_b
Omega constant for b.
bool importPhase(XML_Node &phase, ThermoPhase *th, SpeciesThermoFactory *spfactory)
Import a phase information into an empty ThermoPhase object.
virtual void setMoleFractions_NoNorm(const doublereal *const x)
Set the mole fractions to the specified values without normalizing.
int m_formTempParam
Form of the temperature parameterization.
virtual void setMassFractions_NoNorm(const doublereal *const y)
Set the mass fractions to the specified values without normalizing.
void setTol(doublereal rtolf, doublereal atolf, doublereal rtolx=0.0, doublereal atolx=0.0)
Set the tolerance parameters for the rootfinder.
Definition: RootFind.cpp:1085
virtual void setMassFractions(const doublereal *const y)
Set the mass fractions to the specified values, and then normalize them so that they sum to 1...
virtual doublereal psatEst(doublereal TKelvin) const
Estimate for the saturation pressure.
Root finder for 1D problems.
Definition: RootFind.h:131
void setFuncIsGenerallyIncreasing(bool value)
Set the function behavior flag.
Definition: RootFind.cpp:1106
doublereal dpdT_
The derivative of the pressure wrt the temperature.
doublereal m_a_current
Value of a in the equation of state.
virtual doublereal sresid() const
Calculate the deviation terms for the total entropy of the mixture from the ideal gas mixture...
doublereal molarVolume() const
Molar volume (m^3/kmol).
Definition: Phase.cpp:673
virtual void setConcentrations(const doublereal *const c)
Set the concentrations to the specified values within the phase.
doublereal sum_xlogx() const
Evaluate .
Definition: Phase.cpp:703
void pressureDerivatives() const
Calculate dpdV and dpdT at the current conditions.
std::string fp2str(const double x, const std::string &fmt)
Convert a double into a c++ string.
Definition: stringUtils.cpp:28
virtual void setMoleFractions(const doublereal *const x)
Set the mole fractions to the specified values, and then normalize them so that they sum to 1...
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
vector_fp m_pp
Temporary storage - length = m_kk.
std::string name() const
Returns the name of the XML node.
Definition: xml.h:394
virtual void setParametersFromXML(const XML_Node &thermoNode)
Set equation of state parameter values from XML entries.
Definition file for a derived class of ThermoPhase that assumes either an ideal gas or ideal solution...
virtual doublereal dpdVCalc(doublereal TKelvin, doublereal molarVol, doublereal &presCalc) const
Calculate the pressure and the pressure derivative given the temperature and the molar volume...
virtual void setMassFractions_NoNorm(const doublereal *const y)
Set the mass fractions to the specified values without normalizing.
virtual doublereal standardConcentration(size_t k=0) const
Returns the standard concentration , which is used to normalize the generalized concentration.
std::vector< doublereal > moleFractions_
Storage for the current values of the mole fractions of the species.
virtual doublereal refPressure(size_t k=npos) const =0
The reference-state pressure for species k.
virtual void setConcentrations(const doublereal *const c)
Set the concentrations to the specified values within the phase.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:99
virtual doublereal cp_mole() const
Molar heat capacity at constant pressure. Units: J/kmol/K.
virtual void setTemperature(const doublereal temp)
Set the temperature (K)
virtual doublereal hresid() const
Calculate the deviation terms for the total enthalpy of the mixture from the ideal gas mixture...
virtual doublereal critDensity() const
Critical density (kg/m3).
void readXMLCrossFluid(XML_Node &pureFluidParam)
Read the cross species RedlichKwong input parameters.
virtual doublereal liquidVolEst(doublereal TKelvin, doublereal &pres) const
Estimate for the molar volume of the liquid.
virtual void calcDensity()
Calculate the density of the mixture using the partial molar volumes and mole fractions as input...
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:563
doublereal dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
Definition: utilities.h:135
static const doublereal omega_a
Omega constant for a -> value of a in terms of critical properties.
virtual void getActivityConcentrations(doublereal *c) const
This method returns an array of generalized concentrations.
virtual void getIntEnergy_RT(doublereal *urt) const
Returns the vector of nondimensional internal Energies of the standard state at the current temperatu...
virtual doublereal refPressure() const
Returns the reference pressure in Pa.
Definition: ThermoPhase.h:150
int solve(doublereal xmin, doublereal xmax, int itmax, doublereal &funcTargetValue, doublereal *xbest)
Using a line search method, find the root of a 1D function.
Definition: RootFind.cpp:171
virtual doublereal densSpinodalGas() const
Return the value of the density at the gas spinodal point (on the gas side) for the current temperatu...
void writelogendl()
Write an end of line character to the screen and flush output.
Definition: global.cpp:56
doublereal moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition: Phase.cpp:561
doublereal temperature() const
Temperature (K).
Definition: Phase.h:602
size_t nDim() const
Returns the number of spatial dimensions (1, 2, or 3)
Definition: Phase.h:586
virtual doublereal critPressure() const
Critical pressure (Pa).
virtual doublereal critCompressibility() const
Critical compressibility (unitless).
const doublereal SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:126
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:157
void calculateAB(doublereal temp, doublereal &aCalc, doublereal &bCalc) const
Calculate the a and the b parameters given the temperature.
virtual void setTemperature(const doublereal temp)
Set the internally stored temperature of the phase (K).
Definition: Phase.h:638
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
Definition: utilities.h:158
size_t getFloatArray(const XML_Node &node, std::vector< doublereal > &v, const bool convert, const std::string &unitsString, const std::string &nodeName)
This function reads the current node or a child node of the current node with the default name...
Definition: ctml.cpp:323
virtual void setParametersFromXML(const XML_Node &eosdata)
Set equation of state parameter values from XML entries.
Definition: ThermoPhase.h:1470
doublereal meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition: Phase.h:669
const doublereal GasConstant
Universal Gas Constant. [J/kmol/K].
Definition: ct_defs.h:64
virtual void setState_PX(doublereal p, doublereal *x)
Set the pressure (Pa) and mole fractions.
Contains declarations for string manipulation functions within Cantera.
#define DATA_PTR(vec)
Creates a pointer to the start of the raw data for a vector.
Definition: ct_defs.h:36
virtual void getPartialMolarEnthalpies(doublereal *hbar) const
Get the species partial molar enthalpies. Units: J/kmol.
static const doublereal omega_vc
Omega constant for the critical molar volume.
virtual doublereal densSpinodalLiquid() const
Return the value of the density at the liquid spinodal point (on the liquid side) for the current tem...
virtual doublereal cv_mole() const
Molar heat capacity at constant volume. Units: J/kmol/K.
void writelog(const std::string &msg)
Write a message to the screen.
Definition: global.cpp:33
virtual void getUnitsStandardConc(double *uA, int k=0, int sizeUA=6) const
Returns the units of the standard and generalized concentrations.
size_t m_kk
Number of species in the phase.
Definition: Phase.h:843
void applyStandardMixingRules()
Apply mixing rules for a coefficients.
virtual void getEntropy_R_ref(doublereal *er) const
virtual doublereal pressureCalc(doublereal TKelvin, doublereal molarVol) const
Calculate the pressure given the temperature and the molar volume.
void setPrintLvl(int printLvl)
Set the print level from the rootfinder.
Definition: RootFind.cpp:1101
virtual void _updateReferenceStateThermo() const
Updates the reference state thermodynamic functions at the current T of the solution.
void getChemPotentials_RT(doublereal *mu) const
Get the array of non-dimensional species chemical potentials.
virtual doublereal critTemperature() const
Critical temperature (K).
virtual void getPartialMolarEntropies(doublereal *sbar) const
Get the species partial molar entropies. Units: J/kmol/K.
#define ROOTFIND_SUCCESS
This means that the root solver was a success.
Definition: RootFind.h:29
vector_fp dpdni_
Vector of derivatives of pressure wrt mole number.
SpeciesThermo * m_spthermo
Pointer to the calculation manager for species reference-state thermodynamic properties.
Definition: ThermoPhase.h:1607
void updateAB()
Update the a and b parameters.
const doublereal * moleFractdivMMW() const
Returns a const pointer to the start of the moleFraction/MW array.
Definition: Phase.cpp:577
XML_Node * get_XML_NameID(const std::string &nameTarget, const std::string &file_ID, XML_Node *root)
This routine will locate an XML node in either the input XML tree or in another input file specified ...
Definition: global.cpp:252
size_t nChildren(bool discardComments=false) const
Return the number of children.
Definition: xml.cpp:583
virtual void getActivityCoefficients(doublereal *ac) const
Get the array of non-dimensional activity coefficients at the current solution temperature, pressure, and solution concentration.
virtual void setDensity(const doublereal density_)
Set the internally stored density (kg/m^3) of the phase Note the density of a phase is an independent...
Definition: Phase.h:623
vector_fp m_h0_RT
Temporary storage for dimensionless reference state enthalpies.
vector_fp m_cp0_R
Temporary storage for dimensionless reference state heat capacities.
virtual doublereal critVolume() const
Critical volume (m3/kmol)