Cantera  3.2.0b1
Loading...
Searching...
No Matches
PengRobinson.cpp
Go to the documentation of this file.
1//! @file PengRobinson.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
11
12#include <boost/algorithm/string.hpp>
13#include <boost/math/tools/roots.hpp>
14
15namespace bmt = boost::math::tools;
16
17namespace Cantera
18{
19
20const double PengRobinson::omega_a = 4.5723552892138218E-01;
21const double PengRobinson::omega_b = 7.77960739038885E-02;
22const double PengRobinson::omega_vc = 3.07401308698703833E-01;
23
24PengRobinson::PengRobinson(const string& infile, const string& id_)
25{
26 initThermoFile(infile, id_);
27}
28
29void PengRobinson::setSpeciesCoeffs(const string& species, double a, double b, double w)
30{
31 size_t k = speciesIndex(species, true);
32
33 // Calculate value of kappa (independent of temperature)
34 // w is an acentric factor of species
35 if (w <= 0.491) {
36 m_kappa[k] = 0.37464 + 1.54226*w - 0.26992*w*w;
37 } else {
38 m_kappa[k] = 0.374642 + 1.487503*w - 0.164423*w*w + 0.016666*w*w*w;
39 }
40 m_acentric[k] = w; // store the original acentric factor to enable serialization
41
42 // Calculate alpha (temperature dependent interaction parameter)
43 double critTemp = speciesCritTemperature(a, b);
44 double sqt_T_r = sqrt(temperature() / critTemp);
45 double sqt_alpha = 1 + m_kappa[k] * (1 - sqt_T_r);
46 m_alpha[k] = sqt_alpha*sqt_alpha;
47
48 m_a_coeffs(k,k) = a;
49 double aAlpha_k = a*m_alpha[k];
50 m_aAlpha_binary(k,k) = aAlpha_k;
51
52 // standard mixing rule for cross-species interaction term
53 for (size_t j = 0; j < m_kk; j++) {
54 if (k == j) {
55 continue;
56 }
57 double a0kj = sqrt(m_a_coeffs(j,j) * a);
58 double aAlpha_j = a*m_alpha[j];
59 double a_Alpha = sqrt(aAlpha_j*aAlpha_k);
60 if (m_a_coeffs(j, k) == 0) {
61 m_a_coeffs(j, k) = a0kj;
62 m_aAlpha_binary(j, k) = a_Alpha;
63 m_a_coeffs(k, j) = a0kj;
64 m_aAlpha_binary(k, j) = a_Alpha;
65 }
66 }
67 m_b_coeffs[k] = b;
68}
69
70void PengRobinson::setBinaryCoeffs(const string& species_i,
71 const string& species_j, double a0)
72{
73 size_t ki = speciesIndex(species_i, true);
74 size_t kj = speciesIndex(species_j, true);
75
76 m_a_coeffs(ki, kj) = m_a_coeffs(kj, ki) = a0;
77 m_binaryParameters[species_i][species_j] = a0;
78 m_binaryParameters[species_j][species_i] = a0;
79 // Calculate alpha_ij
80 double alpha_ij = m_alpha[ki] * m_alpha[kj];
81 m_aAlpha_binary(ki, kj) = m_aAlpha_binary(kj, ki) = a0*alpha_ij;
82}
83
84// ------------Molar Thermodynamic Properties -------------------------
85
87{
89 double T = temperature();
90 double mv = molarVolume();
91 double vpb = mv + (1 + Sqrt2) * m_b;
92 double vmb = mv + (1 - Sqrt2) * m_b;
94 double cpref = GasConstant * mean_X(m_cp0_R);
95 double dHdT_V = cpref + mv * m_dpdT - GasConstant
96 + 1.0 / (2.0 * Sqrt2 * m_b) * log(vpb / vmb) * T * d2aAlpha_dT2();
97 return dHdT_V - (mv + T * m_dpdT / m_dpdV) * m_dpdT;
98}
99
101{
103 double T = temperature();
105 return (cp_mole() + T * m_dpdT * m_dpdT / m_dpdV);
106}
107
109{
111 // Get a copy of the private variables stored in the State object
112 double T = temperature();
113 double mv = molarVolume();
114 double denom = mv * mv + 2 * mv * m_b - m_b * m_b;
115 return GasConstant * T / (mv - m_b) - m_aAlpha_mix / denom;
116}
117
119{
121 return 1.0 / m_workS[k];
122}
123
125{
126 double mv = molarVolume();
127 double vpb2 = mv + (1 + Sqrt2) * m_b;
128 double vmb2 = mv + (1 - Sqrt2) * m_b;
129 double vmb = mv - m_b;
130 double pres = pressure();
131
132 for (size_t k = 0; k < m_kk; k++) {
133 m_pp[k] = 0.0;
134 for (size_t i = 0; i < m_kk; i++) {
135 m_pp[k] += moleFractions_[i] * m_aAlpha_binary(k, i);
136 }
137 }
138 double num = 0;
139 double denom = 2 * Sqrt2 * m_b * m_b;
140 double denom2 = m_b * (mv * mv + 2 * mv * m_b - m_b * m_b);
141 double RT_ = RT();
142 for (size_t k = 0; k < m_kk; k++) {
143 num = 2 * m_b * m_pp[k] - m_aAlpha_mix * m_b_coeffs[k];
144 ac[k] = (-RT_ * log(pres * mv/ RT_) + RT_ * log(mv / vmb)
145 + RT_ * m_b_coeffs[k] / vmb
146 - (num /denom) * log(vpb2/vmb2)
147 - m_aAlpha_mix * m_b_coeffs[k] * mv/denom2
148 );
149 }
150 for (size_t k = 0; k < m_kk; k++) {
151 ac[k] = exp(ac[k]/ RT_);
152 }
153}
154
155// ---- Partial Molar Properties of the Solution -----------------
156
158{
159 getGibbs_ref(mu);
160 double RT_ = RT();
161 for (size_t k = 0; k < m_kk; k++) {
162 double xx = std::max(SmallNumber, moleFraction(k));
163 mu[k] += RT_ * (log(xx));
164 }
165
166 double mv = molarVolume();
167 double vmb = mv - m_b;
168 double vpb2 = mv + (1 + Sqrt2) * m_b;
169 double vmb2 = mv + (1 - Sqrt2) * m_b;
170
171 for (size_t k = 0; k < m_kk; k++) {
172 m_pp[k] = 0.0;
173 for (size_t i = 0; i < m_kk; i++) {
174 m_pp[k] += moleFractions_[i] * m_aAlpha_binary(k, i);
175 }
176 }
177 double pres = pressure();
178 double refP = refPressure();
179 double denom = 2 * Sqrt2 * m_b * m_b;
180 double denom2 = m_b * (mv * mv + 2 * mv * m_b - m_b * m_b);
181
182 for (size_t k = 0; k < m_kk; k++) {
183 double num = 2 * m_b * m_pp[k] - m_aAlpha_mix * m_b_coeffs[k];
184
185 mu[k] += RT_ * log(pres/refP) - RT_ * log(pres * mv / RT_)
186 + RT_ * log(mv / vmb) + RT_ * m_b_coeffs[k] / vmb
187 - (num /denom) * log(vpb2/vmb2)
188 - m_aAlpha_mix * m_b_coeffs[k] * mv/denom2;
189 }
190}
191
193{
194 // First we get the reference state contributions
195 getEnthalpy_RT_ref(hbar);
196 scale(hbar, hbar+m_kk, hbar, RT());
197 vector<double> tmp;
198 tmp.resize(m_kk,0.0);
199
200 // We calculate m_dpdni
201 double T = temperature();
202 double mv = molarVolume();
203 double vmb = mv - m_b;
204 double vpb2 = mv + (1 + Sqrt2) * m_b;
205 double vmb2 = mv + (1 - Sqrt2) * m_b;
206 double daAlphadT = daAlpha_dT();
207
208 for (size_t k = 0; k < m_kk; k++) {
209 m_pp[k] = 0.0;
210 tmp[k] = 0.0;
211 for (size_t i = 0; i < m_kk; i++) {
212 double grad_aAlpha = m_dalphadT[i]/m_alpha[i] + m_dalphadT[k]/m_alpha[k];
213 m_pp[k] += moleFractions_[i] * m_aAlpha_binary(k, i);
214 tmp[k] +=moleFractions_[i] * m_aAlpha_binary(k, i) * grad_aAlpha;
215 }
216 }
217
218 double denom = mv * mv + 2 * mv * m_b - m_b * m_b;
219 double denom2 = denom * denom;
220 double RT_ = RT();
221 for (size_t k = 0; k < m_kk; k++) {
222 m_dpdni[k] = RT_ / vmb + RT_ * m_b_coeffs[k] / (vmb * vmb)
223 - 2.0 * m_pp[k] / denom + 2 * vmb * m_aAlpha_mix * m_b_coeffs[k] / denom2;
224 }
225
226 double fac = T * daAlphadT - m_aAlpha_mix;
228 double fac2 = mv + T * m_dpdT / m_dpdV;
229 double fac3 = 2 * Sqrt2 * m_b * m_b;
230 double fac4 = 0;
231 for (size_t k = 0; k < m_kk; k++) {
232 fac4 = T*tmp[k] -2 * m_pp[k];
233 double hE_v = mv * m_dpdni[k] - RT_
234 - m_b_coeffs[k] / fac3 * log(vpb2 / vmb2) * fac
235 + (mv * m_b_coeffs[k]) / (m_b * denom) * fac
236 + 1 / (2 * Sqrt2 * m_b) * log(vpb2 / vmb2) * fac4;
237 hbar[k] = hbar[k] + hE_v;
238 hbar[k] -= fac2 * m_dpdni[k];
239 }
240}
241
243{
244 // Using the identity : (hk - T*sk) = gk
245 double T = temperature();
248 for (size_t k = 0; k < m_kk; k++) {
249 sbar[k] = (sbar[k] - m_workS[k])/T;
250 }
251}
252
254{
255 // u_i = h_i - p*v_i
256 double p = pressure();
259 for (size_t k = 0; k < m_kk; k++) {
260 ubar[k] = ubar[k] - p*m_workS[k];
261 }
262}
263
264void PengRobinson::getPartialMolarCp(double* cpbar) const
265{
266 throw NotImplementedError("PengRobinson::getPartialMolarCp");
267}
268
270{
271 for (size_t k = 0; k < m_kk; k++) {
272 m_pp[k] = 0.0;
273 for (size_t i = 0; i < m_kk; i++) {
274 m_pp[k] += moleFractions_[i] * m_aAlpha_binary(k, i);
275 }
276 }
277
278 double mv = molarVolume();
279 double vmb = mv - m_b;
280 double vpb = mv + m_b;
281 double fac = mv * mv + 2 * mv * m_b - m_b * m_b;
282 double fac2 = fac * fac;
283 double RT_ = RT();
284
285 for (size_t k = 0; k < m_kk; k++) {
286 double num = RT_ + RT_ * m_b/ vmb + RT_ * m_b_coeffs[k] / vmb
287 + RT_ * m_b * m_b_coeffs[k] /(vmb * vmb) - 2 * mv * m_pp[k] / fac
288 + 2 * mv * vmb * m_aAlpha_mix * m_b_coeffs[k] / fac2;
289 double denom = pressure() + RT_ * m_b / (vmb * vmb) + m_aAlpha_mix/fac
290 - 2 * mv* vpb * m_aAlpha_mix / fac2;
291 vbar[k] = num / denom;
292 }
293}
294
295double PengRobinson::speciesCritTemperature(double a, double b) const
296{
297 if (b <= 0.0) {
298 return 1000000.;
299 } else if (a <= 0.0) {
300 return 0.0;
301 } else {
302 return a * omega_b / (b * omega_a * GasConstant);
303 }
304}
305
306bool PengRobinson::addSpecies(shared_ptr<Species> spec)
307{
308 bool added = MixtureFugacityTP::addSpecies(spec);
309 if (added) {
310 m_a_coeffs.resize(m_kk, m_kk, 0.0);
311 m_b_coeffs.push_back(0.0);
312 m_aAlpha_binary.resize(m_kk, m_kk, 0.0);
313 m_kappa.push_back(0.0);
314 m_acentric.push_back(0.0);
315 m_alpha.push_back(0.0);
316 m_dalphadT.push_back(0.0);
317 m_d2alphadT2.push_back(0.0);
318 m_pp.push_back(0.0);
319 m_partialMolarVolumes.push_back(0.0);
320 m_dpdni.push_back(0.0);
321 m_coeffSource.push_back(CoeffSource::EoS);
322 }
323 return added;
324}
325
327{
328 // Contents of 'critical-properties.yaml', loaded later if needed
329 AnyMap critPropsDb;
330 std::unordered_map<string, AnyMap*> dbSpecies;
331
332 for (auto& [name, species] : m_species) {
333 auto& data = species->input;
334 size_t k = speciesIndex(name, true);
335 if (m_a_coeffs(k, k) != 0.0) {
336 continue;
337 }
338 bool foundCoeffs = false;
339 if (data.hasKey("equation-of-state") &&
340 data["equation-of-state"].hasMapWhere("model", "Peng-Robinson"))
341 {
342 // Read a and b coefficients and acentric factor w_ac from species input
343 // information, specified in a YAML input file.
344 auto eos = data["equation-of-state"].getMapWhere(
345 "model", "Peng-Robinson");
346 if (eos.hasKey("a") && eos.hasKey("b") && eos.hasKey("acentric-factor")) {
347 double a0 = eos.convert("a", "Pa*m^6/kmol^2");
348 double b = eos.convert("b", "m^3/kmol");
349 // unitless acentric factor:
350 double w = eos["acentric-factor"].asDouble();
351 setSpeciesCoeffs(name, a0, b, w);
352 foundCoeffs = true;
353 }
354
355 if (eos.hasKey("binary-a")) {
356 AnyMap& binary_a = eos["binary-a"].as<AnyMap>();
357 const UnitSystem& units = binary_a.units();
358 for (auto& [name2, coeff] : binary_a) {
359 double a0 = units.convert(coeff, "Pa*m^6/kmol^2");
360 setBinaryCoeffs(name, name2, a0);
361 }
362 }
363 if (foundCoeffs) {
364 m_coeffSource[k] = CoeffSource::EoS;
365 continue;
366 }
367 }
368
369 // Coefficients have not been populated from model-specific input
370 double Tc = NAN, Pc = NAN, omega_ac = NAN;
371 if (data.hasKey("critical-parameters")) {
372 // Use critical state information stored in the species entry to
373 // calculate a, b, and the acentric factor.
374 auto& critProps = data["critical-parameters"].as<AnyMap>();
375 Tc = critProps.convert("critical-temperature", "K");
376 Pc = critProps.convert("critical-pressure", "Pa");
377 omega_ac = critProps["acentric-factor"].asDouble();
378 m_coeffSource[k] = CoeffSource::CritProps;
379 } else {
380 // Search 'crit-properties.yaml' to find Tc and Pc. Load data if needed.
381 if (critPropsDb.empty()) {
382 critPropsDb = AnyMap::fromYamlFile("critical-properties.yaml");
383 dbSpecies = critPropsDb["species"].asMap("name");
384 }
385
386 // All names in critical-properties.yaml are upper case
387 auto ucName = boost::algorithm::to_upper_copy(name);
388 if (dbSpecies.count(ucName)) {
389 auto& spec = *dbSpecies.at(ucName);
390 auto& critProps = spec["critical-parameters"].as<AnyMap>();
391 Tc = critProps.convert("critical-temperature", "K");
392 Pc = critProps.convert("critical-pressure", "Pa");
393 omega_ac = critProps["acentric-factor"].asDouble();
394 m_coeffSource[k] = CoeffSource::Database;
395 }
396 }
397
398 // Check if critical properties were found in either location
399 if (!isnan(Tc)) {
400 double a = omega_a * std::pow(GasConstant * Tc, 2) / Pc;
401 double b = omega_b * GasConstant * Tc / Pc;
402 setSpeciesCoeffs(name, a, b, omega_ac);
403 } else {
404 throw InputFileError("PengRobinson::initThermo", data,
405 "No Peng-Robinson model parameters or critical properties found for "
406 "species '{}'", name);
407 }
408 }
409}
410
411void PengRobinson::getSpeciesParameters(const string& name, AnyMap& speciesNode) const
412{
414 size_t k = speciesIndex(name, true);
415
416 // Pure species parameters
417 if (m_coeffSource[k] == CoeffSource::EoS) {
418 auto& eosNode = speciesNode["equation-of-state"].getMapWhere(
419 "model", "Peng-Robinson", true);
420 eosNode["a"].setQuantity(m_a_coeffs(k, k), "Pa*m^6/kmol^2");
421 eosNode["b"].setQuantity(m_b_coeffs[k], "m^3/kmol");
422 eosNode["acentric-factor"] = m_acentric[k];
423 } else if (m_coeffSource[k] == CoeffSource::CritProps) {
424 auto& critProps = speciesNode["critical-parameters"];
425 double Tc = speciesCritTemperature(m_a_coeffs(k, k), m_b_coeffs[k]);
426 double Pc = omega_b * GasConstant * Tc / m_b_coeffs[k];
427 critProps["critical-temperature"].setQuantity(Tc, "K");
428 critProps["critical-pressure"].setQuantity(Pc, "Pa");
429 critProps["acentric-factor"] = m_acentric[k];
430 }
431 // Nothing to do in the case where the parameters are from the database
432
433 if (m_binaryParameters.count(name)) {
434 // Include binary parameters regardless of where the pure species parameters
435 // were found
436 auto& eosNode = speciesNode["equation-of-state"].getMapWhere(
437 "model", "Peng-Robinson", true);
438 AnyMap bin_a;
439 for (const auto& [species, coeff] : m_binaryParameters.at(name)) {
440 bin_a[species].setQuantity(coeff, "Pa*m^6/kmol^2");
441 }
442 eosNode["binary-a"] = std::move(bin_a);
443 }
444}
445
447{
448 double molarV = molarVolume();
449 double hh = m_b / molarV;
450 double zz = z();
451 double alpha_1 = daAlpha_dT();
452 double vpb = molarV + (1.0 + Sqrt2) * m_b;
453 double vmb = molarV + (1.0 - Sqrt2) * m_b;
454 double fac = alpha_1 / (2.0 * Sqrt2 * m_b);
455 double sresid_mol_R = log(zz*(1.0 - hh)) + fac * log(vpb / vmb) / GasConstant;
456 return GasConstant * sresid_mol_R;
457}
458
460{
461 double molarV = molarVolume();
462 double zz = z();
463 double aAlpha_1 = daAlpha_dT();
464 double T = temperature();
465 double vpb = molarV + (1 + Sqrt2) * m_b;
466 double vmb = molarV + (1 - Sqrt2) * m_b;
467 double fac = 1 / (2.0 * Sqrt2 * m_b);
468 return GasConstant * T * (zz - 1.0)
469 + fac * log(vpb / vmb) * (T * aAlpha_1 - m_aAlpha_mix);
470}
471
472double PengRobinson::liquidVolEst(double T, double& presGuess) const
473{
474 double v = m_b * 1.1;
475 double atmp, btmp, aAlphatmp;
476 calculateAB(atmp, btmp, aAlphatmp);
477 double pres = std::max(psatEst(T), presGuess);
478 double Vroot[3];
479 bool foundLiq = false;
480 int m = 0;
481 while (m < 100 && !foundLiq) {
482 int nsol = solveCubic(T, pres, atmp, btmp, aAlphatmp, Vroot);
483 if (nsol == 1 || nsol == 2) {
484 double pc = critPressure();
485 if (pres > pc) {
486 foundLiq = true;
487 }
488 pres *= 1.04;
489 } else {
490 foundLiq = true;
491 }
492 }
493
494 if (foundLiq) {
495 v = Vroot[0];
496 presGuess = pres;
497 } else {
498 v = -1.0;
499 }
500 return v;
501}
502
503double PengRobinson::densityCalc(double T, double presPa, int phaseRequested,
504 double rhoGuess)
505{
506 // It's necessary to set the temperature so that m_aAlpha_mix is set correctly.
508 double tcrit = critTemperature();
509 double mmw = meanMolecularWeight();
510 if (rhoGuess == -1.0) {
511 if (phaseRequested >= FLUID_LIQUID_0) {
512 double lqvol = liquidVolEst(T, presPa);
513 rhoGuess = mmw / lqvol;
514 }
515 } else {
516 // Assume the Gas phase initial guess, if nothing is specified to the routine
517 rhoGuess = presPa * mmw / (GasConstant * T);
518 }
519
520 double volGuess = mmw / rhoGuess;
521 m_NSolns = solveCubic(T, presPa, m_a, m_b, m_aAlpha_mix, m_Vroot);
522
523 double molarVolLast = m_Vroot[0];
524 if (m_NSolns >= 2) {
525 if (phaseRequested >= FLUID_LIQUID_0) {
526 molarVolLast = m_Vroot[0];
527 } else if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
528 molarVolLast = m_Vroot[2];
529 } else {
530 if (volGuess > m_Vroot[1]) {
531 molarVolLast = m_Vroot[2];
532 } else {
533 molarVolLast = m_Vroot[0];
534 }
535 }
536 } else if (m_NSolns == 1) {
537 if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT
538 || phaseRequested == FLUID_UNDEFINED)
539 {
540 molarVolLast = m_Vroot[0];
541 } else {
542 return -2.0;
543 }
544 } else if (m_NSolns == -1) {
545 if (phaseRequested >= FLUID_LIQUID_0 || phaseRequested == FLUID_UNDEFINED
546 || phaseRequested == FLUID_SUPERCRIT)
547 {
548 molarVolLast = m_Vroot[0];
549 } else if (T > tcrit) {
550 molarVolLast = m_Vroot[0];
551 } else {
552 return -2.0;
553 }
554 } else {
555 molarVolLast = m_Vroot[0];
556 return -1.0;
557 }
558 return mmw / molarVolLast;
559}
560
562{
563 double Vroot[3];
564 double T = temperature();
565 int nsol = solveCubic(T, pressure(), m_a, m_b, m_aAlpha_mix, Vroot);
566 if (nsol != 3) {
567 return critDensity();
568 }
569
570 auto resid = [this, T](double v) {
571 double pp;
572 return dpdVCalc(T, v, pp);
573 };
574
575 boost::uintmax_t maxiter = 100;
576 auto [lower, upper] = bmt::toms748_solve(
577 resid, Vroot[0], Vroot[1], bmt::eps_tolerance<double>(48), maxiter);
578
579 double mmw = meanMolecularWeight();
580 return mmw / (0.5 * (lower + upper));
581}
582
584{
585 double Vroot[3];
586 double T = temperature();
587 int nsol = solveCubic(T, pressure(), m_a, m_b, m_aAlpha_mix, Vroot);
588 if (nsol != 3) {
589 return critDensity();
590 }
591
592 auto resid = [this, T](double v) {
593 double pp;
594 return dpdVCalc(T, v, pp);
595 };
596
597 boost::uintmax_t maxiter = 100;
598 auto [lower, upper] = bmt::toms748_solve(
599 resid, Vroot[1], Vroot[2], bmt::eps_tolerance<double>(48), maxiter);
600
601 double mmw = meanMolecularWeight();
602 return mmw / (0.5 * (lower + upper));
603}
604
605double PengRobinson::dpdVCalc(double T, double molarVol, double& presCalc) const
606{
607 double denom = molarVol * molarVol + 2 * molarVol * m_b - m_b * m_b;
608 double vpb = molarVol + m_b;
609 double vmb = molarVol - m_b;
610 return -GasConstant * T / (vmb * vmb) + 2 * m_aAlpha_mix * vpb / (denom*denom);
611}
612
614{
616 return -1 / (molarVolume() * m_dpdV);
617}
618
620{
622 return -m_dpdT / (molarVolume() * m_dpdV);
623}
624
626{
628 return molarVolume() * sqrt(-cp_mole() / cv_mole() * m_dpdV / meanMolecularWeight());
629}
630
632{
633 double T = temperature();
634 double mv = molarVolume();
635 double pres;
636
637 m_dpdV = dpdVCalc(T, mv, pres);
638 double vmb = mv - m_b;
639 double denom = mv * mv + 2 * mv * m_b - m_b * m_b;
640 m_dpdT = (GasConstant / vmb - daAlpha_dT() / denom);
641}
642
644{
645 double temp = temperature();
646
647 // Update individual alpha
648 for (size_t j = 0; j < m_kk; j++) {
649 double critTemp_j = speciesCritTemperature(m_a_coeffs(j,j), m_b_coeffs[j]);
650 double sqt_alpha = 1 + m_kappa[j] * (1 - sqrt(temp / critTemp_j));
651 m_alpha[j] = sqt_alpha*sqt_alpha;
652 }
653
654 // Update aAlpha_i, j
655 for (size_t i = 0; i < m_kk; i++) {
656 for (size_t j = 0; j < m_kk; j++) {
657 m_aAlpha_binary(i, j) = sqrt(m_alpha[i] * m_alpha[j]) * m_a_coeffs(i,j);
658 }
659 }
661}
662
663void PengRobinson::calculateAB(double& aCalc, double& bCalc, double& aAlphaCalc) const
664{
665 bCalc = 0.0;
666 aCalc = 0.0;
667 aAlphaCalc = 0.0;
668 for (size_t i = 0; i < m_kk; i++) {
669 bCalc += moleFractions_[i] * m_b_coeffs[i];
670 for (size_t j = 0; j < m_kk; j++) {
671 aCalc += m_a_coeffs(i, j) * moleFractions_[i] * moleFractions_[j];
672 aAlphaCalc += m_aAlpha_binary(i, j) * moleFractions_[i] * moleFractions_[j];
673 }
674 }
675}
676
678{
679 double daAlphadT = 0.0, k, Tc, sqtTr, coeff1, coeff2;
680 for (size_t i = 0; i < m_kk; i++) {
681 // Calculate first derivative of alpha for individual species
682 Tc = speciesCritTemperature(m_a_coeffs(i,i), m_b_coeffs[i]);
683 sqtTr = sqrt(temperature() / Tc);
684 coeff1 = 1 / (Tc*sqtTr);
685 coeff2 = sqtTr - 1;
686 k = m_kappa[i];
687 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
688 }
689 // Calculate mixture derivative
690 for (size_t i = 0; i < m_kk; i++) {
691 for (size_t j = 0; j < m_kk; j++) {
692 daAlphadT += moleFractions_[i] * moleFractions_[j] * 0.5
693 * m_aAlpha_binary(i, j)
694 * (m_dalphadT[i] / m_alpha[i] + m_dalphadT[j] / m_alpha[j]);
695 }
696 }
697 return daAlphadT;
698}
699
701{
702 for (size_t i = 0; i < m_kk; i++) {
703 double Tcrit_i = speciesCritTemperature(m_a_coeffs(i, i), m_b_coeffs[i]);
704 double sqt_Tr = sqrt(temperature() / Tcrit_i);
705 double coeff1 = 1 / (Tcrit_i*sqt_Tr);
706 double coeff2 = sqt_Tr - 1;
707 // Calculate first and second derivatives of alpha for individual species
708 double k = m_kappa[i];
709 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
710 m_d2alphadT2[i] = (k*k + k) * coeff1 / (2*sqt_Tr*sqt_Tr*Tcrit_i);
711 }
712
713 // Calculate mixture derivative
714 double d2aAlphadT2 = 0.0;
715 for (size_t i = 0; i < m_kk; i++) {
716 double alphai = m_alpha[i];
717 for (size_t j = 0; j < m_kk; j++) {
718 double alphaj = m_alpha[j];
719 double alphaij = alphai * alphaj;
720 double term1 = m_d2alphadT2[i] / alphai + m_d2alphadT2[j] / alphaj;
721 double term2 = 2 * m_dalphadT[i] * m_dalphadT[j] / alphaij;
722 double term3 = m_dalphadT[i] / alphai + m_dalphadT[j] / alphaj;
723 d2aAlphadT2 += 0.5 * moleFractions_[i] * moleFractions_[j]
724 * m_aAlpha_binary(i, j)
725 * (term1 + term2 - 0.5 * term3 * term3);
726 }
727 }
728 return d2aAlphadT2;
729}
730
731void PengRobinson::calcCriticalConditions(double& pc, double& tc, double& vc) const
732{
733 if (m_b <= 0.0) {
734 tc = 1000000.;
735 pc = 1.0E13;
736 vc = omega_vc * GasConstant * tc / pc;
737 return;
738 }
739 if (m_a <= 0.0) {
740 tc = 0.0;
741 pc = 0.0;
742 vc = 2.0 * m_b;
743 return;
744 }
745 tc = m_a * omega_b / (m_b * omega_a * GasConstant);
746 pc = omega_b * GasConstant * tc / m_b;
747 vc = omega_vc * GasConstant * tc / pc;
748}
749
750int PengRobinson::solveCubic(double T, double pres, double a, double b, double aAlpha,
751 double Vroot[3]) const
752{
753 // Derive the coefficients of the cubic polynomial (in terms of molar volume v)
754 double bsqr = b * b;
755 double RT_p = GasConstant * T / pres;
756 double aAlpha_p = aAlpha / pres;
757 double an = 1.0;
758 double bn = (b - RT_p);
759 double cn = -(2 * RT_p * b - aAlpha_p + 3 * bsqr);
760 double dn = (bsqr * RT_p + bsqr * b - aAlpha_p * b);
761
762 double tc = a * omega_b / (b * omega_a * GasConstant);
763 double pc = omega_b * GasConstant * tc / b;
764 double vc = omega_vc * GasConstant * tc / pc;
765
766 return MixtureFugacityTP::solveCubic(T, pres, a, b, aAlpha, Vroot,
767 an, bn, cn, dn, tc, vc);
768}
769
771{
773
774 parameters["aAlpha_mix"] = m_aAlpha_mix;
775 parameters["b_mix"] = m_b;
776 parameters["daAlpha_dT"] = daAlpha_dT();
777 parameters["d2aAlpha_dT2"] = d2aAlpha_dT2();
778
779 return parameters;
780}
781
782}
Declaration for class Cantera::Species.
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
Definition AnyMap.h:640
bool empty() const
Return boolean indicating whether AnyMap is empty.
Definition AnyMap.cpp:1468
double convert(const string &key, const string &units) const
Convert the item stored by the given key to the units specified in units.
Definition AnyMap.cpp:1595
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
Definition AnyMap.cpp:1841
virtual void resize(size_t n, size_t m, double v=0.0)
Resize the array, and fill the new entries with 'v'.
Definition Array.cpp:47
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition AnyMap.h:749
double critPressure() const override
Critical pressure (Pa).
double critDensity() const override
Critical density (kg/m3).
void getGibbs_ref(double *g) const override
Returns the vector of the Gibbs function of the reference state at the current temperature of the sol...
double critTemperature() const override
Critical temperature (K).
virtual void _updateReferenceStateThermo() const
Updates the reference state thermodynamic functions at the current T of the solution.
int solveCubic(double T, double pres, double a, double b, double aAlpha, double Vroot[3], double an, double bn, double cn, double dn, double tc, double vc) const
Solve the cubic equation of state.
vector< double > moleFractions_
Storage for the current values of the mole fractions of the species.
void setTemperature(const double temp) override
Set the temperature of the phase.
virtual double psatEst(double TKelvin) const
Estimate for the saturation pressure.
void getStandardVolumes(double *vol) const override
Get the molar volumes of each species in their standard states at the current T and P of the solution...
vector< double > m_cp0_R
Temporary storage for dimensionless reference state heat capacities.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
double z() const
Calculate the value of z.
void getEnthalpy_RT_ref(double *hrt) const override
Returns the vector of nondimensional enthalpies of the reference state at the current temperature of ...
An error indicating that an unimplemented function has been called.
double thermalExpansionCoeff() const override
Return the volumetric thermal expansion coefficient. Units: 1/K.
double densSpinodalLiquid() const override
Return the value of the density at the liquid spinodal point (on the liquid side) for the current tem...
void getPartialMolarEnthalpies(double *hbar) const override
Returns an array of partial molar enthalpies for the species in the mixture.
void getChemPotentials(double *mu) const override
Get the species chemical potentials. Units: J/kmol.
void setSpeciesCoeffs(const string &species, double a, double b, double w)
Set the pure fluid interaction parameters for a species.
double sresid() const override
Calculate the deviation terms for the total entropy of the mixture from the ideal gas mixture.
double soundSpeed() const override
Return the speed of sound. Units: m/s.
double pressure() const override
Return the thermodynamic pressure (Pa).
map< string, map< string, double > > m_binaryParameters
Explicitly-specified binary interaction parameters, to enable serialization.
void getSpeciesParameters(const string &name, AnyMap &speciesNode) const override
Get phase-specific parameters of a Species object such that an identical one could be reconstructed a...
static const double omega_b
Omega constant: b0 (= omega_b) used in Peng-Robinson equation of state.
vector< double > m_pp
Temporary storage - length = m_kk.
void setBinaryCoeffs(const string &species_i, const string &species_j, double a)
Set values for the interaction parameter between two species.
vector< double > m_dpdni
Vector of derivatives of pressure with respect to mole number.
PengRobinson(const string &infile="", const string &id="")
Construct and initialize a PengRobinson object directly from an input file.
void initThermo() override
Initialize the ThermoPhase object after all species have been set up.
double densSpinodalGas() const override
Return the value of the density at the gas spinodal point (on the gas side) for the current temperatu...
double m_dpdT
The derivative of the pressure with respect to the temperature.
void getPartialMolarVolumes(double *vbar) const override
Return an array of partial molar volumes for the species in the mixture.
double cv_mole() const override
Molar heat capacity at constant volume and composition [J/kmol/K].
double liquidVolEst(double T, double &pres) const override
Estimate for the molar volume of the liquid.
int solveCubic(double T, double pres, double a, double b, double aAlpha, double Vroot[3]) const
Prepare variables and call the function to solve the cubic equation of state.
double daAlpha_dT() const
Calculate temperature derivative .
void calculatePressureDerivatives() const
Calculate and at the current conditions.
double isothermalCompressibility() const override
Returns the isothermal compressibility. Units: 1/Pa.
double hresid() const override
Calculate the deviation terms for the total enthalpy of the mixture from the ideal gas mixture.
double m_aAlpha_mix
Value of in the equation of state.
double m_a
Value of in the equation of state.
static const double omega_a
Omega constant: a0 (= omega_a) used in Peng-Robinson equation of state.
static const double omega_vc
Omega constant for the critical molar volume.
vector< CoeffSource > m_coeffSource
For each species, specifies the source of the a, b, and omega coefficients.
void calculateAB(double &aCalc, double &bCalc, double &aAlpha) const
Calculate the , , and parameters given the temperature.
void getPartialMolarIntEnergies(double *ubar) const override
Return an array of partial molar internal energies for the species in the mixture.
void updateMixingExpressions() override
Update the , , and parameters.
double cp_mole() const override
Molar heat capacity at constant pressure and composition [J/kmol/K].
double m_b
Value of in the equation of state.
void getPartialMolarCp(double *cpbar) const override
Calculate species-specific molar specific heats.
double standardConcentration(size_t k=0) const override
Returns the standard concentration , which is used to normalize the generalized concentration.
AnyMap getAuxiliaryData() override
Return parameters used by the Peng-Robinson equation of state.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
double dpdVCalc(double T, double molarVol, double &presCalc) const override
Calculate the pressure and the pressure derivative given the temperature and the molar volume.
void getActivityCoefficients(double *ac) const override
Get the array of non-dimensional activity coefficients at the current solution temperature,...
double d2aAlpha_dT2() const
Calculate second derivative .
double densityCalc(double T, double pressure, int phase, double rhoguess) override
Calculates the density given the temperature and the pressure and a guess at the density.
void getPartialMolarEntropies(double *sbar) const override
Returns an array of partial molar entropies of the species in the solution.
double m_dpdV
The derivative of the pressure with respect to the volume.
double speciesCritTemperature(double a, double b) const
Calculate species-specific critical temperature.
vector< double > m_acentric
acentric factor for each species, length m_kk
vector< double > m_workS
Vector of size m_kk, used as a temporary holding area.
Definition Phase.h:945
size_t m_kk
Number of species in the phase.
Definition Phase.h:921
double temperature() const
Temperature (K).
Definition Phase.h:629
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition Phase.h:722
map< string, shared_ptr< Species > > m_species
Map of Species objects.
Definition Phase.h:934
size_t speciesIndex(const string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:147
double moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition Phase.cpp:504
double mean_X(const double *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition Phase.cpp:679
shared_ptr< Species > species(const string &name) const
Return the Species object for the named species.
Definition Phase.cpp:937
virtual double molarVolume() const
Molar volume (m^3/kmol).
Definition Phase.cpp:644
string name() const
Return the name of the phase.
Definition Phase.cpp:20
double RT() const
Return the Gas Constant multiplied by the current temperature.
AnyMap parameters(bool withInput=true) const
Returns the parameters of a ThermoPhase object such that an identical one could be reconstructed usin...
virtual AnyMap getAuxiliaryData()
Return intermediate or model-specific parameters used by particular derived classes.
void initThermoFile(const string &inputFile, const string &id)
Initialize a ThermoPhase object using an input file.
virtual void getSpeciesParameters(const string &name, AnyMap &speciesNode) const
Get phase-specific parameters of a Species object such that an identical one could be reconstructed a...
virtual double refPressure() const
Returns the reference pressure in Pa.
Unit conversion utility.
Definition Units.h:169
double convert(double value, const string &src, const string &dest) const
Convert value from the units of src to the units of dest.
Definition Units.cpp:539
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
Definition utilities.h:104
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:120
const double Sqrt2
Sqrt(2)
Definition ct_defs.h:71
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const double SmallNumber
smallest number to compare to zero.
Definition ct_defs.h:158
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...