Cantera  3.2.0
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 warn_deprecated("PengRobinson::densSpinodalLiquid",
564 "To be removed after Cantera 3.1.");
565 double Vroot[3];
566 double T = temperature();
567 int nsol = solveCubic(T, pressure(), m_a, m_b, m_aAlpha_mix, Vroot);
568 if (nsol != 3) {
569 return critDensity();
570 }
571
572 auto resid = [this, T](double v) {
573 double pp;
574 return dpdVCalc(T, v, pp);
575 };
576
577 boost::uintmax_t maxiter = 100;
578 auto [lower, upper] = bmt::toms748_solve(
579 resid, Vroot[0], Vroot[1], bmt::eps_tolerance<double>(48), maxiter);
580
581 double mmw = meanMolecularWeight();
582 return mmw / (0.5 * (lower + upper));
583}
584
586{
587 warn_deprecated("PengRobinson::densSpinodalGas",
588 "To be removed after Cantera 3.1.");
589 double Vroot[3];
590 double T = temperature();
591 int nsol = solveCubic(T, pressure(), m_a, m_b, m_aAlpha_mix, Vroot);
592 if (nsol != 3) {
593 return critDensity();
594 }
595
596 auto resid = [this, T](double v) {
597 double pp;
598 return dpdVCalc(T, v, pp);
599 };
600
601 boost::uintmax_t maxiter = 100;
602 auto [lower, upper] = bmt::toms748_solve(
603 resid, Vroot[1], Vroot[2], bmt::eps_tolerance<double>(48), maxiter);
604
605 double mmw = meanMolecularWeight();
606 return mmw / (0.5 * (lower + upper));
607}
608
609double PengRobinson::dpdVCalc(double T, double molarVol, double& presCalc) const
610{
611 double denom = molarVol * molarVol + 2 * molarVol * m_b - m_b * m_b;
612 double vpb = molarVol + m_b;
613 double vmb = molarVol - m_b;
614 return -GasConstant * T / (vmb * vmb) + 2 * m_aAlpha_mix * vpb / (denom*denom);
615}
616
618{
620 return -1 / (molarVolume() * m_dpdV);
621}
622
624{
626 return -m_dpdT / (molarVolume() * m_dpdV);
627}
628
630{
632 return molarVolume() * sqrt(-cp_mole() / cv_mole() * m_dpdV / meanMolecularWeight());
633}
634
636{
637 double T = temperature();
638 double mv = molarVolume();
639 double pres;
640
641 m_dpdV = dpdVCalc(T, mv, pres);
642 double vmb = mv - m_b;
643 double denom = mv * mv + 2 * mv * m_b - m_b * m_b;
644 m_dpdT = (GasConstant / vmb - daAlpha_dT() / denom);
645}
646
648{
649 double temp = temperature();
650
651 // Update individual alpha
652 for (size_t j = 0; j < m_kk; j++) {
653 double critTemp_j = speciesCritTemperature(m_a_coeffs(j,j), m_b_coeffs[j]);
654 double sqt_alpha = 1 + m_kappa[j] * (1 - sqrt(temp / critTemp_j));
655 m_alpha[j] = sqt_alpha*sqt_alpha;
656 }
657
658 // Update aAlpha_i, j
659 for (size_t i = 0; i < m_kk; i++) {
660 for (size_t j = 0; j < m_kk; j++) {
661 m_aAlpha_binary(i, j) = sqrt(m_alpha[i] * m_alpha[j]) * m_a_coeffs(i,j);
662 }
663 }
665}
666
667void PengRobinson::calculateAB(double& aCalc, double& bCalc, double& aAlphaCalc) const
668{
669 bCalc = 0.0;
670 aCalc = 0.0;
671 aAlphaCalc = 0.0;
672 for (size_t i = 0; i < m_kk; i++) {
673 bCalc += moleFractions_[i] * m_b_coeffs[i];
674 for (size_t j = 0; j < m_kk; j++) {
675 aCalc += m_a_coeffs(i, j) * moleFractions_[i] * moleFractions_[j];
676 aAlphaCalc += m_aAlpha_binary(i, j) * moleFractions_[i] * moleFractions_[j];
677 }
678 }
679}
680
682{
683 double daAlphadT = 0.0, k, Tc, sqtTr, coeff1, coeff2;
684 for (size_t i = 0; i < m_kk; i++) {
685 // Calculate first derivative of alpha for individual species
686 Tc = speciesCritTemperature(m_a_coeffs(i,i), m_b_coeffs[i]);
687 sqtTr = sqrt(temperature() / Tc);
688 coeff1 = 1 / (Tc*sqtTr);
689 coeff2 = sqtTr - 1;
690 k = m_kappa[i];
691 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
692 }
693 // Calculate mixture derivative
694 for (size_t i = 0; i < m_kk; i++) {
695 for (size_t j = 0; j < m_kk; j++) {
696 daAlphadT += moleFractions_[i] * moleFractions_[j] * 0.5
697 * m_aAlpha_binary(i, j)
698 * (m_dalphadT[i] / m_alpha[i] + m_dalphadT[j] / m_alpha[j]);
699 }
700 }
701 return daAlphadT;
702}
703
705{
706 for (size_t i = 0; i < m_kk; i++) {
707 double Tcrit_i = speciesCritTemperature(m_a_coeffs(i, i), m_b_coeffs[i]);
708 double sqt_Tr = sqrt(temperature() / Tcrit_i);
709 double coeff1 = 1 / (Tcrit_i*sqt_Tr);
710 double coeff2 = sqt_Tr - 1;
711 // Calculate first and second derivatives of alpha for individual species
712 double k = m_kappa[i];
713 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
714 m_d2alphadT2[i] = (k*k + k) * coeff1 / (2*sqt_Tr*sqt_Tr*Tcrit_i);
715 }
716
717 // Calculate mixture derivative
718 double d2aAlphadT2 = 0.0;
719 for (size_t i = 0; i < m_kk; i++) {
720 double alphai = m_alpha[i];
721 for (size_t j = 0; j < m_kk; j++) {
722 double alphaj = m_alpha[j];
723 double alphaij = alphai * alphaj;
724 double term1 = m_d2alphadT2[i] / alphai + m_d2alphadT2[j] / alphaj;
725 double term2 = 2 * m_dalphadT[i] * m_dalphadT[j] / alphaij;
726 double term3 = m_dalphadT[i] / alphai + m_dalphadT[j] / alphaj;
727 d2aAlphadT2 += 0.5 * moleFractions_[i] * moleFractions_[j]
728 * m_aAlpha_binary(i, j)
729 * (term1 + term2 - 0.5 * term3 * term3);
730 }
731 }
732 return d2aAlphadT2;
733}
734
735void PengRobinson::calcCriticalConditions(double& pc, double& tc, double& vc) const
736{
737 if (m_b <= 0.0) {
738 tc = 1000000.;
739 pc = 1.0E13;
740 vc = omega_vc * GasConstant * tc / pc;
741 return;
742 }
743 if (m_a <= 0.0) {
744 tc = 0.0;
745 pc = 0.0;
746 vc = 2.0 * m_b;
747 return;
748 }
749 tc = m_a * omega_b / (m_b * omega_a * GasConstant);
750 pc = omega_b * GasConstant * tc / m_b;
751 vc = omega_vc * GasConstant * tc / pc;
752}
753
754int PengRobinson::solveCubic(double T, double pres, double a, double b, double aAlpha,
755 double Vroot[3]) const
756{
757 // Derive the coefficients of the cubic polynomial (in terms of molar volume v)
758 double bsqr = b * b;
759 double RT_p = GasConstant * T / pres;
760 double aAlpha_p = aAlpha / pres;
761 double an = 1.0;
762 double bn = (b - RT_p);
763 double cn = -(2 * RT_p * b - aAlpha_p + 3 * bsqr);
764 double dn = (bsqr * RT_p + bsqr * b - aAlpha_p * b);
765
766 double tc = a * omega_b / (b * omega_a * GasConstant);
767 double pc = omega_b * GasConstant * tc / b;
768 double vc = omega_vc * GasConstant * tc / pc;
769
770 return MixtureFugacityTP::solveCubic(T, pres, a, b, aAlpha, Vroot,
771 an, bn, cn, dn, tc, vc);
772}
773
775{
777
778 parameters["aAlpha_mix"] = m_aAlpha_mix;
779 parameters["b_mix"] = m_b;
780 parameters["daAlpha_dT"] = daAlpha_dT();
781 parameters["d2aAlpha_dT2"] = d2aAlpha_dT2();
782
783 return parameters;
784}
785
786}
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
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition AnyMap.cpp:1997
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...