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