11 #include <boost/math/tools/roots.hpp>
16 namespace bmt = boost::math::tools;
21 const doublereal RedlichKwongMFTP::omega_a = 4.27480233540E-01;
22 const doublereal RedlichKwongMFTP::omega_b = 8.66403499650E-02;
23 const doublereal RedlichKwongMFTP::omega_vc = 3.33333333333333E-01;
25 RedlichKwongMFTP::RedlichKwongMFTP() :
33 fill_n(Vroot_, 3, 0.0);
44 fill_n(Vroot_, 3, 0.0);
56 fill_n(Vroot_, 3, 0.0);
61 double a0,
double a1,
double b)
66 "Unknown species '{}'.",
species);
73 size_t counter = k +
m_kk * k;
74 a_coeff_vec(0, counter) = a0;
75 a_coeff_vec(1, counter) = a1;
78 for (
size_t j = 0; j <
m_kk; j++) {
84 if (isnan(a_coeff_vec(0, j +
m_kk * j))) {
87 }
else if (isnan(a_coeff_vec(0, j +
m_kk * k))) {
90 double a0kj = sqrt(a_coeff_vec(0, j +
m_kk * j) * a0);
91 double a1kj = sqrt(a_coeff_vec(1, j +
m_kk * j) * a1);
92 a_coeff_vec(0, j +
m_kk * k) = a0kj;
93 a_coeff_vec(1, j +
m_kk * k) = a1kj;
94 a_coeff_vec(0, k +
m_kk * j) = a0kj;
95 a_coeff_vec(1, k +
m_kk * j) = a1kj;
98 a_coeff_vec.
getRow(0, a_vec_Curr_.data());
103 const std::string& species_j,
double a0,
double a1)
108 "Unknown species '{}'.", species_i);
113 "Unknown species '{}'.", species_j);
119 size_t counter1 = ki +
m_kk * kj;
120 size_t counter2 = kj +
m_kk * ki;
121 a_coeff_vec(0, counter1) = a_coeff_vec(0, counter2) = a0;
122 a_coeff_vec(1, counter1) = a_coeff_vec(1, counter2) = a1;
123 a_vec_Curr_[counter1] = a_vec_Curr_[counter2] = a0;
132 doublereal h_nonideal =
hresid();
133 return h_ideal + h_nonideal;
141 doublereal sr_nonideal =
sresid();
142 return sr_ideal + sr_nonideal;
149 doublereal sqt = sqrt(TKelvin);
154 doublereal dadt = da_dt();
155 doublereal fac = TKelvin * dadt - 3.0 *
m_a_current / 2.0;
157 +1.0/(
m_b_current * sqt) * log(vpb/mv) * (-0.5 * dadt));
165 doublereal sqt = sqrt(TKelvin);
169 doublereal dadt = da_dt();
170 doublereal fac = TKelvin * dadt - 3.0 *
m_a_current / 2.0;
171 return (cvref - 1.0/(2.0 *
m_b_current * TKelvin * sqt) * log(vpb/mv)*fac
172 +1.0/(
m_b_current * sqt) * log(vpb/mv)*(-0.5*dadt));
214 for (
size_t k = 0; k <
m_kk; k++) {
232 for (
size_t k = 0; k <
m_kk; k++) {
234 for (
size_t i = 0; i <
m_kk; i++) {
235 size_t counter = k +
m_kk*i;
241 for (
size_t k = 0; k <
m_kk; k++) {
242 ac[k] = (-
RT() * log(pres * mv /
RT())
243 +
RT() * log(mv / vmb)
244 +
RT() * b_vec_Curr_[k] / vmb
250 for (
size_t k = 0; k <
m_kk; k++) {
251 ac[k] = exp(ac[k]/
RT());
260 for (
size_t k = 0; k <
m_kk; k++) {
261 muRT[k] *= 1.0 /
RT();
268 for (
size_t k = 0; k <
m_kk; k++) {
270 mu[k] +=
RT()*(log(xx));
278 for (
size_t k = 0; k <
m_kk; k++) {
280 for (
size_t i = 0; i <
m_kk; i++) {
281 size_t counter = k +
m_kk*i;
288 for (
size_t k = 0; k <
m_kk; k++) {
289 mu[k] += (
RT() * log(pres/refP) -
RT() * log(pres * mv /
RT())
290 +
RT() * log(mv / vmb)
291 +
RT() * b_vec_Curr_[k] / vmb
308 doublereal sqt = sqrt(TKelvin);
311 for (
size_t k = 0; k <
m_kk; k++) {
313 for (
size_t i = 0; i <
m_kk; i++) {
314 size_t counter = k +
m_kk*i;
318 for (
size_t k = 0; k <
m_kk; k++) {
319 dpdni_[k] =
RT()/vmb +
RT() * b_vec_Curr_[k] / (vmb * vmb) - 2.0 *
m_pp[k] / (sqt * mv * vpb)
320 +
m_a_current * b_vec_Curr_[k]/(sqt * mv * vpb * vpb);
322 doublereal dadt = da_dt();
323 doublereal fac = TKelvin * dadt - 3.0 *
m_a_current / 2.0;
325 for (
size_t k = 0; k <
m_kk; k++) {
327 for (
size_t i = 0; i <
m_kk; i++) {
328 size_t counter = k +
m_kk*i;
334 doublereal fac2 = mv + TKelvin *
dpdT_ /
dpdV_;
335 for (
size_t k = 0; k <
m_kk; k++) {
338 + b_vec_Curr_[k] / vpb / (
m_b_current * sqt) * fac);
339 hbar[k] = hbar[k] + hE_v;
340 hbar[k] -= fac2 *
dpdni_[k];
349 doublereal sqt = sqrt(TKelvin);
353 for (
size_t k = 0; k <
m_kk; k++) {
357 for (
size_t k = 0; k <
m_kk; k++) {
359 for (
size_t i = 0; i <
m_kk; i++) {
360 size_t counter = k +
m_kk*i;
364 for (
size_t k = 0; k <
m_kk; k++) {
366 for (
size_t i = 0; i <
m_kk; i++) {
367 size_t counter = k +
m_kk*i;
372 doublereal dadt = da_dt();
373 doublereal fac = dadt -
m_a_current / (2.0 * TKelvin);
376 for (
size_t k = 0; k <
m_kk; k++) {
384 - 1.0 / (
m_b_current * sqt) * b_vec_Curr_[k] / vpb * fac
390 for (
size_t k = 0; k <
m_kk; k++) {
391 sbar[k] -= -m_partialMolarVolumes[k] *
dpdT_;
409 for (
size_t k = 0; k <
m_kk; k++) {
411 for (
size_t i = 0; i <
m_kk; i++) {
412 size_t counter = k +
m_kk*i;
416 for (
size_t k = 0; k <
m_kk; k++) {
418 for (
size_t i = 0; i <
m_kk; i++) {
419 size_t counter = k +
m_kk*i;
428 for (
size_t k = 0; k <
m_kk; k++) {
431 - 2.0 *
m_pp[k] / (sqt * vpb)
436 vbar[k] = num / denom;
445 for (
size_t i = 0; i <
m_kk; i++) {
446 for (
size_t j = 0; j <
m_kk; j++) {
447 size_t counter = i +
m_kk * j;
461 for (
size_t i = 0; i <
m_kk; i++) {
462 for (
size_t j = 0; j <
m_kk; j++) {
463 size_t counter = i +
m_kk * j;
477 for (
size_t i = 0; i <
m_kk; i++) {
478 for (
size_t j = 0; j <
m_kk; j++) {
479 size_t counter = i +
m_kk * j;
493 for (
size_t i = 0; i <
m_kk; i++) {
494 for (
size_t j = 0; j <
m_kk; j++) {
495 size_t counter = i +
m_kk * j;
509 for (
size_t i = 0; i <
m_kk; i++) {
510 for (
size_t j = 0; j <
m_kk; j++) {
511 size_t counter = i +
m_kk * j;
525 a_vec_Curr_.resize(
m_kk *
m_kk, 0.0);
529 b_vec_Curr_.push_back(NAN);
534 m_partialMolarVolumes.push_back(0.0);
544 std::string model = thermoNode[
"model"];
545 if (model !=
"RedlichKwong" && model !=
"RedlichKwongMFTP") {
547 "Unknown thermo model : " + model);
553 a_coeff_vec.
data().assign(a_coeff_vec.
data().size(), NAN);
557 if (thermoNode.
hasChild(
"activityCoefficients")) {
564 for (
size_t i = 0; i < acNode.
nChildren(); i++) {
580 for (
size_t i = 0; i <
m_kk; i++) {
587 size_t counter = iSpecies +
m_kk * iSpecies;
590 if (isnan(a_coeff_vec(0, counter))) {
592 vector<double> coeffArray;
601 if (!isnan(coeffArray[0])) {
614 for (
auto& item : m_species) {
617 if (item.second->input.hasKey(
"equation-of-state")) {
618 auto eos = item.second->input[
"equation-of-state"].getMapWhere(
619 "model",
"Redlich-Kwong");
620 double a0 = 0, a1 = 0;
621 if (eos[
"a"].isScalar()) {
622 a0 = eos.convert(
"a",
"Pa*m^6/kmol^2*K^0.5");
624 auto avec = eos[
"a"].asVector<
AnyValue>(2);
625 a0 = eos.units().convert(avec[0],
"Pa*m^6/kmol^2*K^0.5");
626 a1 = eos.units().convert(avec[1],
"Pa*m^6/kmol^2/K^0.5");
628 double b = eos.convert(
"b",
"m^3/kmol");
630 if (eos.hasKey(
"binary-a")) {
633 for (
auto& item2 : binary_a) {
634 double a0 = 0, a1 = 0;
635 if (item2.second.isScalar()) {
636 a0 = units.
convert(item2.second,
"Pa*m^6/kmol^2*K^0.5");
638 auto avec = item2.second.asVector<
AnyValue>(2);
639 a0 = units.convert(avec[0],
"Pa*m^6/kmol^2*K^0.5");
640 a1 = units.convert(avec[1],
"Pa*m^6/kmol^2/K^0.5");
650 if (isnan(a_coeff_vec(0, k +
m_kk * k))) {
652 vector<double> coeffs =
getCoeff(item.first);
656 if (!isnan(coeffs[0])) {
677 for (
size_t isp = 0; isp < nDatabase; isp++) {
684 if (iNameLower == dbName) {
695 std::string critTemp = xmlChildCoeff.
attrib(
"value");
701 "Critical Temperature must be positive ");
706 "Critical Temperature not in database ");
719 "Critical Pressure must be positive ");
724 "Critical Pressure not in database ");
738 string xname = pureFluidParam.
name();
739 if (xname !=
"pureFluidParameters") {
740 throw CanteraError(
"RedlichKwongMFTP::readXMLPureFluid",
741 "Incorrect name for processing this routine: " + xname);
747 for (
size_t iChild = 0; iChild < pureFluidParam.
nChildren(); iChild++) {
751 if (nodeName ==
"a_coeff") {
754 getFloatArray(xmlChild, vParams,
true,
"Pascal-m6/kmol2",
"a_coeff");
756 if (iModel ==
"constant" && vParams.size() == 1) {
759 }
else if (iModel ==
"linear_a" && vParams.size() == 2) {
763 throw CanteraError(
"RedlichKwongMFTP::readXMLPureFluid",
764 "unknown model or incorrect number of parameters");
767 }
else if (nodeName ==
"b_coeff") {
776 string xname = CrossFluidParam.
name();
777 if (xname !=
"crossFluidParameters") {
778 throw CanteraError(
"RedlichKwongMFTP::readXMLCrossFluid",
779 "Incorrect name for processing this routine: " + xname);
782 string iName = CrossFluidParam.
attrib(
"species1");
783 string jName = CrossFluidParam.
attrib(
"species2");
785 size_t num = CrossFluidParam.
nChildren();
786 for (
size_t iChild = 0; iChild < num; iChild++) {
790 if (nodeName ==
"a_coeff") {
792 getFloatArray(xmlChild, vParams,
true,
"Pascal-m6/kmol2",
"a_coeff");
794 if (iModel ==
"constant" && vParams.size() == 1) {
796 }
else if (iModel ==
"linear_a") {
799 throw CanteraError(
"RedlichKwongMFTP::readXMLCrossFluid",
800 "unknown model ({}) or wrong number of parameters ({})",
801 iModel, vParams.size());
810 std::string model = thermoNode[
"model"];
818 doublereal molarV = mmw / rho;
821 doublereal dadt = da_dt();
823 doublereal sqT = sqrt(T);
834 doublereal molarV = mmw / rho;
837 doublereal dadt = da_dt();
839 doublereal sqT = sqrt(T);
840 doublereal fac = T * dadt - 3.0 *
m_a_current / (2.0);
850 doublereal pres = std::max(
psatEst(TKelvin), presGuess);
852 bool foundLiq =
false;
854 while (m < 100 && !foundLiq) {
855 int nsol =
NicholsSolve(TKelvin, pres, atmp, btmp, Vroot);
856 if (nsol == 1 || nsol == 2) {
882 if (rhoguess == -1.0) {
883 if (phaseRequested != FLUID_GAS) {
884 if (TKelvin > tcrit) {
887 if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
889 }
else if (phaseRequested >= FLUID_LIQUID_0) {
891 rhoguess = mmw / lqvol;
901 doublereal volguess = mmw / rhoguess;
904 doublereal molarVolLast = Vroot_[0];
906 if (phaseRequested >= FLUID_LIQUID_0) {
907 molarVolLast = Vroot_[0];
908 }
else if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
909 molarVolLast = Vroot_[2];
911 if (volguess > Vroot_[1]) {
912 molarVolLast = Vroot_[2];
914 molarVolLast = Vroot_[0];
917 }
else if (NSolns_ == 1) {
918 if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT || phaseRequested == FLUID_UNDEFINED) {
919 molarVolLast = Vroot_[0];
923 }
else if (NSolns_ == -1) {
924 if (phaseRequested >= FLUID_LIQUID_0 || phaseRequested == FLUID_UNDEFINED || phaseRequested == FLUID_SUPERCRIT) {
925 molarVolLast = Vroot_[0];
926 }
else if (TKelvin > tcrit) {
927 molarVolLast = Vroot_[0];
932 molarVolLast = Vroot_[0];
935 return mmw / molarVolLast;
947 auto resid = [
this, T](
double v) {
952 boost::uintmax_t maxiter = 100;
953 std::pair<double, double> vv = bmt::toms748_solve(
954 resid, Vroot[0], Vroot[1], bmt::eps_tolerance<double>(48), maxiter);
957 return mmw / (0.5 * (vv.first + vv.second));
969 auto resid = [
this, T](
double v) {
974 boost::uintmax_t maxiter = 100;
975 std::pair<double, double> vv = bmt::toms748_solve(
976 resid, Vroot[1], Vroot[2], bmt::eps_tolerance<double>(48), maxiter);
979 return mmw / (0.5 * (vv.first + vv.second));
984 doublereal sqt = sqrt(TKelvin);
992 doublereal sqt = sqrt(TKelvin);
998 doublereal dpdv = (-
GasConstant * TKelvin / (vmb * vmb)
1010 doublereal sqt = sqrt(TKelvin);
1013 doublereal dadt = da_dt();
1014 doublereal fac = dadt -
m_a_current/(2.0 * TKelvin);
1018 void RedlichKwongMFTP::updateMixingExpressions()
1027 for (
size_t i = 0; i <
m_kk; i++) {
1028 for (
size_t j = 0; j <
m_kk; j++) {
1029 size_t counter = i *
m_kk + j;
1030 a_vec_Curr_[counter] = a_coeff_vec(0,counter) + a_coeff_vec(1,counter) * temp;
1037 for (
size_t i = 0; i <
m_kk; i++) {
1039 for (
size_t j = 0; j <
m_kk; j++) {
1045 fmt::memory_buffer b;
1046 for (
size_t k = 0; k <
m_kk; k++) {
1047 if (isnan(b_vec_Curr_[k])) {
1056 "Missing Redlich-Kwong coefficients for species: {}", to_string(b));
1065 for (
size_t i = 0; i <
m_kk; i++) {
1067 for (
size_t j = 0; j <
m_kk; j++) {
1068 size_t counter = i *
m_kk + j;
1069 doublereal a_vec_Curr = a_coeff_vec(0,counter) + a_coeff_vec(1,counter) * temp;
1074 for (
size_t i = 0; i <
m_kk; i++) {
1076 for (
size_t j = 0; j <
m_kk; j++) {
1077 size_t counter = i *
m_kk + j;
1078 doublereal a_vec_Curr = a_coeff_vec(0,counter);
1085 doublereal RedlichKwongMFTP::da_dt()
const
1087 doublereal dadT = 0.0;
1089 for (
size_t i = 0; i <
m_kk; i++) {
1090 for (
size_t j = 0; j <
m_kk; j++) {
1091 size_t counter = i *
m_kk + j;
1099 void RedlichKwongMFTP::calcCriticalConditions(doublereal a, doublereal b, doublereal a0_coeff, doublereal aT_coeff,
1100 doublereal& pc, doublereal& tc, doublereal& vc)
const
1119 doublereal sqrttc, f, dfdt, deltatc;
1125 for (
int j = 0; j < 10; j++) {
1129 deltatc = - f / dfdt;
1132 if (deltatc > 0.1) {
1133 throw CanteraError(
"RedlichKwongMFTP::calcCriticalConditions",
"didn't converge");
1142 doublereal Vroot[3])
const
1147 if (TKelvin <= 0.0) {
1148 throw CanteraError(
"RedlichKwongMFTP::NicholsSolve",
"neg temperature");
1152 doublereal an = 1.0;
1154 doublereal sqt = sqrt(TKelvin);
1155 doublereal cn = - (
GasConstant * TKelvin * b / pres - a/(pres * sqt) + b * b);
1156 doublereal dn = - (a * b / (pres * sqt));
1160 double tc = pow(tmp, pp);
1164 doublereal xN = - bn /(3 * an);
1168 doublereal delta2 = (bn * bn - 3 * an * cn) / (9 * an * an);
1169 doublereal delta = 0.0;
1172 doublereal ratio1 = 3.0 * an * cn / (bn * bn);
1173 doublereal ratio2 = pres * b / (
GasConstant * TKelvin);
1174 if (fabs(ratio1) < 1.0E-7) {
1176 if (fabs(ratio2) < 1.0E-5 && fabs(ratio3) < 1.0E-5) {
1177 doublereal zz = 1.0;
1178 for (
int i = 0; i < 10; i++) {
1179 doublereal znew = zz / (zz - ratio2) - ratio3 / (zz + ratio1);
1180 doublereal deltaz = znew - zz;
1182 if (fabs(deltaz) < 1.0E-14) {
1193 double h2 = 4. * an * an * delta2 * delta2 * delta2;
1195 delta = sqrt(delta2);
1198 doublereal h = 2.0 * an * delta * delta2;
1199 doublereal yN = 2.0 * bn * bn * bn / (27.0 * an * an) - bn * cn / (3.0 * an) + dn;
1200 doublereal desc = yN * yN - h2;
1202 if (fabs(fabs(h) - fabs(yN)) < 1.0E-10) {
1205 throw CanteraError(
"RedlichKwongMFTP::NicholsSolve",
"numerical issues");
1212 }
else if (desc == 0.0) {
1215 }
else if (desc > 0.0) {
1221 doublereal tmpD = sqrt(desc);
1222 doublereal tmp1 = (- yN + tmpD) / (2.0 * an);
1223 doublereal sgn1 = 1.0;
1228 doublereal tmp2 = (- yN - tmpD) / (2.0 * an);
1229 doublereal sgn2 = 1.0;
1234 doublereal p1 = pow(tmp1, 1./3.);
1235 doublereal p2 = pow(tmp2, 1./3.);
1236 doublereal alpha = xN + sgn1 * p1 + sgn2 * p2;
1240 tmp = an * Vroot[0] * Vroot[0] * Vroot[0] + bn * Vroot[0] * Vroot[0] + cn * Vroot[0] + dn;
1241 }
else if (desc < 0.0) {
1242 doublereal tmp = - yN/h;
1243 doublereal val = acos(tmp);
1244 doublereal theta = val / 3.0;
1245 doublereal oo = 2. *
Pi / 3.;
1246 doublereal alpha = xN + 2. * delta * cos(theta);
1247 doublereal beta = xN + 2. * delta * cos(theta + oo);
1248 doublereal gamma = xN + 2. * delta * cos(theta + 2.0 * oo);
1253 for (
int i = 0; i < 3; i++) {
1254 tmp = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
1255 if (fabs(tmp) > 1.0E-4) {
1256 for (
int j = 0; j < 3; j++) {
1257 if (j != i && fabs(Vroot[i] - Vroot[j]) < 1.0E-4 * (fabs(Vroot[i]) + fabs(Vroot[j]))) {
1258 warn_user(
"RedlichKwongMFTP::NicholsSolve",
1259 "roots have merged: {}, {} (T = {}, p = {})",
1260 Vroot[i], Vroot[j], TKelvin, pres);
1265 }
else if (desc == 0.0) {
1266 if (yN == 0.0 && h == 0.0) {
1273 tmp = pow(yN/(2*an), 1./3.);
1274 if (fabs(tmp - delta) > 1.0E-9) {
1275 throw CanteraError(
"RedlichKwongMFTP::NicholsSolve",
"unexpected");
1277 Vroot[1] = xN + delta;
1278 Vroot[0] = xN - 2.0*delta;
1280 tmp = pow(yN/(2*an), 1./3.);
1281 if (fabs(tmp - delta) > 1.0E-9) {
1282 throw CanteraError(
"RedlichKwongMFTP::NicholsSolve",
"unexpected");
1285 Vroot[0] = xN + delta;
1286 Vroot[1] = xN - 2.0*delta;
1289 for (
int i = 0; i < 2; i++) {
1290 tmp = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
1296 double res, dresdV = 0.0;
1297 for (
int i = 0; i < nSolnValues; i++) {
1298 for (
int n = 0; n < 20; n++) {
1299 res = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
1300 if (fabs(res) < 1.0E-14) {
1303 dresdV = 3.0 * an * Vroot[i] * Vroot[i] + 2.0 * bn * Vroot[i] + cn;
1304 double del = - res / dresdV;
1306 if (fabs(del) / (fabs(Vroot[i]) + fabs(del)) < 1.0E-14) {
1309 double res2 = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
1310 if (fabs(res2) < fabs(res)) {
1314 Vroot[i] += 0.1 * del;
1317 if ((fabs(res) > 1.0E-14) && (fabs(res) > 1.0E-14 * fabs(dresdV) * fabs(Vroot[i]))) {
1318 warn_user(
"RedlichKwongMFTP::NicholsSolve",
1319 "root did not converge: V = {} (T = {}, p = {})",
1320 Vroot[i], TKelvin, pres);
1324 if (nSolnValues == 1) {
1326 if (Vroot[0] < vc) {
1330 if (Vroot[0] < xN) {
1335 if (nSolnValues == 2 && delta > 0.0) {