Cantera  3.2.0b1
Loading...
Searching...
No Matches
RedlichKwongMFTP.cpp
Go to the documentation of this file.
1//! @file RedlichKwongMFTP.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 RedlichKwongMFTP::omega_a = 4.27480233540E-01;
21const double RedlichKwongMFTP::omega_b = 8.66403499650E-02;
22const double RedlichKwongMFTP::omega_vc = 3.33333333333333E-01;
23
24RedlichKwongMFTP::RedlichKwongMFTP(const string& infile, const string& id_)
25{
26 initThermoFile(infile, id_);
27}
28
29void RedlichKwongMFTP::setSpeciesCoeffs(const string& species,
30 double a0, double a1, double b)
31{
32 size_t k = speciesIndex(species, true);
33
34 if (a1 != 0.0) {
35 m_formTempParam = 1; // expression is temperature-dependent
36 }
37
38 size_t counter = k + m_kk * k;
39 a_coeff_vec(0, counter) = a0;
40 a_coeff_vec(1, counter) = a1;
41
42 // standard mixing rule for cross-species interaction term
43 for (size_t j = 0; j < m_kk; j++) {
44 if (k == j) {
45 continue;
46 }
47
48 // a_coeff_vec(0) is initialized to NaN to mark uninitialized species
49 if (isnan(a_coeff_vec(0, j + m_kk * j))) {
50 // The diagonal element of the jth species has not yet been defined.
51 continue;
52 } else if (isnan(a_coeff_vec(0, j + m_kk * k))) {
53 // Only use the mixing rules if the off-diagonal element has not already been defined by a
54 // user-specified crossFluidParameters entry:
55 double a0kj = sqrt(a_coeff_vec(0, j + m_kk * j) * a0);
56 double a1kj = sqrt(a_coeff_vec(1, j + m_kk * j) * a1);
57 a_coeff_vec(0, j + m_kk * k) = a0kj;
58 a_coeff_vec(1, j + m_kk * k) = a1kj;
59 a_coeff_vec(0, k + m_kk * j) = a0kj;
60 a_coeff_vec(1, k + m_kk * j) = a1kj;
61 }
62 }
63 a_coeff_vec.getRow(0, a_vec_Curr_.data());
64 b_vec_Curr_[k] = b;
65}
66
67void RedlichKwongMFTP::setBinaryCoeffs(const string& species_i, const string& species_j,
68 double a0, double a1)
69{
70 size_t ki = speciesIndex(species_i, true);
71 size_t kj = speciesIndex(species_j, true);
72
73 if (a1 != 0.0) {
74 m_formTempParam = 1; // expression is temperature-dependent
75 }
76
77 m_binaryParameters[species_i][species_j] = {a0, a1};
78 m_binaryParameters[species_j][species_i] = {a0, a1};
79 size_t counter1 = ki + m_kk * kj;
80 size_t counter2 = kj + m_kk * ki;
81 a_coeff_vec(0, counter1) = a_coeff_vec(0, counter2) = a0;
82 a_coeff_vec(1, counter1) = a_coeff_vec(1, counter2) = a1;
83 a_vec_Curr_[counter1] = a_vec_Curr_[counter2] = a0;
84}
85
86// ------------Molar Thermodynamic Properties -------------------------
87
89{
91 double TKelvin = temperature();
92 double sqt = sqrt(TKelvin);
93 double mv = molarVolume();
94 double vpb = mv + m_b_current;
96 double cpref = GasConstant * mean_X(m_cp0_R);
97 double dadt = da_dt();
98 double fac = TKelvin * dadt - 3.0 * m_a_current / 2.0;
99 double dHdT_V = (cpref + mv * dpdT_ - GasConstant - 1.0 / (2.0 * m_b_current * TKelvin * sqt) * log(vpb/mv) * fac
100 +1.0/(m_b_current * sqt) * log(vpb/mv) * (-0.5 * dadt));
101 return dHdT_V - (mv + TKelvin * dpdT_ / dpdV_) * dpdT_;
102}
103
105{
107 double TKelvin = temperature();
108 double sqt = sqrt(TKelvin);
109 double mv = molarVolume();
110 double vpb = mv + m_b_current;
111 double cvref = GasConstant * (mean_X(m_cp0_R) - 1.0);
112 double dadt = da_dt();
113 double fac = TKelvin * dadt - 3.0 * m_a_current / 2.0;
114 return (cvref - 1.0/(2.0 * m_b_current * TKelvin * sqt) * log(vpb/mv)*fac
115 +1.0/(m_b_current * sqt) * log(vpb/mv)*(-0.5*dadt));
116}
117
119{
121
122 // Get a copy of the private variables stored in the State object
123 double T = temperature();
124 double molarV = meanMolecularWeight() / density();
125 double pp = GasConstant * T/(molarV - m_b_current) - m_a_current/(sqrt(T) * molarV * (molarV + m_b_current));
126 return pp;
127}
128
130{
132 return 1.0 / m_workS[k];
133}
134
136{
137 double mv = molarVolume();
138 double sqt = sqrt(temperature());
139 double vpb = mv + m_b_current;
140 double vmb = mv - m_b_current;
141
142 for (size_t k = 0; k < m_kk; k++) {
143 m_pp[k] = 0.0;
144 for (size_t i = 0; i < m_kk; i++) {
145 size_t counter = k + m_kk*i;
146 m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
147 }
148 }
149 double pres = pressure();
150
151 for (size_t k = 0; k < m_kk; k++) {
152 ac[k] = (- RT() * log(pres * mv / RT())
153 + RT() * log(mv / vmb)
154 + RT() * b_vec_Curr_[k] / vmb
155 - 2.0 * m_pp[k] / (m_b_current * sqt) * log(vpb/mv)
156 + m_a_current * b_vec_Curr_[k] / (m_b_current * m_b_current * sqt) * log(vpb/mv)
157 - m_a_current / (m_b_current * sqt) * (b_vec_Curr_[k]/vpb)
158 );
159 }
160 for (size_t k = 0; k < m_kk; k++) {
161 ac[k] = exp(ac[k]/RT());
162 }
163}
164
165// ---- Partial Molar Properties of the Solution -----------------
166
168{
169 getGibbs_ref(mu);
170 for (size_t k = 0; k < m_kk; k++) {
171 double xx = std::max(SmallNumber, moleFraction(k));
172 mu[k] += RT()*(log(xx));
173 }
174
175 double mv = molarVolume();
176 double sqt = sqrt(temperature());
177 double vpb = mv + m_b_current;
178 double vmb = mv - m_b_current;
179
180 for (size_t k = 0; k < m_kk; k++) {
181 m_pp[k] = 0.0;
182 for (size_t i = 0; i < m_kk; i++) {
183 size_t counter = k + m_kk*i;
184 m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
185 }
186 }
187 double pres = pressure();
188 double refP = refPressure();
189
190 for (size_t k = 0; k < m_kk; k++) {
191 mu[k] += (RT() * log(pres/refP) - RT() * log(pres * mv / RT())
192 + RT() * log(mv / vmb)
193 + RT() * b_vec_Curr_[k] / vmb
194 - 2.0 * m_pp[k] / (m_b_current * sqt) * log(vpb/mv)
195 + m_a_current * b_vec_Curr_[k] / (m_b_current * m_b_current * sqt) * log(vpb/mv)
196 - m_a_current / (m_b_current * sqt) * (b_vec_Curr_[k]/vpb)
197 );
198 }
199}
200
202{
203 // First we get the reference state contributions
204 getEnthalpy_RT_ref(hbar);
205 scale(hbar, hbar+m_kk, hbar, RT());
206
207 // We calculate dpdni_
208 double TKelvin = temperature();
209 double mv = molarVolume();
210 double sqt = sqrt(TKelvin);
211 double vpb = mv + m_b_current;
212 double vmb = mv - m_b_current;
213 for (size_t k = 0; k < m_kk; k++) {
214 m_pp[k] = 0.0;
215 for (size_t i = 0; i < m_kk; i++) {
216 size_t counter = k + m_kk*i;
217 m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
218 }
219 }
220 for (size_t k = 0; k < m_kk; k++) {
221 dpdni_[k] = RT()/vmb + RT() * b_vec_Curr_[k] / (vmb * vmb) - 2.0 * m_pp[k] / (sqt * mv * vpb)
222 + m_a_current * b_vec_Curr_[k]/(sqt * mv * vpb * vpb);
223 }
224 double dadt = da_dt();
225 double fac = TKelvin * dadt - 3.0 * m_a_current / 2.0;
226
227 for (size_t k = 0; k < m_kk; k++) {
228 m_workS[k] = 0.0;
229 for (size_t i = 0; i < m_kk; i++) {
230 size_t counter = k + m_kk*i;
231 m_workS[k] += 2.0 * moleFractions_[i] * TKelvin * a_coeff_vec(1,counter) - 3.0 * moleFractions_[i] * a_vec_Curr_[counter];
232 }
233 }
234
236 double fac2 = mv + TKelvin * dpdT_ / dpdV_;
237 for (size_t k = 0; k < m_kk; k++) {
238 double hE_v = (mv * dpdni_[k] - RT() - b_vec_Curr_[k]/ (m_b_current * m_b_current * sqt) * log(vpb/mv)*fac
239 + 1.0 / (m_b_current * sqt) * log(vpb/mv) * m_workS[k]
240 + b_vec_Curr_[k] / vpb / (m_b_current * sqt) * fac);
241 hbar[k] = hbar[k] + hE_v;
242 hbar[k] -= fac2 * dpdni_[k];
243 }
244}
245
247{
248 getEntropy_R_ref(sbar);
249 scale(sbar, sbar+m_kk, sbar, GasConstant);
250 double TKelvin = temperature();
251 double sqt = sqrt(TKelvin);
252 double mv = molarVolume();
253 double refP = refPressure();
254
255 for (size_t k = 0; k < m_kk; k++) {
256 double xx = std::max(SmallNumber, moleFraction(k));
257 sbar[k] += GasConstant * (- log(xx));
258 }
259 for (size_t k = 0; k < m_kk; k++) {
260 m_pp[k] = 0.0;
261 for (size_t i = 0; i < m_kk; i++) {
262 size_t counter = k + m_kk*i;
263 m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
264 }
265 }
266 for (size_t k = 0; k < m_kk; k++) {
267 m_workS[k] = 0.0;
268 for (size_t i = 0; i < m_kk; i++) {
269 size_t counter = k + m_kk*i;
270 m_workS[k] += moleFractions_[i] * a_coeff_vec(1,counter);
271 }
272 }
273
274 double dadt = da_dt();
275 double fac = dadt - m_a_current / (2.0 * TKelvin);
276 double vmb = mv - m_b_current;
277 double vpb = mv + m_b_current;
278 for (size_t k = 0; k < m_kk; k++) {
279 sbar[k] -=(GasConstant * log(GasConstant * TKelvin / (refP * mv))
281 + GasConstant * log(mv/vmb)
282 + GasConstant * b_vec_Curr_[k]/vmb
283 + m_pp[k]/(m_b_current * TKelvin * sqt) * log(vpb/mv)
284 - 2.0 * m_workS[k]/(m_b_current * sqt) * log(vpb/mv)
285 + b_vec_Curr_[k] / (m_b_current * m_b_current * sqt) * log(vpb/mv) * fac
286 - 1.0 / (m_b_current * sqt) * b_vec_Curr_[k] / vpb * fac
287 );
288 }
289
291 getPartialMolarVolumes(m_partialMolarVolumes.data());
292 for (size_t k = 0; k < m_kk; k++) {
293 sbar[k] -= -m_partialMolarVolumes[k] * dpdT_;
294 }
295}
296
298{
299 // u_k = h_k - P * v_k
300 getPartialMolarVolumes(m_partialMolarVolumes.data());
302 double p = pressure();
303 for (size_t k = 0; k < nSpecies(); k++) {
304 ubar[k] -= p * m_partialMolarVolumes[k];
305 }
306}
307
309{
310 for (size_t k = 0; k < m_kk; k++) {
311 m_pp[k] = 0.0;
312 for (size_t i = 0; i < m_kk; i++) {
313 size_t counter = k + m_kk*i;
314 m_pp[k] += moleFractions_[i] * a_vec_Curr_[counter];
315 }
316 }
317 for (size_t k = 0; k < m_kk; k++) {
318 m_workS[k] = 0.0;
319 for (size_t i = 0; i < m_kk; i++) {
320 size_t counter = k + m_kk*i;
321 m_workS[k] += moleFractions_[i] * a_coeff_vec(1,counter);
322 }
323 }
324
325 double sqt = sqrt(temperature());
326 double mv = molarVolume();
327 double vmb = mv - m_b_current;
328 double vpb = mv + m_b_current;
329 for (size_t k = 0; k < m_kk; k++) {
330 double num = (RT() + RT() * m_b_current/ vmb + RT() * b_vec_Curr_[k] / vmb
331 + RT() * m_b_current * b_vec_Curr_[k] /(vmb * vmb)
332 - 2.0 * m_pp[k] / (sqt * vpb)
333 + m_a_current * b_vec_Curr_[k] / (sqt * vpb * vpb)
334 );
335 double denom = (pressure() + RT() * m_b_current/(vmb * vmb) - m_a_current / (sqt * vpb * vpb)
336 );
337 vbar[k] = num / denom;
338 }
339}
340
341bool RedlichKwongMFTP::addSpecies(shared_ptr<Species> spec)
342{
343 bool added = MixtureFugacityTP::addSpecies(spec);
344 if (added) {
345 a_vec_Curr_.resize(m_kk * m_kk, 0.0);
346
347 // Initialize a_vec and b_vec to NaN, to screen for species with
348 // pureFluidParameters which are undefined in the input file:
349 b_vec_Curr_.push_back(NAN);
350 a_coeff_vec.resize(2, m_kk * m_kk, NAN);
351
352 m_pp.push_back(0.0);
353 m_coeffSource.push_back(CoeffSource::EoS);
354 m_partialMolarVolumes.push_back(0.0);
355 dpdni_.push_back(0.0);
356 }
357 return added;
358}
359
361{
362 // Contents of 'critical-properties.yaml', loaded later if needed
363 AnyMap critPropsDb;
364 std::unordered_map<string, AnyMap*> dbSpecies;
365
366 for (auto& [name, species] : m_species) {
367 auto& data = species->input;
368 size_t k = speciesIndex(name, true);
369 if (!isnan(a_coeff_vec(0, k + m_kk * k))) {
370 continue;
371 }
372 bool foundCoeffs = false;
373
374 if (data.hasKey("equation-of-state") &&
375 data["equation-of-state"].hasMapWhere("model", "Redlich-Kwong"))
376 {
377 // Read a and b coefficients from species 'input' information (that is, as
378 // specified in a YAML input file) specific to the Redlich-Kwong model
379 auto eos = data["equation-of-state"].getMapWhere(
380 "model", "Redlich-Kwong");
381
382 if (eos.hasKey("a") && eos.hasKey("b")) {
383 double a0 = 0, a1 = 0;
384 if (eos["a"].isScalar()) {
385 a0 = eos.convert("a", "Pa*m^6/kmol^2*K^0.5");
386 } else {
387 auto avec = eos["a"].asVector<AnyValue>(2);
388 a0 = eos.units().convert(avec[0], "Pa*m^6/kmol^2*K^0.5");
389 a1 = eos.units().convert(avec[1], "Pa*m^6/kmol^2/K^0.5");
390 }
391 double b = eos.convert("b", "m^3/kmol");
392 foundCoeffs = true;
393 setSpeciesCoeffs(name, a0, a1, b);
394 m_coeffSource[k] = CoeffSource::EoS;
395 }
396
397 if (eos.hasKey("binary-a")) {
398 AnyMap& binary_a = eos["binary-a"].as<AnyMap>();
399 const UnitSystem& units = binary_a.units();
400 for (auto& [name2, coeff] : binary_a) {
401 double a0 = 0, a1 = 0;
402 if (coeff.isScalar()) {
403 a0 = units.convert(coeff, "Pa*m^6/kmol^2*K^0.5");
404 } else {
405 auto avec = coeff.asVector<AnyValue>(2);
406 a0 = units.convert(avec[0], "Pa*m^6/kmol^2*K^0.5");
407 a1 = units.convert(avec[1], "Pa*m^6/kmol^2/K^0.5");
408 }
409 setBinaryCoeffs(name, name2, a0, a1);
410 }
411 }
412
413 if (foundCoeffs) {
414 continue;
415 }
416 }
417
418 // Coefficients have not been populated from model-specific input
419 double Tc = NAN, Pc = NAN;
420 if (data.hasKey("critical-parameters")) {
421 // Use critical state information stored in the species entry to
422 // calculate a and b
423 auto& critProps = data["critical-parameters"].as<AnyMap>();
424 Tc = critProps.convert("critical-temperature", "K");
425 Pc = critProps.convert("critical-pressure", "Pa");
426 m_coeffSource[k] = CoeffSource::CritProps;
427 } else {
428 // Search 'crit-properties.yaml' to find Tc and Pc. Load data if needed
429 if (critPropsDb.empty()) {
430 critPropsDb = AnyMap::fromYamlFile("critical-properties.yaml");
431 dbSpecies = critPropsDb["species"].asMap("name");
432 }
433
434 // All names in critical-properties.yaml are upper case
435 auto ucName = boost::algorithm::to_upper_copy(name);
436 if (dbSpecies.count(ucName)) {
437 auto& spec = *dbSpecies.at(ucName);
438 auto& critProps = spec["critical-parameters"].as<AnyMap>();
439 Tc = critProps.convert("critical-temperature", "K");
440 Pc = critProps.convert("critical-pressure", "Pa");
441 m_coeffSource[k] = CoeffSource::Database;
442 }
443 }
444
445 // Check if critical properties were found in either location
446 if (!isnan(Tc)) {
447 // Assuming no temperature dependence (that is, a1 = 0)
448 double a = omega_a * pow(GasConstant, 2) * pow(Tc, 2.5) / Pc;
449 double b = omega_b * GasConstant * Tc / Pc;
450 setSpeciesCoeffs(name, a, 0.0, b);
451 } else {
452 throw InputFileError("RedlichKwongMFTP::initThermo", data,
453 "No critical property or Redlich-Kwong parameters found "
454 "for species {}.", name);
455 }
456 }
457}
458
460 AnyMap& speciesNode) const
461{
463 size_t k = speciesIndex(name, true);
464 if (m_coeffSource[k] == CoeffSource::EoS) {
465 auto& eosNode = speciesNode["equation-of-state"].getMapWhere(
466 "model", "Redlich-Kwong", true);
467
468 size_t counter = k + m_kk * k;
469 if (a_coeff_vec(1, counter) != 0.0) {
470 vector<AnyValue> coeffs(2);
471 coeffs[0].setQuantity(a_coeff_vec(0, counter), "Pa*m^6/kmol^2*K^0.5");
472 coeffs[1].setQuantity(a_coeff_vec(1, counter), "Pa*m^6/kmol^2/K^0.5");
473 eosNode["a"] = std::move(coeffs);
474 } else {
475 eosNode["a"].setQuantity(a_coeff_vec(0, counter),
476 "Pa*m^6/kmol^2*K^0.5");
477 }
478 eosNode["b"].setQuantity(b_vec_Curr_[k], "m^3/kmol");
479 } else if (m_coeffSource[k] == CoeffSource::CritProps) {
480 auto& critProps = speciesNode["critical-parameters"];
481 double a = a_coeff_vec(0, k + m_kk * k);
482 double b = b_vec_Curr_[k];
483 double Tc = pow(a * omega_b / (b * omega_a * GasConstant), 2.0/3.0);
484 double Pc = omega_b * GasConstant * Tc / b;
485 critProps["critical-temperature"].setQuantity(Tc, "K");
486 critProps["critical-pressure"].setQuantity(Pc, "Pa");
487 }
488
489 if (m_binaryParameters.count(name)) {
490 auto& eosNode = speciesNode["equation-of-state"].getMapWhere(
491 "model", "Redlich-Kwong", true);
492 AnyMap bin_a;
493 for (const auto& [name2, coeffs] : m_binaryParameters.at(name)) {
494 if (coeffs.second == 0) {
495 bin_a[name2].setQuantity(coeffs.first, "Pa*m^6/kmol^2*K^0.5");
496 } else {
497 vector<AnyValue> C(2);
498 C[0].setQuantity(coeffs.first, "Pa*m^6/kmol^2*K^0.5");
499 C[1].setQuantity(coeffs.second, "Pa*m^6/kmol^2/K^0.5");
500 bin_a[name2] = std::move(C);
501 }
502 }
503 eosNode["binary-a"] = std::move(bin_a);
504 }
505}
506
508{
509 // note this agrees with tpx
510 double rho = density();
511 double mmw = meanMolecularWeight();
512 double molarV = mmw / rho;
513 double hh = m_b_current / molarV;
514 double zz = z();
515 double dadt = da_dt();
516 double T = temperature();
517 double sqT = sqrt(T);
518 double fac = dadt - m_a_current / (2.0 * T);
519 double sresid_mol_R = log(zz*(1.0 - hh)) + log(1.0 + hh) * fac / (sqT * GasConstant * m_b_current);
520 return GasConstant * sresid_mol_R;
521}
522
524{
525 // note this agrees with tpx
526 double rho = density();
527 double mmw = meanMolecularWeight();
528 double molarV = mmw / rho;
529 double hh = m_b_current / molarV;
530 double zz = z();
531 double dadt = da_dt();
532 double T = temperature();
533 double sqT = sqrt(T);
534 double fac = T * dadt - 3.0 *m_a_current / (2.0);
535 return GasConstant * T * (zz - 1.0) + fac * log(1.0 + hh) / (sqT * m_b_current);
536}
537
538double RedlichKwongMFTP::liquidVolEst(double TKelvin, double& presGuess) const
539{
540 double v = m_b_current * 1.1;
541 double atmp;
542 double btmp;
543 calculateAB(TKelvin, atmp, btmp);
544 double pres = std::max(psatEst(TKelvin), presGuess);
545 double Vroot[3];
546 bool foundLiq = false;
547 int m = 0;
548 while (m < 100 && !foundLiq) {
549 int nsol = solveCubic(TKelvin, pres, atmp, btmp, Vroot);
550 if (nsol == 1 || nsol == 2) {
551 double pc = critPressure();
552 if (pres > pc) {
553 foundLiq = true;
554 }
555 pres *= 1.04;
556 } else {
557 foundLiq = true;
558 }
559 }
560
561 if (foundLiq) {
562 v = Vroot[0];
563 presGuess = pres;
564 } else {
565 v = -1.0;
566 }
567 return v;
568}
569
570double RedlichKwongMFTP::densityCalc(double TKelvin, double presPa, int phaseRequested,
571 double rhoguess)
572{
573 // It's necessary to set the temperature so that m_a_current is set correctly.
574 setTemperature(TKelvin);
575 double tcrit = critTemperature();
576 double mmw = meanMolecularWeight();
577 if (rhoguess == -1.0) {
578 if (phaseRequested >= FLUID_LIQUID_0) {
579 double lqvol = liquidVolEst(TKelvin, presPa);
580 rhoguess = mmw / lqvol;
581 }
582 } else {
583 // Assume the Gas phase initial guess, if nothing is specified to the routine
584 rhoguess = presPa * mmw / (GasConstant * TKelvin);
585 }
586
587
588 double volguess = mmw / rhoguess;
589 NSolns_ = solveCubic(TKelvin, presPa, m_a_current, m_b_current, Vroot_);
590
591 double molarVolLast = Vroot_[0];
592 if (NSolns_ >= 2) {
593 if (phaseRequested >= FLUID_LIQUID_0) {
594 molarVolLast = Vroot_[0];
595 } else if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
596 molarVolLast = Vroot_[2];
597 } else {
598 if (volguess > Vroot_[1]) {
599 molarVolLast = Vroot_[2];
600 } else {
601 molarVolLast = Vroot_[0];
602 }
603 }
604 } else if (NSolns_ == 1) {
605 if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT || phaseRequested == FLUID_UNDEFINED) {
606 molarVolLast = Vroot_[0];
607 } else {
608 return -2.0;
609 }
610 } else if (NSolns_ == -1) {
611 if (phaseRequested >= FLUID_LIQUID_0 || phaseRequested == FLUID_UNDEFINED || phaseRequested == FLUID_SUPERCRIT) {
612 molarVolLast = Vroot_[0];
613 } else if (TKelvin > tcrit) {
614 molarVolLast = Vroot_[0];
615 } else {
616 return -2.0;
617 }
618 } else {
619 molarVolLast = Vroot_[0];
620 return -1.0;
621 }
622 return mmw / molarVolLast;
623}
624
626{
627 double Vroot[3];
628 double T = temperature();
629 int nsol = solveCubic(T, pressure(), m_a_current, m_b_current, Vroot);
630 if (nsol != 3) {
631 return critDensity();
632 }
633
634 auto resid = [this, T](double v) {
635 double pp;
636 return dpdVCalc(T, v, pp);
637 };
638
639 boost::uintmax_t maxiter = 100;
640 auto [lower, upper] = bmt::toms748_solve(
641 resid, Vroot[0], Vroot[1], bmt::eps_tolerance<double>(48), maxiter);
642
643 double mmw = meanMolecularWeight();
644 return mmw / (0.5 * (lower + upper));
645}
646
648{
649 double Vroot[3];
650 double T = temperature();
651 int nsol = solveCubic(T, pressure(), m_a_current, m_b_current, Vroot);
652 if (nsol != 3) {
653 return critDensity();
654 }
655
656 auto resid = [this, T](double v) {
657 double pp;
658 return dpdVCalc(T, v, pp);
659 };
660
661 boost::uintmax_t maxiter = 100;
662 auto [lower, upper] = bmt::toms748_solve(
663 resid, Vroot[1], Vroot[2], bmt::eps_tolerance<double>(48), maxiter);
664
665 double mmw = meanMolecularWeight();
666 return mmw / (0.5 * (lower + upper));
667}
668
669double RedlichKwongMFTP::dpdVCalc(double TKelvin, double molarVol, double& presCalc) const
670{
671 double sqt = sqrt(TKelvin);
672 presCalc = GasConstant * TKelvin / (molarVol - m_b_current)
673 - m_a_current / (sqt * molarVol * (molarVol + m_b_current));
674
675 double vpb = molarVol + m_b_current;
676 double vmb = molarVol - m_b_current;
677 double dpdv = (- GasConstant * TKelvin / (vmb * vmb)
678 + m_a_current * (2 * molarVol + m_b_current) / (sqt * molarVol * molarVol * vpb * vpb));
679 return dpdv;
680}
681
683{
685 return -1 / (molarVolume() * dpdV_);
686}
687
689{
691 return -dpdT_ / (molarVolume() * dpdV_);
692}
693
695{
697 return molarVolume() * sqrt(-cp_mole() / cv_mole() * dpdV_ / meanMolecularWeight());
698}
699
701{
702 double TKelvin = temperature();
703 double mv = molarVolume();
704 double pres;
705
706 dpdV_ = dpdVCalc(TKelvin, mv, pres);
707 double sqt = sqrt(TKelvin);
708 double vpb = mv + m_b_current;
709 double vmb = mv - m_b_current;
710 double dadt = da_dt();
711 double fac = dadt - m_a_current/(2.0 * TKelvin);
712 dpdT_ = (GasConstant / vmb - fac / (sqt * mv * vpb));
713}
714
716{
717 double temp = temperature();
718 if (m_formTempParam == 1) {
719 for (size_t i = 0; i < m_kk; i++) {
720 for (size_t j = 0; j < m_kk; j++) {
721 size_t counter = i * m_kk + j;
722 a_vec_Curr_[counter] = a_coeff_vec(0,counter) + a_coeff_vec(1,counter) * temp;
723 }
724 }
725 }
726
727 m_b_current = 0.0;
728 m_a_current = 0.0;
729 for (size_t i = 0; i < m_kk; i++) {
730 m_b_current += moleFractions_[i] * b_vec_Curr_[i];
731 for (size_t j = 0; j < m_kk; j++) {
732 m_a_current += a_vec_Curr_[i * m_kk + j] * moleFractions_[i] * moleFractions_[j];
733 }
734 }
735 if (isnan(m_b_current)) {
736 // One or more species do not have specified coefficients.
737 fmt::memory_buffer b;
738 for (size_t k = 0; k < m_kk; k++) {
739 if (isnan(b_vec_Curr_[k])) {
740 if (b.size() > 0) {
741 fmt_append(b, ", {}", speciesName(k));
742 } else {
743 fmt_append(b, "{}", speciesName(k));
744 }
745 }
746 }
747 throw CanteraError("RedlichKwongMFTP::updateMixingExpressions",
748 "Missing Redlich-Kwong coefficients for species: {}", to_string(b));
749 }
750}
751
752void RedlichKwongMFTP::calculateAB(double temp, double& aCalc, double& bCalc) const
753{
754 bCalc = 0.0;
755 aCalc = 0.0;
756 if (m_formTempParam == 1) {
757 for (size_t i = 0; i < m_kk; i++) {
758 bCalc += moleFractions_[i] * b_vec_Curr_[i];
759 for (size_t j = 0; j < m_kk; j++) {
760 size_t counter = i * m_kk + j;
761 double a_vec_Curr = a_coeff_vec(0,counter) + a_coeff_vec(1,counter) * temp;
762 aCalc += a_vec_Curr * moleFractions_[i] * moleFractions_[j];
763 }
764 }
765 } else {
766 for (size_t i = 0; i < m_kk; i++) {
767 bCalc += moleFractions_[i] * b_vec_Curr_[i];
768 for (size_t j = 0; j < m_kk; j++) {
769 size_t counter = i * m_kk + j;
770 double a_vec_Curr = a_coeff_vec(0,counter);
771 aCalc += a_vec_Curr * moleFractions_[i] * moleFractions_[j];
772 }
773 }
774 }
775}
776
777double RedlichKwongMFTP::da_dt() const
778{
779 double dadT = 0.0;
780 if (m_formTempParam == 1) {
781 for (size_t i = 0; i < m_kk; i++) {
782 for (size_t j = 0; j < m_kk; j++) {
783 size_t counter = i * m_kk + j;
784 dadT+= a_coeff_vec(1,counter) * moleFractions_[i] * moleFractions_[j];
785 }
786 }
787 }
788 return dadT;
789}
790
791void RedlichKwongMFTP::calcCriticalConditions(double& pc, double& tc, double& vc) const
792{
793 double a0 = 0.0;
794 double aT = 0.0;
795 for (size_t i = 0; i < m_kk; i++) {
796 for (size_t j = 0; j <m_kk; j++) {
797 size_t counter = i + m_kk * j;
798 a0 += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(0, counter);
799 aT += moleFractions_[i] * moleFractions_[j] * a_coeff_vec(1, counter);
800 }
801 }
802 double a = m_a_current;
803 double b = m_b_current;
804 if (m_formTempParam != 0) {
805 a = a0;
806 }
807 if (b <= 0.0) {
808 tc = 1000000.;
809 pc = 1.0E13;
810 vc = omega_vc * GasConstant * tc / pc;
811 return;
812 }
813 if (a <= 0.0) {
814 tc = 0.0;
815 pc = 0.0;
816 vc = 2.0 * b;
817 return;
818 }
819 double tmp = a * omega_b / (b * omega_a * GasConstant);
820 double pp = 2./3.;
821 double sqrttc, f, dfdt, deltatc;
822
823 if (m_formTempParam == 0) {
824 tc = pow(tmp, pp);
825 } else {
826 tc = pow(tmp, pp);
827 for (int j = 0; j < 10; j++) {
828 sqrttc = sqrt(tc);
829 f = omega_a * b * GasConstant * tc * sqrttc / omega_b - aT * tc - a0;
830 dfdt = 1.5 * omega_a * b * GasConstant * sqrttc / omega_b - aT;
831 deltatc = - f / dfdt;
832 tc += deltatc;
833 }
834 if (deltatc > 0.1) {
835 throw CanteraError("RedlichKwongMFTP::calcCriticalConditions",
836 "didn't converge");
837 }
838 }
839
840 pc = omega_b * GasConstant * tc / b;
841 vc = omega_vc * GasConstant * tc / pc;
842}
843
844int RedlichKwongMFTP::solveCubic(double T, double pres, double a, double b, double Vroot[3]) const
845{
846
847 // Derive the coefficients of the cubic polynomial to solve.
848 double an = 1.0;
849 double bn = - GasConstant * T / pres;
850 double sqt = sqrt(T);
851 double cn = - (GasConstant * T * b / pres - a/(pres * sqt) + b * b);
852 double dn = - (a * b / (pres * sqt));
853
854 double tmp = a * omega_b / (b * omega_a * GasConstant);
855 double pp = 2./3.;
856 double tc = pow(tmp, pp);
857 double pc = omega_b * GasConstant * tc / b;
858 double vc = omega_vc * GasConstant * tc / pc;
859
860 int nSolnValues = MixtureFugacityTP::solveCubic(T, pres, a, b, a, Vroot, an, bn, cn, dn, tc, vc);
861
862 return nSolnValues;
863}
864
865}
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
A wrapper for a variable whose type is determined at runtime.
Definition AnyMap.h:88
void getRow(size_t n, double *const rw)
Get the nth row and return it in a vector.
Definition Array.cpp:79
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
Base class for exceptions thrown by Cantera classes.
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 getEntropy_R_ref(double *er) const override
Returns the vector of nondimensional entropies of the reference state at the current temperature of t...
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 ...
vector< double > m_workS
Vector of size m_kk, used as a temporary holding area.
Definition Phase.h:945
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:270
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
string speciesName(size_t k) const
Name of the species with index k.
Definition Phase.cpp:174
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
virtual double density() const
Density (kg/m^3).
Definition Phase.h:654
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 dpdT_
The derivative of the pressure wrt the temperature.
void calculateAB(double temp, double &aCalc, double &bCalc) const
Calculate the a and the b parameters given the temperature.
double thermalExpansionCoeff() const override
Return the volumetric thermal expansion coefficient. Units: 1/K.
void setBinaryCoeffs(const string &species_i, const string &species_j, double a0, double a1)
Set values for the interaction parameter between two species.
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.
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).
int m_formTempParam
Form of the temperature parameterization.
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...
RedlichKwongMFTP(const string &infile="", const string &id="")
Construct a RedlichKwongMFTP object from an input file.
static const double omega_b
Omega constant for b.
vector< double > m_pp
Temporary storage - length = m_kk.
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...
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].
void pressureDerivatives() const
Calculate dpdV and dpdT 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.
static const double omega_a
Omega constant for a -> value of a in terms of critical properties.
double liquidVolEst(double TKelvin, double &pres) const override
Estimate for the molar volume of the liquid.
double dpdVCalc(double TKelvin, double molarVol, double &presCalc) const override
Calculate the pressure and the pressure derivative given the temperature and the molar volume.
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 and b coefficients.
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 a and b parameters.
double cp_mole() const override
Molar heat capacity at constant pressure and composition [J/kmol/K].
double m_a_current
Value of a in the equation of state.
double standardConcentration(size_t k=0) const override
Returns the standard concentration , which is used to normalize the generalized concentration.
double dpdV_
The derivative of the pressure wrt the volume.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
double m_b_current
Value of b in the equation of state.
void getActivityCoefficients(double *ac) const override
Get the array of non-dimensional activity coefficients at the current solution temperature,...
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.
vector< double > dpdni_
Vector of derivatives of pressure wrt mole number.
void getPartialMolarEntropies(double *sbar) const override
Returns an array of partial molar entropies of the species in the solution.
map< string, map< string, pair< double, double > > > m_binaryParameters
Explicitly-specified binary interaction parameters.
void setSpeciesCoeffs(const string &species, double a0, double a1, double b)
Set the pure fluid interaction parameters for a species.
int solveCubic(double T, double pres, double a, double b, double Vroot[3]) const
Prepare variables and call the function to solve the cubic equation of state.
double RT() const
Return the Gas Constant multiplied by the current temperature.
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
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...