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