Cantera  3.3.0a1
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
14namespace Cantera
15{
16
17const double PengRobinson::omega_a = 4.5723552892138218E-01;
18const double PengRobinson::omega_b = 7.77960739038885E-02;
19const double PengRobinson::omega_vc = 3.07401308698703833E-01;
20
21PengRobinson::PengRobinson(const string& infile, const string& id_)
22{
23 initThermoFile(infile, id_);
24}
25
26void PengRobinson::setSpeciesCoeffs(const string& species, double a, double b, double w)
27{
28 size_t k = speciesIndex(species, true);
29
30 // Calculate value of kappa (independent of temperature)
31 // w is an acentric factor of species
32 if (w <= 0.491) {
33 m_kappa[k] = 0.37464 + 1.54226*w - 0.26992*w*w;
34 } else {
35 m_kappa[k] = 0.374642 + 1.487503*w - 0.164423*w*w + 0.016666*w*w*w;
36 }
37 m_acentric[k] = w; // store the original acentric factor to enable serialization
38
39 // Calculate alpha (temperature dependent interaction parameter)
40 double critTemp = speciesCritTemperature(a, b);
41 double sqt_T_r = sqrt(temperature() / critTemp);
42 double sqt_alpha = 1 + m_kappa[k] * (1 - sqt_T_r);
43 m_alpha[k] = sqt_alpha*sqt_alpha;
44
45 m_a_coeffs(k,k) = a;
46 double aAlpha_k = a*m_alpha[k];
47 m_aAlpha_binary(k,k) = aAlpha_k;
48
49 // standard mixing rule for cross-species interaction term
50 for (size_t j = 0; j < m_kk; j++) {
51 if (k == j) {
52 continue;
53 }
54 double a0kj = sqrt(m_a_coeffs(j,j) * a);
55 double aAlpha_j = a*m_alpha[j];
56 double a_Alpha = sqrt(aAlpha_j*aAlpha_k);
57 if (m_a_coeffs(j, k) == 0) {
58 m_a_coeffs(j, k) = a0kj;
59 m_aAlpha_binary(j, k) = a_Alpha;
60 m_a_coeffs(k, j) = a0kj;
61 m_aAlpha_binary(k, j) = a_Alpha;
62 }
63 }
64 m_b_coeffs[k] = b;
65}
66
67void PengRobinson::setBinaryCoeffs(const string& species_i,
68 const string& species_j, double a0)
69{
70 size_t ki = speciesIndex(species_i, true);
71 size_t kj = speciesIndex(species_j, true);
72
73 m_a_coeffs(ki, kj) = m_a_coeffs(kj, ki) = a0;
74 m_binaryParameters[species_i][species_j] = a0;
75 m_binaryParameters[species_j][species_i] = a0;
76 // Calculate alpha_ij
77 double alpha_ij = m_alpha[ki] * m_alpha[kj];
78 m_aAlpha_binary(ki, kj) = m_aAlpha_binary(kj, ki) = a0*alpha_ij;
79}
80
81// ------------Molar Thermodynamic Properties -------------------------
82
84{
86 double T = temperature();
87 double mv = molarVolume();
88 double vpb = mv + (1 + Sqrt2) * m_b;
89 double vmb = mv + (1 - Sqrt2) * m_b;
91 double cpref = GasConstant * mean_X(m_cp0_R);
92 double dHdT_V = cpref + mv * m_dpdT - GasConstant
93 + 1.0 / (2.0 * Sqrt2 * m_b) * log(vpb / vmb) * T * d2aAlpha_dT2();
94 return dHdT_V - (mv + T * m_dpdT / m_dpdV) * m_dpdT;
95}
96
98{
100 double T = temperature();
102 return (cp_mole() + T * m_dpdT * m_dpdT / m_dpdV);
103}
104
106{
108 // Get a copy of the private variables stored in the State object
109 double T = temperature();
110 double mv = molarVolume();
111 double denom = mv * mv + 2 * mv * m_b - m_b * m_b;
112 return GasConstant * T / (mv - m_b) - m_aAlpha_mix / denom;
113}
114
116{
118 return 1.0 / m_workS[k];
119}
120
122{
123 double mv = molarVolume();
124 double vpb2 = mv + (1 + Sqrt2) * m_b;
125 double vmb2 = mv + (1 - Sqrt2) * m_b;
126 double vmb = mv - m_b;
127 double pres = pressure();
128
129 for (size_t k = 0; k < m_kk; k++) {
130 m_pp[k] = 0.0;
131 for (size_t i = 0; i < m_kk; i++) {
132 m_pp[k] += moleFractions_[i] * m_aAlpha_binary(k, i);
133 }
134 }
135 double num = 0;
136 double denom = 2 * Sqrt2 * m_b * m_b;
137 double denom2 = m_b * (mv * mv + 2 * mv * m_b - m_b * m_b);
138 double RT_ = RT();
139 for (size_t k = 0; k < m_kk; k++) {
140 num = 2 * m_b * m_pp[k] - m_aAlpha_mix * m_b_coeffs[k];
141 ac[k] = (-RT_ * log(pres * mv/ RT_) + RT_ * log(mv / vmb)
142 + RT_ * m_b_coeffs[k] / vmb
143 - (num /denom) * log(vpb2/vmb2)
144 - m_aAlpha_mix * m_b_coeffs[k] * mv/denom2
145 );
146 }
147 for (size_t k = 0; k < m_kk; k++) {
148 ac[k] = exp(ac[k]/ RT_);
149 }
150}
151
152// ---- Partial Molar Properties of the Solution -----------------
153
155{
156 getGibbs_ref(mu);
157 double RT_ = RT();
158 for (size_t k = 0; k < m_kk; k++) {
159 double xx = std::max(SmallNumber, moleFraction(k));
160 mu[k] += RT_ * (log(xx));
161 }
162
163 double mv = molarVolume();
164 double vmb = mv - m_b;
165 double vpb2 = mv + (1 + Sqrt2) * m_b;
166 double vmb2 = mv + (1 - Sqrt2) * m_b;
167
168 for (size_t k = 0; k < m_kk; k++) {
169 m_pp[k] = 0.0;
170 for (size_t i = 0; i < m_kk; i++) {
171 m_pp[k] += moleFractions_[i] * m_aAlpha_binary(k, i);
172 }
173 }
174 double pres = pressure();
175 double refP = refPressure();
176 double denom = 2 * Sqrt2 * m_b * m_b;
177 double denom2 = m_b * (mv * mv + 2 * mv * m_b - m_b * m_b);
178
179 for (size_t k = 0; k < m_kk; k++) {
180 double num = 2 * m_b * m_pp[k] - m_aAlpha_mix * m_b_coeffs[k];
181
182 mu[k] += RT_ * log(pres/refP) - RT_ * log(pres * mv / RT_)
183 + RT_ * log(mv / vmb) + RT_ * m_b_coeffs[k] / vmb
184 - (num /denom) * log(vpb2/vmb2)
185 - m_aAlpha_mix * m_b_coeffs[k] * mv/denom2;
186 }
187}
188
190{
191 // First we get the reference state contributions
192 getEnthalpy_RT_ref(hbar);
193 scale(hbar, hbar+m_kk, hbar, RT());
194 vector<double> tmp;
195 tmp.resize(m_kk,0.0);
196
197 // We calculate m_dpdni
198 double T = temperature();
199 double mv = molarVolume();
200 double vmb = mv - m_b;
201 double vpb2 = mv + (1 + Sqrt2) * m_b;
202 double vmb2 = mv + (1 - Sqrt2) * m_b;
203 double daAlphadT = daAlpha_dT();
204
205 for (size_t k = 0; k < m_kk; k++) {
206 m_pp[k] = 0.0;
207 tmp[k] = 0.0;
208 for (size_t i = 0; i < m_kk; i++) {
209 double grad_aAlpha = m_dalphadT[i]/m_alpha[i] + m_dalphadT[k]/m_alpha[k];
210 m_pp[k] += moleFractions_[i] * m_aAlpha_binary(k, i);
211 tmp[k] +=moleFractions_[i] * m_aAlpha_binary(k, i) * grad_aAlpha;
212 }
213 }
214
215 double denom = mv * mv + 2 * mv * m_b - m_b * m_b;
216 double denom2 = denom * denom;
217 double RT_ = RT();
218 for (size_t k = 0; k < m_kk; k++) {
219 m_dpdni[k] = RT_ / vmb + RT_ * m_b_coeffs[k] / (vmb * vmb)
220 - 2.0 * m_pp[k] / denom + 2 * vmb * m_aAlpha_mix * m_b_coeffs[k] / denom2;
221 }
222
223 double fac = T * daAlphadT - m_aAlpha_mix;
225 double fac2 = mv + T * m_dpdT / m_dpdV;
226 double fac3 = 2 * Sqrt2 * m_b * m_b;
227 double fac4 = 0;
228 for (size_t k = 0; k < m_kk; k++) {
229 fac4 = T*tmp[k] -2 * m_pp[k];
230 double hE_v = mv * m_dpdni[k] - RT_
231 - m_b_coeffs[k] / fac3 * log(vpb2 / vmb2) * fac
232 + (mv * m_b_coeffs[k]) / (m_b * denom) * fac
233 + 1 / (2 * Sqrt2 * m_b) * log(vpb2 / vmb2) * fac4;
234 hbar[k] = hbar[k] + hE_v;
235 hbar[k] -= fac2 * m_dpdni[k];
236 }
237}
238
240{
241 // Using the identity : (hk - T*sk) = gk
242 double T = temperature();
245 for (size_t k = 0; k < m_kk; k++) {
246 sbar[k] = (sbar[k] - m_workS[k])/T;
247 }
248}
249
251{
252 // u_i = h_i - p*v_i
253 double p = pressure();
256 for (size_t k = 0; k < m_kk; k++) {
257 ubar[k] = ubar[k] - p*m_workS[k];
258 }
259}
260
261void PengRobinson::getPartialMolarCp(double* cpbar) const
262{
263 throw NotImplementedError("PengRobinson::getPartialMolarCp");
264}
265
267{
268 for (size_t k = 0; k < m_kk; k++) {
269 m_pp[k] = 0.0;
270 for (size_t i = 0; i < m_kk; i++) {
271 m_pp[k] += moleFractions_[i] * m_aAlpha_binary(k, i);
272 }
273 }
274
275 double mv = molarVolume();
276 double vmb = mv - m_b;
277 double vpb = mv + m_b;
278 double fac = mv * mv + 2 * mv * m_b - m_b * m_b;
279 double fac2 = fac * fac;
280 double RT_ = RT();
281
282 for (size_t k = 0; k < m_kk; k++) {
283 double num = RT_ + RT_ * m_b/ vmb + RT_ * m_b_coeffs[k] / vmb
284 + RT_ * m_b * m_b_coeffs[k] /(vmb * vmb) - 2 * mv * m_pp[k] / fac
285 + 2 * mv * vmb * m_aAlpha_mix * m_b_coeffs[k] / fac2;
286 double denom = pressure() + RT_ * m_b / (vmb * vmb) + m_aAlpha_mix/fac
287 - 2 * mv* vpb * m_aAlpha_mix / fac2;
288 vbar[k] = num / denom;
289 }
290}
291
292double PengRobinson::speciesCritTemperature(double a, double b) const
293{
294 if (b <= 0.0) {
295 return 1000000.;
296 } else if (a <= 0.0) {
297 return 0.0;
298 } else {
299 return a * omega_b / (b * omega_a * GasConstant);
300 }
301}
302
303bool PengRobinson::addSpecies(shared_ptr<Species> spec)
304{
305 bool added = MixtureFugacityTP::addSpecies(spec);
306 if (added) {
307 m_a_coeffs.resize(m_kk, m_kk, 0.0);
308 m_b_coeffs.push_back(0.0);
309 m_aAlpha_binary.resize(m_kk, m_kk, 0.0);
310 m_kappa.push_back(0.0);
311 m_acentric.push_back(0.0);
312 m_alpha.push_back(0.0);
313 m_dalphadT.push_back(0.0);
314 m_d2alphadT2.push_back(0.0);
315 m_pp.push_back(0.0);
316 m_partialMolarVolumes.push_back(0.0);
317 m_dpdni.push_back(0.0);
318 m_coeffSource.push_back(CoeffSource::EoS);
319 }
320 return added;
321}
322
324{
325 // Contents of 'critical-properties.yaml', loaded later if needed
326 AnyMap critPropsDb;
327 std::unordered_map<string, AnyMap*> dbSpecies;
328
329 for (auto& [name, species] : m_species) {
330 auto& data = species->input;
331 size_t k = speciesIndex(name, true);
332 if (m_a_coeffs(k, k) != 0.0) {
333 continue;
334 }
335 bool foundCoeffs = false;
336 if (data.hasKey("equation-of-state") &&
337 data["equation-of-state"].hasMapWhere("model", "Peng-Robinson"))
338 {
339 // Read a and b coefficients and acentric factor w_ac from species input
340 // information, specified in a YAML input file.
341 auto eos = data["equation-of-state"].getMapWhere(
342 "model", "Peng-Robinson");
343 if (eos.hasKey("a") && eos.hasKey("b") && eos.hasKey("acentric-factor")) {
344 double a0 = eos.convert("a", "Pa*m^6/kmol^2");
345 double b = eos.convert("b", "m^3/kmol");
346 // unitless acentric factor:
347 double w = eos["acentric-factor"].asDouble();
348 setSpeciesCoeffs(name, a0, b, w);
349 foundCoeffs = true;
350 }
351
352 if (eos.hasKey("binary-a")) {
353 AnyMap& binary_a = eos["binary-a"].as<AnyMap>();
354 const UnitSystem& units = binary_a.units();
355 for (auto& [name2, coeff] : binary_a) {
356 double a0 = units.convert(coeff, "Pa*m^6/kmol^2");
357 setBinaryCoeffs(name, name2, a0);
358 }
359 }
360 if (foundCoeffs) {
361 m_coeffSource[k] = CoeffSource::EoS;
362 continue;
363 }
364 }
365
366 // Coefficients have not been populated from model-specific input
367 double Tc = NAN, Pc = NAN, omega_ac = NAN;
368 if (data.hasKey("critical-parameters")) {
369 // Use critical state information stored in the species entry to
370 // calculate a, b, and the acentric factor.
371 auto& critProps = data["critical-parameters"].as<AnyMap>();
372 Tc = critProps.convert("critical-temperature", "K");
373 Pc = critProps.convert("critical-pressure", "Pa");
374 omega_ac = critProps["acentric-factor"].asDouble();
375 m_coeffSource[k] = CoeffSource::CritProps;
376 } else {
377 // Search 'crit-properties.yaml' to find Tc and Pc. Load data if needed.
378 if (critPropsDb.empty()) {
379 critPropsDb = AnyMap::fromYamlFile("critical-properties.yaml");
380 dbSpecies = critPropsDb["species"].asMap("name");
381 }
382
383 // All names in critical-properties.yaml are upper case
384 auto ucName = boost::algorithm::to_upper_copy(name);
385 if (dbSpecies.count(ucName)) {
386 auto& spec = *dbSpecies.at(ucName);
387 auto& critProps = spec["critical-parameters"].as<AnyMap>();
388 Tc = critProps.convert("critical-temperature", "K");
389 Pc = critProps.convert("critical-pressure", "Pa");
390 omega_ac = critProps["acentric-factor"].asDouble();
391 m_coeffSource[k] = CoeffSource::Database;
392 }
393 }
394
395 // Check if critical properties were found in either location
396 if (!isnan(Tc)) {
397 double a = omega_a * std::pow(GasConstant * Tc, 2) / Pc;
398 double b = omega_b * GasConstant * Tc / Pc;
399 setSpeciesCoeffs(name, a, b, omega_ac);
400 } else {
401 throw InputFileError("PengRobinson::initThermo", data,
402 "No Peng-Robinson model parameters or critical properties found for "
403 "species '{}'", name);
404 }
405 }
406}
407
408void PengRobinson::getSpeciesParameters(const string& name, AnyMap& speciesNode) const
409{
411 size_t k = speciesIndex(name, true);
412
413 // Pure species parameters
414 if (m_coeffSource[k] == CoeffSource::EoS) {
415 auto& eosNode = speciesNode["equation-of-state"].getMapWhere(
416 "model", "Peng-Robinson", true);
417 eosNode["a"].setQuantity(m_a_coeffs(k, k), "Pa*m^6/kmol^2");
418 eosNode["b"].setQuantity(m_b_coeffs[k], "m^3/kmol");
419 eosNode["acentric-factor"] = m_acentric[k];
420 } else if (m_coeffSource[k] == CoeffSource::CritProps) {
421 auto& critProps = speciesNode["critical-parameters"];
422 double Tc = speciesCritTemperature(m_a_coeffs(k, k), m_b_coeffs[k]);
423 double Pc = omega_b * GasConstant * Tc / m_b_coeffs[k];
424 critProps["critical-temperature"].setQuantity(Tc, "K");
425 critProps["critical-pressure"].setQuantity(Pc, "Pa");
426 critProps["acentric-factor"] = m_acentric[k];
427 }
428 // Nothing to do in the case where the parameters are from the database
429
430 if (m_binaryParameters.count(name)) {
431 // Include binary parameters regardless of where the pure species parameters
432 // were found
433 auto& eosNode = speciesNode["equation-of-state"].getMapWhere(
434 "model", "Peng-Robinson", true);
435 AnyMap bin_a;
436 for (const auto& [species, coeff] : m_binaryParameters.at(name)) {
437 bin_a[species].setQuantity(coeff, "Pa*m^6/kmol^2");
438 }
439 eosNode["binary-a"] = std::move(bin_a);
440 }
441}
442
444{
445 double molarV = molarVolume();
446 double hh = m_b / molarV;
447 double zz = z();
448 double alpha_1 = daAlpha_dT();
449 double vpb = molarV + (1.0 + Sqrt2) * m_b;
450 double vmb = molarV + (1.0 - Sqrt2) * m_b;
451 double fac = alpha_1 / (2.0 * Sqrt2 * m_b);
452 double sresid_mol_R = log(zz*(1.0 - hh)) + fac * log(vpb / vmb) / GasConstant;
453 return GasConstant * sresid_mol_R;
454}
455
457{
458 double molarV = molarVolume();
459 double zz = z();
460 double aAlpha_1 = daAlpha_dT();
461 double T = temperature();
462 double vpb = molarV + (1 + Sqrt2) * m_b;
463 double vmb = molarV + (1 - Sqrt2) * m_b;
464 double fac = 1 / (2.0 * Sqrt2 * m_b);
465 return GasConstant * T * (zz - 1.0)
466 + fac * log(vpb / vmb) * (T * aAlpha_1 - m_aAlpha_mix);
467}
468
469double PengRobinson::liquidVolEst(double T, double& presGuess) const
470{
471 double v = m_b * 1.1;
472 double atmp, btmp, aAlphatmp;
473 calculateAB(atmp, btmp, aAlphatmp);
474 double pres = std::max(psatEst(T), presGuess);
475 double Vroot[3];
476 bool foundLiq = false;
477 int m = 0;
478 while (m < 100 && !foundLiq) {
479 int nsol = solveCubic(T, pres, atmp, btmp, aAlphatmp, Vroot);
480 if (nsol == 1 || nsol == 2) {
481 double pc = critPressure();
482 if (pres > pc) {
483 foundLiq = true;
484 }
485 pres *= 1.04;
486 } else {
487 foundLiq = true;
488 }
489 }
490
491 if (foundLiq) {
492 v = Vroot[0];
493 presGuess = pres;
494 } else {
495 v = -1.0;
496 }
497 return v;
498}
499
500double PengRobinson::densityCalc(double T, double presPa, int phaseRequested,
501 double rhoGuess)
502{
503 // It's necessary to set the temperature so that m_aAlpha_mix is set correctly.
505 double tcrit = critTemperature();
506 double mmw = meanMolecularWeight();
507 if (rhoGuess == -1.0) {
508 if (phaseRequested >= FLUID_LIQUID_0) {
509 double lqvol = liquidVolEst(T, presPa);
510 rhoGuess = mmw / lqvol;
511 }
512 } else {
513 // Assume the Gas phase initial guess, if nothing is specified to the routine
514 rhoGuess = presPa * mmw / (GasConstant * T);
515 }
516
517 double volGuess = mmw / rhoGuess;
518 m_NSolns = solveCubic(T, presPa, m_a, m_b, m_aAlpha_mix, m_Vroot);
519
520 double molarVolLast = m_Vroot[0];
521 if (m_NSolns >= 2) {
522 if (phaseRequested >= FLUID_LIQUID_0) {
523 molarVolLast = m_Vroot[0];
524 } else if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
525 molarVolLast = m_Vroot[2];
526 } else {
527 if (volGuess > m_Vroot[1]) {
528 molarVolLast = m_Vroot[2];
529 } else {
530 molarVolLast = m_Vroot[0];
531 }
532 }
533 } else if (m_NSolns == 1) {
534 if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT
535 || phaseRequested == FLUID_UNDEFINED)
536 {
537 molarVolLast = m_Vroot[0];
538 } else {
539 return -2.0;
540 }
541 } else if (m_NSolns == -1) {
542 if (phaseRequested >= FLUID_LIQUID_0 || phaseRequested == FLUID_UNDEFINED
543 || phaseRequested == FLUID_SUPERCRIT)
544 {
545 molarVolLast = m_Vroot[0];
546 } else if (T > tcrit) {
547 molarVolLast = m_Vroot[0];
548 } else {
549 return -2.0;
550 }
551 } else {
552 molarVolLast = m_Vroot[0];
553 return -1.0;
554 }
555 return mmw / molarVolLast;
556}
557
558double PengRobinson::dpdVCalc(double T, double molarVol, double& presCalc) const
559{
560 double denom = molarVol * molarVol + 2 * molarVol * m_b - m_b * m_b;
561 double vpb = molarVol + m_b;
562 double vmb = molarVol - m_b;
563 return -GasConstant * T / (vmb * vmb) + 2 * m_aAlpha_mix * vpb / (denom*denom);
564}
565
567{
569 return -1 / (molarVolume() * m_dpdV);
570}
571
573{
575 return -m_dpdT / (molarVolume() * m_dpdV);
576}
577
579{
581 return molarVolume() * sqrt(-cp_mole() / cv_mole() * m_dpdV / meanMolecularWeight());
582}
583
585{
586 double T = temperature();
587 double mv = molarVolume();
588 double pres;
589
590 m_dpdV = dpdVCalc(T, mv, pres);
591 double vmb = mv - m_b;
592 double denom = mv * mv + 2 * mv * m_b - m_b * m_b;
593 m_dpdT = (GasConstant / vmb - daAlpha_dT() / denom);
594}
595
597{
598 double temp = temperature();
599
600 // Update individual alpha
601 for (size_t j = 0; j < m_kk; j++) {
602 double critTemp_j = speciesCritTemperature(m_a_coeffs(j,j), m_b_coeffs[j]);
603 double sqt_alpha = 1 + m_kappa[j] * (1 - sqrt(temp / critTemp_j));
604 m_alpha[j] = sqt_alpha*sqt_alpha;
605 }
606
607 // Update aAlpha_i, j
608 for (size_t i = 0; i < m_kk; i++) {
609 for (size_t j = 0; j < m_kk; j++) {
610 m_aAlpha_binary(i, j) = sqrt(m_alpha[i] * m_alpha[j]) * m_a_coeffs(i,j);
611 }
612 }
614}
615
616void PengRobinson::calculateAB(double& aCalc, double& bCalc, double& aAlphaCalc) const
617{
618 bCalc = 0.0;
619 aCalc = 0.0;
620 aAlphaCalc = 0.0;
621 for (size_t i = 0; i < m_kk; i++) {
622 bCalc += moleFractions_[i] * m_b_coeffs[i];
623 for (size_t j = 0; j < m_kk; j++) {
624 aCalc += m_a_coeffs(i, j) * moleFractions_[i] * moleFractions_[j];
625 aAlphaCalc += m_aAlpha_binary(i, j) * moleFractions_[i] * moleFractions_[j];
626 }
627 }
628}
629
631{
632 double daAlphadT = 0.0, k, Tc, sqtTr, coeff1, coeff2;
633 for (size_t i = 0; i < m_kk; i++) {
634 // Calculate first derivative of alpha for individual species
635 Tc = speciesCritTemperature(m_a_coeffs(i,i), m_b_coeffs[i]);
636 sqtTr = sqrt(temperature() / Tc);
637 coeff1 = 1 / (Tc*sqtTr);
638 coeff2 = sqtTr - 1;
639 k = m_kappa[i];
640 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
641 }
642 // Calculate mixture derivative
643 for (size_t i = 0; i < m_kk; i++) {
644 for (size_t j = 0; j < m_kk; j++) {
645 daAlphadT += moleFractions_[i] * moleFractions_[j] * 0.5
646 * m_aAlpha_binary(i, j)
647 * (m_dalphadT[i] / m_alpha[i] + m_dalphadT[j] / m_alpha[j]);
648 }
649 }
650 return daAlphadT;
651}
652
654{
655 for (size_t i = 0; i < m_kk; i++) {
656 double Tcrit_i = speciesCritTemperature(m_a_coeffs(i, i), m_b_coeffs[i]);
657 double sqt_Tr = sqrt(temperature() / Tcrit_i);
658 double coeff1 = 1 / (Tcrit_i*sqt_Tr);
659 double coeff2 = sqt_Tr - 1;
660 // Calculate first and second derivatives of alpha for individual species
661 double k = m_kappa[i];
662 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
663 m_d2alphadT2[i] = (k*k + k) * coeff1 / (2*sqt_Tr*sqt_Tr*Tcrit_i);
664 }
665
666 // Calculate mixture derivative
667 double d2aAlphadT2 = 0.0;
668 for (size_t i = 0; i < m_kk; i++) {
669 double alphai = m_alpha[i];
670 for (size_t j = 0; j < m_kk; j++) {
671 double alphaj = m_alpha[j];
672 double alphaij = alphai * alphaj;
673 double term1 = m_d2alphadT2[i] / alphai + m_d2alphadT2[j] / alphaj;
674 double term2 = 2 * m_dalphadT[i] * m_dalphadT[j] / alphaij;
675 double term3 = m_dalphadT[i] / alphai + m_dalphadT[j] / alphaj;
676 d2aAlphadT2 += 0.5 * moleFractions_[i] * moleFractions_[j]
677 * m_aAlpha_binary(i, j)
678 * (term1 + term2 - 0.5 * term3 * term3);
679 }
680 }
681 return d2aAlphadT2;
682}
683
684void PengRobinson::calcCriticalConditions(double& pc, double& tc, double& vc) const
685{
686 if (m_b <= 0.0) {
687 tc = 1000000.;
688 pc = 1.0E13;
689 vc = omega_vc * GasConstant * tc / pc;
690 return;
691 }
692 if (m_a <= 0.0) {
693 tc = 0.0;
694 pc = 0.0;
695 vc = 2.0 * m_b;
696 return;
697 }
698 tc = m_a * omega_b / (m_b * omega_a * GasConstant);
699 pc = omega_b * GasConstant * tc / m_b;
700 vc = omega_vc * GasConstant * tc / pc;
701}
702
703int PengRobinson::solveCubic(double T, double pres, double a, double b, double aAlpha,
704 double Vroot[3]) const
705{
706 // Derive the coefficients of the cubic polynomial (in terms of molar volume v)
707 double bsqr = b * b;
708 double RT_p = GasConstant * T / pres;
709 double aAlpha_p = aAlpha / pres;
710 double an = 1.0;
711 double bn = (b - RT_p);
712 double cn = -(2 * RT_p * b - aAlpha_p + 3 * bsqr);
713 double dn = (bsqr * RT_p + bsqr * b - aAlpha_p * b);
714
715 double tc = a * omega_b / (b * omega_a * GasConstant);
716 double pc = omega_b * GasConstant * tc / b;
717 double vc = omega_vc * GasConstant * tc / pc;
718
719 return MixtureFugacityTP::solveCubic(T, pres, a, b, aAlpha, Vroot,
720 an, bn, cn, dn, tc, vc);
721}
722
724{
726
727 parameters["aAlpha_mix"] = m_aAlpha_mix;
728 parameters["b_mix"] = m_b;
729 parameters["daAlpha_dT"] = daAlpha_dT();
730 parameters["d2aAlpha_dT2"] = d2aAlpha_dT2();
731
732 return parameters;
733}
734
735}
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).
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.
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 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:914
size_t m_kk
Number of species in the phase.
Definition Phase.h:890
size_t speciesIndex(const string &name, bool raise=true) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:127
double temperature() const
Temperature (K).
Definition Phase.h:598
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition Phase.h:691
map< string, shared_ptr< Species > > m_species
Map of Species objects.
Definition Phase.h:903
double moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition Phase.cpp:464
double mean_X(const double *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition Phase.cpp:639
shared_ptr< Species > species(const string &name) const
Return the Species object for the named species.
Definition Phase.cpp:897
virtual double molarVolume() const
Molar volume (m^3/kmol).
Definition Phase.cpp:604
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...