Cantera  2.3.0
MixtureFugacityTP.cpp
Go to the documentation of this file.
1 /**
2  * @file MixtureFugacityTP.cpp
3  * Methods file for a derived class of ThermoPhase that handles
4  * non-ideal mixtures based on the fugacity models (see \ref thermoprops and
5  * class \link Cantera::MixtureFugacityTP MixtureFugacityTP\endlink).
6  */
7 
8 // This file is part of Cantera. See License.txt in the top-level directory or
9 // at http://www.cantera.org/license.txt for license and copyright information.
10 
13 #include "cantera/base/ctml.h"
14 
15 using namespace std;
16 
17 namespace Cantera
18 {
19 
20 MixtureFugacityTP::MixtureFugacityTP() :
21  m_Pcurrent(-1.0),
22  iState_(FLUID_GAS),
23  forcedState_(FLUID_UNDEFINED),
24  m_Tlast_ref(-1.0)
25 {
26 }
27 
29  m_Pcurrent(-1.0),
30  iState_(FLUID_GAS),
31  forcedState_(FLUID_UNDEFINED),
32  m_Tlast_ref(-1.0)
33 {
34  MixtureFugacityTP::operator=(b);
35 }
36 
37 MixtureFugacityTP& MixtureFugacityTP::operator=(const MixtureFugacityTP& b)
38 {
39  if (&b != this) {
40  // Mostly, this is a passthrough to the underlying assignment operator
41  // for the ThermoPhase parent object.
43  // However, we have to handle data that we own.
44  m_Pcurrent = b.m_Pcurrent;
45  moleFractions_ = b.moleFractions_;
46  iState_ = b.iState_;
47  forcedState_ = b.forcedState_;
48  m_Tlast_ref = b.m_Tlast_ref;
49  m_h0_RT = b.m_h0_RT;
50  m_cp0_R = b.m_cp0_R;
51  m_g0_RT = b.m_g0_RT;
52  m_s0_R = b.m_s0_R;
53  }
54  return *this;
55 }
56 
58 {
59  return new MixtureFugacityTP(*this);
60 }
61 
63 {
65 }
66 
68 {
69  forcedState_ = solnBranch;
70 }
71 
73 {
74  return forcedState_;
75 }
76 
78 {
79  return iState_;
80 }
81 
82 // ---- Partial Molar Properties of the Solution -----------------
83 
84 void MixtureFugacityTP::getChemPotentials_RT(doublereal* muRT) const
85 {
86  getChemPotentials(muRT);
87  for (size_t k = 0; k < m_kk; k++) {
88  muRT[k] *= 1.0 / RT();
89  }
90 }
91 
92 // ----- Thermodynamic Values for the Species Standard States States ----
93 
95 {
97  copy(m_g0_RT.begin(), m_g0_RT.end(), g);
98  double tmp = log(pressure() /m_spthermo->refPressure());
99  for (size_t k = 0; k < m_kk; k++) {
100  g[k] = RT() * (g[k] + tmp);
101  }
102 }
103 
104 void MixtureFugacityTP::getEnthalpy_RT(doublereal* hrt) const
105 {
106  getEnthalpy_RT_ref(hrt);
107 }
108 
109 void MixtureFugacityTP::getEntropy_R(doublereal* sr) const
110 {
112  copy(m_s0_R.begin(), m_s0_R.end(), sr);
113  double tmp = log(pressure() /m_spthermo->refPressure());
114  for (size_t k = 0; k < m_kk; k++) {
115  sr[k] -= tmp;
116  }
117 }
118 
119 void MixtureFugacityTP::getGibbs_RT(doublereal* grt) const
120 {
122  copy(m_g0_RT.begin(), m_g0_RT.end(), grt);
123  double tmp = log(pressure() /m_spthermo->refPressure());
124  for (size_t k = 0; k < m_kk; k++) {
125  grt[k] += tmp;
126  }
127 }
128 
129 void MixtureFugacityTP::getPureGibbs(doublereal* g) const
130 {
132  scale(m_g0_RT.begin(), m_g0_RT.end(), g, RT());
133  double tmp = log(pressure() /m_spthermo->refPressure()) * RT();
134  for (size_t k = 0; k < m_kk; k++) {
135  g[k] += tmp;
136  }
137 }
138 
139 void MixtureFugacityTP::getIntEnergy_RT(doublereal* urt) const
140 {
142  copy(m_h0_RT.begin(), m_h0_RT.end(), urt);
143  for (size_t i = 0; i < m_kk; i++) {
144  urt[i] -= 1.0;
145  }
146 }
147 
148 void MixtureFugacityTP::getCp_R(doublereal* cpr) const
149 {
151  copy(m_cp0_R.begin(), m_cp0_R.end(), cpr);
152 }
153 
154 void MixtureFugacityTP::getStandardVolumes(doublereal* vol) const
155 {
157  for (size_t i = 0; i < m_kk; i++) {
158  vol[i] = RT() / pressure();
159  }
160 }
161 
162 // ----- Thermodynamic Values for the Species Reference States ----
163 
164 void MixtureFugacityTP::getEnthalpy_RT_ref(doublereal* hrt) const
165 {
167  copy(m_h0_RT.begin(), m_h0_RT.end(), hrt);
168 }
169 
170 void MixtureFugacityTP::getGibbs_RT_ref(doublereal* grt) const
171 {
173  copy(m_g0_RT.begin(), m_g0_RT.end(), grt);
174 }
175 
176 void MixtureFugacityTP::getGibbs_ref(doublereal* g) const
177 {
178  const vector_fp& gibbsrt = gibbs_RT_ref();
179  scale(gibbsrt.begin(), gibbsrt.end(), g, RT());
180 }
181 
183 {
185  return m_g0_RT;
186 }
187 
188 void MixtureFugacityTP::getEntropy_R_ref(doublereal* er) const
189 {
191  copy(m_s0_R.begin(), m_s0_R.end(), er);
192 }
193 
194 void MixtureFugacityTP::getCp_R_ref(doublereal* cpr) const
195 {
197  copy(m_cp0_R.begin(), m_cp0_R.end(), cpr);
198 }
199 
200 void MixtureFugacityTP::getStandardVolumes_ref(doublereal* vol) const
201 {
203  for (size_t i = 0; i < m_kk; i++) {
204  vol[i]= RT() / refPressure();
205  }
206 }
207 
209 {
210  int doTP = 0;
211  string comp = getChildValue(state,"moleFractions");
212  if (comp != "") {
213  // not overloaded in current object -> phase state is not calculated.
215  doTP = 1;
216  } else {
217  comp = getChildValue(state,"massFractions");
218  if (comp != "") {
219  // not overloaded in current object -> phase state is not calculated.
221  doTP = 1;
222  }
223  }
224  double t = temperature();
225  if (state.hasChild("temperature")) {
226  t = getFloat(state, "temperature", "temperature");
227  doTP = 1;
228  }
229  if (state.hasChild("pressure")) {
230  double p = getFloat(state, "pressure", "pressure");
231  setState_TP(t, p);
232  } else if (state.hasChild("density")) {
233  double rho = getFloat(state, "density", "density");
234  setState_TR(t, rho);
235  } else if (doTP) {
236  double rho = Phase::density();
237  setState_TR(t, rho);
238  }
239 }
240 
241 bool MixtureFugacityTP::addSpecies(shared_ptr<Species> spec)
242 {
243  bool added = ThermoPhase::addSpecies(spec);
244  if (added) {
245  if (m_kk == 1) {
246  moleFractions_.push_back(1.0);
247  } else {
248  moleFractions_.push_back(0.0);
249  }
250  m_h0_RT.push_back(0.0);
251  m_cp0_R.push_back(0.0);
252  m_g0_RT.push_back(0.0);
253  m_s0_R.push_back(0.0);
254  }
255  return added;
256 }
257 
258 void MixtureFugacityTP::setTemperature(const doublereal temp)
259 {
261  setState_TR(temperature(), density());
262 }
263 
265 {
266  setState_TP(temperature(), p);
267  }
268 
270 {
273 }
274 
275 void MixtureFugacityTP::setMoleFractions_NoState(const doublereal* const x)
276 {
279  updateMixingExpressions();
280 }
281 
283 {
284  throw NotImplementedError("MixtureFugacityTP::calcDensity() "
285  "called, but EOS for phase is not known");
286 }
287 
288 void MixtureFugacityTP::setState_TP(doublereal t, doublereal pres)
289 {
290  // A pretty tricky algorithm is needed here, due to problems involving
291  // standard states of real fluids. For those cases you need to combine the T
292  // and P specification for the standard state, or else you may venture into
293  // the forbidden zone, especially when nearing the triple point. Therefore,
294  // we need to do the standard state thermo calc with the (t, pres) combo.
296 
299  // Depends on the mole fractions and the temperature
300  updateMixingExpressions();
301  m_Pcurrent = pres;
302 
303  if (forcedState_ == FLUID_UNDEFINED) {
304  double rhoNow = Phase::density();
305  double rho = densityCalc(t, pres, iState_, rhoNow);
306  if (rho > 0.0) {
307  Phase::setDensity(rho);
308  m_Pcurrent = pres;
309  iState_ = phaseState(true);
310  } else {
311  if (rho < -1.5) {
312  rho = densityCalc(t, pres, FLUID_UNDEFINED , rhoNow);
313  if (rho > 0.0) {
314  Phase::setDensity(rho);
315  m_Pcurrent = pres;
316  iState_ = phaseState(true);
317  } else {
318  throw CanteraError("MixtureFugacityTP::setState_TP()", "neg rho");
319  }
320  } else {
321  throw CanteraError("MixtureFugacityTP::setState_TP()", "neg rho");
322  }
323  }
324  } else if (forcedState_ == FLUID_GAS) {
325  // Normal density calculation
326  if (iState_ < FLUID_LIQUID_0) {
327  double rhoNow = Phase::density();
328  double rho = densityCalc(t, pres, iState_, rhoNow);
329  if (rho > 0.0) {
330  Phase::setDensity(rho);
331  m_Pcurrent = pres;
332  iState_ = phaseState(true);
333  if (iState_ >= FLUID_LIQUID_0) {
334  throw CanteraError("MixtureFugacityTP::setState_TP()", "wrong state");
335  }
336  } else {
337  throw CanteraError("MixtureFugacityTP::setState_TP()", "neg rho");
338  }
339  }
340  } else if (forcedState_ > FLUID_LIQUID_0) {
341  if (iState_ >= FLUID_LIQUID_0) {
342  double rhoNow = Phase::density();
343  double rho = densityCalc(t, pres, iState_, rhoNow);
344  if (rho > 0.0) {
345  Phase::setDensity(rho);
346  m_Pcurrent = pres;
347  iState_ = phaseState(true);
348  if (iState_ == FLUID_GAS) {
349  throw CanteraError("MixtureFugacityTP::setState_TP()", "wrong state");
350  }
351  } else {
352  throw CanteraError("MixtureFugacityTP::setState_TP()", "neg rho");
353  }
354  }
355  }
356 }
357 
358 void MixtureFugacityTP::setState_TR(doublereal T, doublereal rho)
359 {
363  Phase::setDensity(rho);
364  doublereal mv = molarVolume();
365  // depends on mole fraction and temperature
366  updateMixingExpressions();
367 
368  m_Pcurrent = pressureCalc(T, mv);
369  iState_ = phaseState(true);
370 }
371 
372 void MixtureFugacityTP::setState_TPX(doublereal t, doublereal p, const doublereal* x)
373 {
374  setMoleFractions_NoState(x);
375  setState_TP(t,p);
376 }
377 
378 doublereal MixtureFugacityTP::z() const
379 {
380  return pressure() * meanMolecularWeight() / (density() * RT());
381 }
382 
383 doublereal MixtureFugacityTP::sresid() const
384 {
385  throw CanteraError("MixtureFugacityTP::sresid()", "Base Class: not implemented");
386 }
387 
388 doublereal MixtureFugacityTP::hresid() const
389 {
390  throw CanteraError("MixtureFugacityTP::hresid()", "Base Class: not implemented");
391 }
392 
393 doublereal MixtureFugacityTP::psatEst(doublereal TKelvin) const
394 {
395  doublereal pcrit = critPressure();
396  doublereal tt = critTemperature() / TKelvin;
397  if (tt < 1.0) {
398  return pcrit;
399  }
400  doublereal lpr = -0.8734*tt*tt - 3.4522*tt + 4.2918;
401  return pcrit*exp(lpr);
402 }
403 
404 doublereal MixtureFugacityTP::liquidVolEst(doublereal TKelvin, doublereal& pres) const
405 {
406  throw CanteraError("MixtureFugacityTP::liquidVolEst()", "unimplemented");
407 }
408 
409 doublereal MixtureFugacityTP::densityCalc(doublereal TKelvin, doublereal presPa,
410  int phase, doublereal rhoguess)
411 {
412  doublereal tcrit = critTemperature();
413  doublereal mmw = meanMolecularWeight();
414  if (rhoguess == -1.0) {
415  if (phase != -1) {
416  if (TKelvin > tcrit) {
417  rhoguess = presPa * mmw / (GasConstant * TKelvin);
418  } else {
419  if (phase == FLUID_GAS || phase == FLUID_SUPERCRIT) {
420  rhoguess = presPa * mmw / (GasConstant * TKelvin);
421  } else if (phase >= FLUID_LIQUID_0) {
422  double lqvol = liquidVolEst(TKelvin, presPa);
423  rhoguess = mmw / lqvol;
424  }
425  }
426  } else {
427  // Assume the Gas phase initial guess, if nothing is specified to
428  // the routine
429  rhoguess = presPa * mmw / (GasConstant * TKelvin);
430  }
431  }
432 
433  double molarVolBase = mmw / rhoguess;
434  double molarVolLast = molarVolBase;
435  double vc = mmw / critDensity();
436 
437  // molar volume of the spinodal at the current temperature and mole
438  // fractions. this will be updated as we go.
439  double molarVolSpinodal = vc;
440  bool conv = false;
441 
442  // We start on one side of the vc and stick with that side
443  bool gasSide = molarVolBase > vc;
444  if (gasSide) {
445  molarVolLast = (GasConstant * TKelvin)/presPa;
446  } else {
447  molarVolLast = liquidVolEst(TKelvin, presPa);
448  }
449 
450  // OK, now we do a small solve to calculate the molar volume given the T,P
451  // value. The algorithm is taken from dfind()
452  for (int n = 0; n < 200; n++) {
453  // Calculate the predicted reduced pressure, pred0, based on the current
454  // tau and dd. Calculate the derivative of the predicted pressure wrt
455  // the molar volume. This routine also returns the pressure, presBase
456  double presBase;
457  double dpdVBase = dpdVCalc(TKelvin, molarVolBase, presBase);
458 
459  // If dpdV is positive, then we are in the middle of the 2 phase region
460  // and beyond the spinodal stability curve. We need to adjust the
461  // initial guess outwards and start a new iteration.
462  if (dpdVBase >= 0.0) {
463  if (TKelvin > tcrit) {
464  throw CanteraError("MixtureFugacityTP::densityCalc",
465  "T > tcrit unexpectedly");
466  }
467 
468  // TODO Spawn a calculation for the value of the spinodal point that
469  // is very accurate. Answer the question as to whether a
470  // solution is possible on the current side of the vapor dome.
471  if (gasSide) {
472  if (molarVolBase >= vc) {
473  molarVolSpinodal = molarVolBase;
474  molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
475  } else {
476  molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
477  }
478  } else {
479  if (molarVolBase <= vc) {
480  molarVolSpinodal = molarVolBase;
481  molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
482  } else {
483  molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
484  }
485  }
486  continue;
487  }
488 
489  // Check for convergence
490  if (fabs(presBase-presPa) < 1.0E-30 + 1.0E-8 * presPa) {
491  conv = true;
492  break;
493  }
494 
495  // Dampen and crop the update
496  doublereal dpdV = dpdVBase;
497  if (n < 10) {
498  dpdV = dpdVBase * 1.5;
499  }
500 
501  // Formulate the update to the molar volume by Newton's method. Then,
502  // crop it to a max value of 0.1 times the current volume
503  double delMV = - (presBase - presPa) / dpdV;
504  if ((!gasSide || delMV < 0.0) && fabs(delMV) > 0.2 * molarVolBase) {
505  delMV = delMV / fabs(delMV) * 0.2 * molarVolBase;
506  }
507  // Only go 1/10 the way towards the spinodal at any one time.
508  if (TKelvin < tcrit) {
509  if (gasSide) {
510  if (delMV < 0.0 && -delMV > 0.5 * (molarVolBase - molarVolSpinodal)) {
511  delMV = - 0.5 * (molarVolBase - molarVolSpinodal);
512  }
513  } else {
514  if (delMV > 0.0 && delMV > 0.5 * (molarVolSpinodal - molarVolBase)) {
515  delMV = 0.5 * (molarVolSpinodal - molarVolBase);
516  }
517  }
518  }
519  // updated the molar volume value
520  molarVolLast = molarVolBase;
521  molarVolBase += delMV;
522 
523  if (fabs(delMV/molarVolBase) < 1.0E-14) {
524  conv = true;
525  break;
526  }
527 
528  // Check for negative molar volumes
529  if (molarVolBase <= 0.0) {
530  molarVolBase = std::min(1.0E-30, fabs(delMV*1.0E-4));
531  }
532  }
533 
534  // Check for convergence, and return 0.0 if it wasn't achieved.
535  double densBase = 0.0;
536  if (! conv) {
537  molarVolBase = 0.0;
538  throw CanteraError("MixtureFugacityTP::densityCalc()", "Process did not converge");
539  } else {
540  densBase = mmw / molarVolBase;
541  }
542  return densBase;
543 }
544 
545 void MixtureFugacityTP::updateMixingExpressions()
546 {
547 }
548 
549 int MixtureFugacityTP::corr0(doublereal TKelvin, doublereal pres, doublereal& densLiqGuess,
550  doublereal& densGasGuess, doublereal& liqGRT, doublereal& gasGRT)
551 {
552  int retn = 0;
553  doublereal densLiq = densityCalc(TKelvin, pres, FLUID_LIQUID_0, densLiqGuess);
554  if (densLiq <= 0.0) {
555  retn = -1;
556  } else {
557  densLiqGuess = densLiq;
558  setState_TR(TKelvin, densLiq);
559  liqGRT = gibbs_mole() / RT();
560  }
561 
562  doublereal densGas = densityCalc(TKelvin, pres, FLUID_GAS, densGasGuess);
563  if (densGas <= 0.0) {
564  if (retn == -1) {
565  throw CanteraError("MixtureFugacityTP::corr0",
566  "Error occurred trying to find gas density at (T,P) = {} {}",
567  TKelvin, pres);
568  }
569  retn = -2;
570  } else {
571  densGasGuess = densGas;
572  setState_TR(TKelvin, densGas);
573  gasGRT = gibbs_mole() / RT();
574  }
575  return retn;
576 }
577 
578 int MixtureFugacityTP::phaseState(bool checkState) const
579 {
580  int state = iState_;
581  if (checkState) {
582  double t = temperature();
583  double tcrit = critTemperature();
584  double rhocrit = critDensity();
585  if (t >= tcrit) {
586  return FLUID_SUPERCRIT;
587  }
588  double tmid = tcrit - 100.;
589  if (tmid < 0.0) {
590  tmid = tcrit / 2.0;
591  }
592  double pp = psatEst(tmid);
593  double mmw = meanMolecularWeight();
594  double molVolLiqTmid = liquidVolEst(tmid, pp);
595  double molVolGasTmid = GasConstant * tmid / pp;
596  double densLiqTmid = mmw / molVolLiqTmid;
597  double densGasTmid = mmw / molVolGasTmid;
598  double densMidTmid = 0.5 * (densLiqTmid + densGasTmid);
599  doublereal rhoMid = rhocrit + (t - tcrit) * (rhocrit - densMidTmid) / (tcrit - tmid);
600 
601  double rho = density();
602  int iStateGuess = FLUID_LIQUID_0;
603  if (rho < rhoMid) {
604  iStateGuess = FLUID_GAS;
605  }
606  double molarVol = mmw / rho;
607  double presCalc;
608 
609  double dpdv = dpdVCalc(t, molarVol, presCalc);
610  if (dpdv < 0.0) {
611  state = iStateGuess;
612  } else {
613  state = FLUID_UNSTABLE;
614  }
615  }
616  return state;
617 }
618 
620 {
621  throw CanteraError("MixtureFugacityTP::densSpinodalLiquid", "unimplemented");
622 }
623 
625 {
626  throw CanteraError("MixtureFugacityTP::densSpinodalGas", "unimplemented");
627 }
628 
629 doublereal MixtureFugacityTP::satPressure(doublereal TKelvin)
630 {
631  doublereal molarVolGas;
632  doublereal molarVolLiquid;
633  return calculatePsat(TKelvin, molarVolGas, molarVolLiquid);
634 }
635 
636 doublereal MixtureFugacityTP::calculatePsat(doublereal TKelvin, doublereal& molarVolGas,
637  doublereal& molarVolLiquid)
638 {
639  // The algorithm for this routine has undergone quite a bit of work. It
640  // probably needs more work. However, it seems now to be fairly robust. The
641  // key requirement is to find an initial pressure where both the liquid and
642  // the gas exist. This is not as easy as it sounds, and it gets exceedingly
643  // hard as the critical temperature is approached from below. Once we have
644  // this initial state, then we seek to equilibrate the Gibbs free energies
645  // of the gas and liquid and use the formula
646  //
647  // dp = VdG
648  //
649  // to create an update condition for deltaP using
650  //
651  // - (Gliq - Ggas) = (Vliq - Vgas) (deltaP)
652  //
653  // @TODO Suggestions for the future would be to switch it to an algorithm
654  // that uses the gas molar volume and the liquid molar volumes as the
655  // fundamental unknowns.
656 
657  // we need this because this is a non-const routine that is public
658  setTemperature(TKelvin);
659  double densSave = density();
660  double tempSave = temperature();
661  double pres;
662  doublereal mw = meanMolecularWeight();
663  if (TKelvin < critTemperature()) {
664  pres = psatEst(TKelvin);
665  // trial value = Psat from correlation
666  doublereal volLiquid = liquidVolEst(TKelvin, pres);
667  double RhoLiquidGood = mw / volLiquid;
668  double RhoGasGood = pres * mw / (GasConstant * TKelvin);
669  doublereal delGRT = 1.0E6;
670  doublereal liqGRT, gasGRT;
671 
672  // First part of the calculation involves finding a pressure at which
673  // the gas and the liquid state coexists.
674  doublereal presLiquid = 0.;
675  doublereal presGas;
676  doublereal presBase = pres;
677  bool foundLiquid = false;
678  bool foundGas = false;
679 
680  doublereal densLiquid = densityCalc(TKelvin, presBase, FLUID_LIQUID_0, RhoLiquidGood);
681  if (densLiquid > 0.0) {
682  foundLiquid = true;
683  presLiquid = pres;
684  RhoLiquidGood = densLiquid;
685  }
686  if (!foundLiquid) {
687  for (int i = 0; i < 50; i++) {
688  pres = 1.1 * pres;
689  densLiquid = densityCalc(TKelvin, pres, FLUID_LIQUID_0, RhoLiquidGood);
690  if (densLiquid > 0.0) {
691  foundLiquid = true;
692  presLiquid = pres;
693  RhoLiquidGood = densLiquid;
694  break;
695  }
696  }
697  }
698 
699  pres = presBase;
700  doublereal densGas = densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
701  if (densGas <= 0.0) {
702  foundGas = false;
703  } else {
704  foundGas = true;
705  presGas = pres;
706  RhoGasGood = densGas;
707  }
708  if (!foundGas) {
709  for (int i = 0; i < 50; i++) {
710  pres = 0.9 * pres;
711  densGas = densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
712  if (densGas > 0.0) {
713  foundGas = true;
714  presGas = pres;
715  RhoGasGood = densGas;
716  break;
717  }
718  }
719  }
720 
721  if (foundGas && foundLiquid && presGas != presLiquid) {
722  pres = 0.5 * (presLiquid + presGas);
723  bool goodLiq;
724  bool goodGas;
725  for (int i = 0; i < 50; i++) {
726  densLiquid = densityCalc(TKelvin, pres, FLUID_LIQUID_0, RhoLiquidGood);
727  if (densLiquid <= 0.0) {
728  goodLiq = false;
729  } else {
730  goodLiq = true;
731  RhoLiquidGood = densLiquid;
732  presLiquid = pres;
733  }
734  densGas = densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
735  if (densGas <= 0.0) {
736  goodGas = false;
737  } else {
738  goodGas = true;
739  RhoGasGood = densGas;
740  presGas = pres;
741  }
742  if (goodGas && goodLiq) {
743  break;
744  }
745  if (!goodLiq && !goodGas) {
746  pres = 0.5 * (pres + presLiquid);
747  }
748  if (goodLiq || goodGas) {
749  pres = 0.5 * (presLiquid + presGas);
750  }
751  }
752  }
753  if (!foundGas || !foundLiquid) {
754  writelog("error couldn't find a starting pressure\n");
755  return 0.0;
756  }
757  if (presGas != presLiquid) {
758  writelog("error couldn't find a starting pressure\n");
759  return 0.0;
760  }
761 
762  pres = presGas;
763  double presLast = pres;
764  double RhoGas = RhoGasGood;
765  double RhoLiquid = RhoLiquidGood;
766 
767  // Now that we have found a good pressure we can proceed with the algorithm.
768  for (int i = 0; i < 20; i++) {
769  int stab = corr0(TKelvin, pres, RhoLiquid, RhoGas, liqGRT, gasGRT);
770  if (stab == 0) {
771  presLast = pres;
772  delGRT = liqGRT - gasGRT;
773  doublereal delV = mw * (1.0/RhoLiquid - 1.0/RhoGas);
774  doublereal dp = - delGRT * GasConstant * TKelvin / delV;
775 
776  if (fabs(dp) > 0.1 * pres) {
777  if (dp > 0.0) {
778  dp = 0.1 * pres;
779  } else {
780  dp = -0.1 * pres;
781  }
782  }
783  pres += dp;
784  } else if (stab == -1) {
785  delGRT = 1.0E6;
786  if (presLast > pres) {
787  pres = 0.5 * (presLast + pres);
788  } else {
789  // we are stuck here - try this
790  pres = 1.1 * pres;
791  }
792  } else if (stab == -2) {
793  if (presLast < pres) {
794  pres = 0.5 * (presLast + pres);
795  } else {
796  // we are stuck here - try this
797  pres = 0.9 * pres;
798  }
799  }
800  molarVolGas = mw / RhoGas;
801  molarVolLiquid = mw / RhoLiquid;
802 
803  if (fabs(delGRT) < 1.0E-8) {
804  // converged
805  break;
806  }
807  }
808 
809  molarVolGas = mw / RhoGas;
810  molarVolLiquid = mw / RhoLiquid;
811  // Put the fluid in the desired end condition
812  setState_TR(tempSave, densSave);
813  return pres;
814  } else {
815  pres = critPressure();
816  setState_TP(TKelvin, pres);
817  molarVolGas = mw / density();
818  molarVolLiquid = molarVolGas;
819  setState_TR(tempSave, densSave);
820  }
821  return pres;
822 }
823 
824 doublereal MixtureFugacityTP::pressureCalc(doublereal TKelvin, doublereal molarVol) const
825 {
826  throw CanteraError("MixtureFugacityTP::pressureCalc", "unimplemented");
827 }
828 
829 doublereal MixtureFugacityTP::dpdVCalc(doublereal TKelvin, doublereal molarVol, doublereal& presCalc) const
830 {
831  throw CanteraError("MixtureFugacityTP::dpdVCalc", "unimplemented");
832 }
833 
835 {
836  double Tnow = temperature();
837 
838  // If the temperature has changed since the last time these
839  // properties were computed, recompute them.
840  if (m_Tlast_ref != Tnow) {
841  m_spthermo->update(Tnow, &m_cp0_R[0], &m_h0_RT[0], &m_s0_R[0]);
842  m_Tlast_ref = Tnow;
843 
844  // update the species Gibbs functions
845  for (size_t k = 0; k < m_kk; k++) {
846  m_g0_RT[k] = m_h0_RT[k] - m_s0_R[k];
847  }
848  doublereal pref = refPressure();
849  if (pref <= 0.0) {
850  throw CanteraError("MixtureFugacityTP::_updateReferenceStateThermo()", "neg ref pressure");
851  }
852  }
853 }
854 
856 {
858  m_Tlast_ref += 0.001234;
859 }
860 
861 }
virtual int forcedSolutionBranch() const
Report the solution branch which the solution is restricted to.
virtual doublereal satPressure(doublereal TKelvin)
Calculate the saturation pressure at the current mixture content for the given temperature.
virtual void calcDensity()
Calculate the density of the mixture using the partial molar volumes and mole fractions as input...
virtual void setState_TPX(doublereal t, doublereal p, const doublereal *x)
Set the temperature (K), pressure (Pa), and mole fractions.
doublereal molarVolume() const
Molar volume (m^3/kmol).
Definition: Phase.cpp:676
virtual void getEnthalpy_RT_ref(doublereal *hrt) const
virtual bool addSpecies(shared_ptr< Species > spec)
virtual doublereal sresid() const
Calculate the deviation terms for the total entropy of the mixture from the ideal gas mixture...
virtual doublereal densityCalc(doublereal TKelvin, doublereal pressure, int phaseRequested, doublereal rhoguess)
Calculates the density given the temperature and the pressure and a guess at the density.
virtual int standardStateConvention() const
This method returns the convention used in specification of the standard state, of which there are cu...
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
virtual void getGibbs_RT_ref(doublereal *grt) const
Returns the vector of nondimensional Gibbs Free Energies of the reference state at the current temper...
std::string getChildValue(const XML_Node &parent, const std::string &nameString)
This function reads a child node with the name, nameString, and returns its XML value as the return s...
Definition: ctml.cpp:145
doublereal temperature() const
Temperature (K).
Definition: Phase.h:601
virtual doublereal hresid() const
Calculate the deviation terms for the total enthalpy of the mixture from the ideal gas mixture...
An error indicating that an unimplemented function has been called.
Definition: ctexceptions.h:193
virtual void getChemPotentials_RT(doublereal *mu) const
Get the array of non-dimensional species chemical potentials These are partial molar Gibbs free energ...
virtual doublereal psatEst(doublereal TKelvin) const
Estimate for the saturation pressure.
virtual void update(doublereal T, doublereal *cp_R, doublereal *h_RT, doublereal *s_R) const
Compute the reference-state properties for all species.
virtual doublereal dpdVCalc(doublereal TKelvin, doublereal molarVol, doublereal &presCalc) const
Calculate the pressure and the pressure derivative given the temperature and the molar volume...
ThermoPhase & operator=(const ThermoPhase &right)
Definition: ThermoPhase.cpp:59
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
Definition: ThermoPhase.h:494
doublereal z() const
Calculate the value of z.
int forcedState_
Force the system to be on a particular side of the spinodal curve.
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:179
const int cSS_CONVENTION_TEMPERATURE
Standard state uses the molar convention.
Definition: ThermoPhase.h:36
vector_fp m_g0_RT
Temporary storage for dimensionless reference state Gibbs energies.
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:97
This is a filter class for ThermoPhase that implements some preparatory steps for efficiently handlin...
STL namespace.
virtual doublereal density() const
Density (kg/m^3).
Definition: Phase.h:607
virtual doublereal refPressure(size_t k=npos) const
The reference-state pressure for species k.
doublereal calculatePsat(doublereal TKelvin, doublereal &molarVolGas, doublereal &molarVolLiquid)
Calculate the saturation pressure at the current mixture content for the given temperature.
#define FLUID_UNSTABLE
Various states of the Fugacity object.
virtual void getPureGibbs(doublereal *gpure) const
Get the pure Gibbs free energies of each species.
doublereal RT() const
Return the Gas Constant multiplied by the current temperature.
Definition: ThermoPhase.h:809
virtual int reportSolnBranchActual() const
Report the solution branch which the solution is actually on.
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:93
virtual bool addSpecies(shared_ptr< Species > spec)
virtual void invalidateCache()
Invalidate any cached values which are normally updated only when a change in state is detected...
virtual void invalidateCache()
Invalidate any cached values which are normally updated only when a change in state is detected...
vector_fp m_s0_R
Temporary storage for dimensionless reference state entropies.
virtual void getGibbs_ref(doublereal *g) const
Returns the vector of the Gibbs function of the reference state at the current temperature of the sol...
doublereal m_Pcurrent
Current value of the pressure.
virtual doublereal critPressure() const
Critical pressure (Pa).
Definition: ThermoPhase.h:1336
virtual void setTemperature(const doublereal temp)
Set the temperature of the phase.
virtual doublereal gibbs_mole() const
Molar Gibbs function. Units: J/kmol.
Definition: ThermoPhase.h:253
Header file for a derived class of ThermoPhase that handles non-ideal mixtures based on the fugacity ...
virtual void getCp_R_ref(doublereal *cprt) const
Returns the vector of nondimensional constant pressure heat capacities of the reference state at the ...
virtual void _updateReferenceStateThermo() const
Updates the reference state thermodynamic functions at the current T of the solution.
virtual doublereal densSpinodalLiquid() const
Return the value of the density at the liquid spinodal point (on the liquid side) for the current tem...
virtual void compositionChanged()
Apply changes to the state which are needed after the composition changes.
Definition: Phase.cpp:899
void setMoleFractionsByName(const compositionMap &xMap)
Set the species mole fractions by name.
Definition: Phase.cpp:368
void setMassFractionsByName(const compositionMap &yMap)
Set the species mass fractions by name.
Definition: Phase.cpp:412
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:65
virtual void setMoleFractions(const doublereal *const x)
Set the mole fractions to the specified values.
Definition: Phase.cpp:327
virtual void setForcedSolutionBranch(int solnBranch)
Set the solution branch to force the ThermoPhase to exist on one branch or another.
void getMoleFractions(doublereal *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:542
virtual void getStandardChemPotentials(doublereal *mu) const
Get the array of chemical potentials at unit activity.
virtual void setPressure(doublereal p)
Set the internally stored pressure (Pa) at constant temperature and composition.
virtual doublereal densSpinodalGas() const
Return the value of the density at the gas spinodal point (on the gas side) for the current temperatu...
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:536
MultiSpeciesThermo * m_spthermo
Pointer to the calculation manager for species reference-state thermodynamic properties.
Definition: ThermoPhase.h:1693
virtual doublereal pressure() const
Returns the current pressure of the phase.
virtual doublereal critTemperature() const
Critical temperature (K).
Definition: ThermoPhase.h:1331
virtual doublereal pressureCalc(doublereal TKelvin, doublereal molarVol) const
Calculate the pressure given the temperature and the molar volume.
int iState_
Current state of the fluid.
virtual void getEntropy_R_ref(doublereal *er) const
Returns the vector of nondimensional entropies of the reference state at the current temperature of t...
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
doublereal meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition: Phase.h:661
virtual void setTemperature(const doublereal temp)
Set the internally stored temperature of the phase (K).
Definition: Phase.h:637
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
Definition: utilities.h:130
doublereal m_Tlast_ref
The last temperature at which the reference state thermodynamic properties were calculated at...
virtual void compositionChanged()
Apply changes to the state which are needed after the composition changes.
virtual doublereal refPressure() const
Returns the reference pressure in Pa.
Definition: ThermoPhase.h:149
const doublereal GasConstant
Universal Gas Constant. [J/kmol/K].
Definition: ct_defs.h:64
int phaseState(bool checkState=false) const
Returns the Phase State flag for the current state of the object.
Contains declarations for string manipulation functions within Cantera.
int corr0(doublereal TKelvin, doublereal pres, doublereal &densLiq, doublereal &densGas, doublereal &liqGRT, doublereal &gasGRT)
Utility routine in the calculation of the saturation pressure.
doublereal getFloat(const XML_Node &parent, const std::string &name, const std::string &type)
Get a floating-point value from a child element.
Definition: ctml.cpp:178
const vector_fp & gibbs_RT_ref() const
Returns the vector of nondimensional Gibbs free energies of the reference state at the current temper...
vector_fp moleFractions_
Storage for the current values of the mole fractions of the species.
virtual void getEnthalpy_RT(doublereal *hrt) const
Get the nondimensional Enthalpy functions for the species at their standard states at the current T a...
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...
size_t m_kk
Number of species in the phase.
Definition: Phase.h:784
virtual void setState_TP(doublereal T, doublereal pres)
Set the temperature (K) and pressure (Pa)
virtual void getStandardVolumes_ref(doublereal *vol) const
Get the molar volumes of the species reference states at the current T and P_ref of the solution...
virtual ThermoPhase * duplMyselfAsThermoPhase() const
Duplication routine for objects which inherit from ThermoPhase.
virtual void getGibbs_RT(doublereal *grt) const
Get the nondimensional Gibbs functions for the species at their standard states of solution at the cu...
virtual void getCp_R(doublereal *cpr) const
Get the nondimensional Heat Capacities at constant pressure for the standard state of the species at ...
Namespace for the Cantera kernel.
Definition: application.cpp:29
virtual doublereal critDensity() const
Critical density (kg/m3).
Definition: ThermoPhase.h:1351
virtual void getIntEnergy_RT(doublereal *urt) const
Returns the vector of nondimensional internal Energies of the standard state at the current temperatu...
virtual void setDensity(const doublereal density_)
Set the internally stored density (kg/m^3) of the phase.
Definition: Phase.h:622
vector_fp m_h0_RT
Temporary storage for dimensionless reference state enthalpies.
virtual void getEntropy_R(doublereal *sr) const
Get the array of nondimensional Enthalpy functions for the standard state species at the current T an...
vector_fp m_cp0_R
Temporary storage for dimensionless reference state heat capacities.
virtual void setStateFromXML(const XML_Node &state)
Set the initial state of the phase to the conditions specified in the state XML element.
virtual doublereal liquidVolEst(doublereal TKelvin, doublereal &pres) const
Estimate for the molar volume of the liquid.