12#include <boost/algorithm/string.hpp>
28 double a0,
double a1,
double b)
40 for (
size_t j = 0; j <
m_kk; j++) {
46 if (isnan(
m_a0(j, j))) {
49 }
else if (isnan(
m_a0(j, k))) {
74 m_a(ki, kj) =
m_a(kj, ki) = a0;
91 double dadt = da_dt();
92 double fac = T * dadt - 3.0 *
m_aMix / 2.0;
93 double dHdT_V = cpref - 0.5 * dadt * log(vpb/mv) / (
m_bMix * sqt)
106 double sqt = sqrt(T);
109 double dadt = da_dt();
110 double fac = T * dadt - 3.0 *
m_aMix / 2.0;
111 return (cvref - 1.0/(2.0 *
m_bMix * T * sqt) * log(vpb/mv)*fac
112 +1.0/(
m_bMix * sqt) * log(vpb/mv)*(-0.5*dadt));
136 for (
size_t k = 0; k <
m_kk; k++) {
146 double logv = log(vpb / mv);
151 MappedVector(ac.data(),
m_kk) = (g /
RT()).exp();
159 Eigen::Map<Eigen::ArrayXd> muVec(mu.data(),
m_kk);
170 double logv = log(vpb / mv);
182 scale(hbar.begin(), hbar.end(), hbar.begin(),
RT());
190 double sqt = sqrt(T);
194 - 2.0 *
m_Ak / (sqt * mv * vpb)
196 double dadt = da_dt();
197 double fac = T * dadt - 3.0 *
m_aMix / 2.0;
198 Eigen::ArrayXd Sk = 2.0 * T *
m_dAkdT - 3.0 *
m_Ak;
202 double logv = log(vpb / mv);
203 Eigen::ArrayXd hE_v = mv *
m_dpdni
206 + (1.0 / (
m_bMix * sqt) * logv) * Sk
208 Eigen::Map<Eigen::ArrayXd>(hbar.data(),
m_kk) += hE_v - fac2 *
m_dpdni;
215 double sqt = sqrt(T);
219 MappedVector sbarVec(sbar.data(),
m_kk);
226 double fac = da_dt() -
m_aMix / (2.0 * T);
229 double logv = log(vpb / mv);
230 Eigen::ArrayXd sdep =
GasConstant * (log(
RT() / (pres * vmb)) + 1)
236 sbarVec -= sdep.matrix();
249 for (
size_t k = 0; k <
nSpecies(); k++) {
250 ubar[k] -= p * m_partialMolarVolumes[k];
256 checkArraySize(
"RedlichKwongMFTP::getPartialMolarIntEnergies_TV", utilde.size(),
m_kk);
259 Eigen::Map<Eigen::ArrayXd> utildeVec(utilde.data(),
m_kk);
269 double logv = log(vpb / mv);
270 double F = T * da_dt() - 1.5 *
m_aMix;
271 double C = -logv /
m_bMix + 1.0 / vpb;
272 double pref = 1.0 / (
m_bMix * sqrt(T));
274 Eigen::ArrayXd Sk = 2.0 * T *
m_dAkdT - 3.0 *
m_Ak;
275 utildeVec += pref * (logv * Sk +
m_b * F * C);
282 for (
size_t k = 0; k <
m_kk; k++) {
291 double sqt = sqrt(T);
293 double dadt = da_dt();
304 double pTT = (-d2adt2 + dadt / T - 3.0 *
m_aMix / (4.0 * T * T)) / (sqt * v * vpb);
306 + (dadt -
m_aMix / (2.0 * T)) * (2.0 * v +
m_bMix) / (sqt * v * v * vpb * vpb);
307 double pVV = 2.0 *
GasConstant * T / (vmb * vmb * vmb)
309 / (sqt * v * v * v * vpb * vpb * vpb);
310 double d2vdT2_P = -(pTT + 2.0 * pTV * dvdT_P + pVV * dvdT_P * dvdT_P) /
dpdV_;
312 double F = T * dadt - 1.5 *
m_aMix;
313 double dF_dT = -0.5 * dadt + T * d2adt2;
314 double logvpv = log(vpb / v);
315 double termLPrime = -
m_bMix * dvdT_P / (v * vpb);
317 for (
size_t k = 0; k <
m_kk; k++) {
321 double Sk = 2.0 * T * dAk - 3.0 * Ak;
322 double dSk_dT = -dAk;
325 double dpdni =
RT() / vmb +
RT() * bk / (vmb * vmb)
326 - 2.0 * Ak / (sqt * v * vpb)
327 +
m_aMix * bk / (sqt * v * vpb * vpb);
333 - 2.0 *
GasConstant * T * bk * dvdT_P / (vmb * vmb * vmb)
334 - 2.0 * dAk / (sqt * v * vpb)
335 + Ak / (T * sqt * v * vpb)
336 + 2.0 * Ak * (2.0 * v +
m_bMix) * dvdT_P / (sqt * v * v * vpb * vpb)
337 + bk * da_dt() / (sqt * v * vpb * vpb)
338 - bk *
m_aMix / (2.0 * T * sqt * v * vpb * vpb)
339 - bk *
m_aMix * (3.0 * v +
m_bMix) * dvdT_P / (sqt * v * v * vpb * vpb * vpb);
343 * (termLPrime * F + logvpv * dF_dT - logvpv * F / (2.0 * T));
344 double dterm2 = 1.0 / (
m_bMix * sqt)
345 * (termLPrime * Sk + logvpv * dSk_dT - logvpv * Sk / (2.0 * T));
346 double dterm3 = bk / (
m_bMix * sqt)
347 * (dF_dT / vpb - F * dvdT_P / (vpb * vpb) - F / (2.0 * T * vpb));
351 + (dvdT_P + T * d2vdT2_P) * dpdni
352 + T * dvdT_P * d_dpdni_dT
353 + dterm1 + dterm2 + dterm3;
362 Eigen::Map<Eigen::ArrayXd> cvtildeVec(cvtilde.data(),
m_kk);
370 double sqt = sqrt(T);
372 double dadt = da_dt();
378 double logv = log(vpb / mv);
379 double F = T * dadt - 1.5 *
m_aMix;
380 double C = -logv /
m_bMix + 1.0 / vpb;
381 double pref = 1.0 / (
m_bMix * sqt);
383 Eigen::ArrayXd Sk = 2.0 * T *
m_dAkdT - 3.0 *
m_Ak;
384 Eigen::ArrayXd ures = pref * (logv * Sk +
m_b * F * C);
385 Eigen::ArrayXd dresdT = - pref * (logv *
m_dAkdT + 0.5 *
m_b * dadt * C)
387 cvtildeVec += dresdT;
398 - 2.0 *
m_Ak / (sqt * vpb)
401 MappedVector(vbar.data(),
m_kk) = num / denom;
411 m_a.row(k).setZero();
412 m_a.col(k).setZero();
419 m_a0.row(k).setConstant(NAN);
420 m_a0.col(k).setConstant(NAN);
422 m_a1.row(k).setConstant(NAN);
423 m_a1.col(k).setConstant(NAN);
425 m_Ak.conservativeResizeLike(Eigen::VectorXd::Zero(
m_kk));
426 m_dAkdT.conservativeResizeLike(Eigen::VectorXd::Zero(
m_kk));
428 m_partialMolarVolumes.push_back(0.0);
429 m_dpdni.conservativeResizeLike(Eigen::VectorXd::Zero(
m_kk));
438 std::unordered_map<string, AnyMap*> dbSpecies;
443 if (!isnan(
m_a0(k, k)) && !isnan(
m_b[k])) {
446 bool foundCoeffs =
false;
448 if (data.hasKey(
"equation-of-state") &&
449 data[
"equation-of-state"].hasMapWhere(
"model",
"Redlich-Kwong"))
453 auto eos = data[
"equation-of-state"].getMapWhere(
454 "model",
"Redlich-Kwong");
456 if (eos.hasKey(
"a") && eos.hasKey(
"b")) {
457 double a0 = 0, a1 = 0;
458 if (eos[
"a"].isScalar()) {
459 a0 = eos.convert(
"a",
"Pa*m^6/kmol^2*K^0.5");
461 auto avec = eos[
"a"].asVector<
AnyValue>(2);
462 a0 = eos.units().convert(avec[0],
"Pa*m^6/kmol^2*K^0.5");
463 a1 = eos.units().convert(avec[1],
"Pa*m^6/kmol^2/K^0.5");
465 double b = eos.convert(
"b",
"m^3/kmol");
471 if (eos.hasKey(
"binary-a")) {
474 for (
auto& [name2, coeff] : binary_a) {
475 double a0 = 0, a1 = 0;
476 if (coeff.isScalar()) {
477 a0 = units.
convert(coeff,
"Pa*m^6/kmol^2*K^0.5");
479 auto avec = coeff.asVector<
AnyValue>(2);
480 a0 = units.convert(avec[0],
"Pa*m^6/kmol^2*K^0.5");
481 a1 = units.convert(avec[1],
"Pa*m^6/kmol^2/K^0.5");
493 double Tc = NAN, Pc = NAN;
494 if (data.hasKey(
"critical-parameters")) {
497 auto& critProps = data[
"critical-parameters"].as<
AnyMap>();
498 Tc = critProps.
convert(
"critical-temperature",
"K");
499 Pc = critProps.convert(
"critical-pressure",
"Pa");
503 if (critPropsDb.
empty()) {
505 dbSpecies = critPropsDb[
"species"].asMap(
"name");
509 auto ucName = boost::algorithm::to_upper_copy(
name);
510 if (dbSpecies.count(ucName)) {
511 auto& spec = *dbSpecies.at(ucName);
512 auto& critProps = spec[
"critical-parameters"].as<
AnyMap>();
513 Tc = critProps.
convert(
"critical-temperature",
"K");
514 Pc = critProps.convert(
"critical-pressure",
"Pa");
527 "No critical property or Redlich-Kwong parameters found "
528 "for species {}.",
name);
534 AnyMap& speciesNode)
const
539 auto& eosNode = speciesNode[
"equation-of-state"].getMapWhere(
540 "model",
"Redlich-Kwong",
true);
542 if (
m_a1(k, k) != 0.0) {
543 vector<AnyValue> coeffs(2);
544 coeffs[0].setQuantity(
m_a0(k, k),
"Pa*m^6/kmol^2*K^0.5");
545 coeffs[1].setQuantity(
m_a1(k, k),
"Pa*m^6/kmol^2/K^0.5");
546 eosNode[
"a"] = std::move(coeffs);
548 eosNode[
"a"].setQuantity(
m_a0(k, k),
549 "Pa*m^6/kmol^2*K^0.5");
551 eosNode[
"b"].setQuantity(
m_b[k],
"m^3/kmol");
553 auto& critProps = speciesNode[
"critical-parameters"];
554 double a =
m_a0(k, k);
558 critProps[
"critical-temperature"].setQuantity(Tc,
"K");
559 critProps[
"critical-pressure"].setQuantity(Pc,
"Pa");
563 auto& eosNode = speciesNode[
"equation-of-state"].getMapWhere(
564 "model",
"Redlich-Kwong",
true);
567 if (coeffs.second == 0) {
568 bin_a[name2].setQuantity(coeffs.first,
"Pa*m^6/kmol^2*K^0.5");
570 vector<AnyValue> C(2);
571 C[0].setQuantity(coeffs.first,
"Pa*m^6/kmol^2*K^0.5");
572 C[1].setQuantity(coeffs.second,
"Pa*m^6/kmol^2/K^0.5");
573 bin_a[name2] = std::move(C);
576 eosNode[
"binary-a"] = std::move(bin_a);
589 double fac = da_dt() -
m_aMix / (2.0 * T);
590 double sresid_mol_R = log(zz*(1.0 - hh))
604 double fac = T * da_dt() - 3.0 *
m_aMix / (2.0);
614 double pres = std::max(
psatEst(T), presGuess);
616 bool foundLiq =
false;
618 while (m < 100 && !foundLiq) {
619 int nsol =
solveCubic(T, pres, atmp, btmp, Vroot);
620 if (nsol == 1 || nsol == 2) {
647 if (rhoguess == -1.0) {
648 if (phaseRequested >= FLUID_LIQUID_0) {
650 rhoguess = mmw / lqvol;
658 double volguess = mmw / rhoguess;
662 double vmin = std::max(0.0,
m_bMix * (1.0 + 1e-12));
663 vector<double> physicalRoots;
664 for (
double root : Vroot_) {
665 if (std::isfinite(
root) &&
root > vmin) {
666 physicalRoots.push_back(
root);
669 std::sort(physicalRoots.begin(), physicalRoots.end());
670 if (physicalRoots.empty()) {
672 }
else if (physicalRoots.size() == 1) {
676 if ((phaseRequested == FLUID_GAS && NSolns_ < 0)
677 || (phaseRequested >= FLUID_LIQUID_0 && NSolns_ > 0))
682 return mmw / physicalRoots[0];
683 }
else if (physicalRoots.size() == 2) {
684 Vroot_[0] = physicalRoots[0];
685 Vroot_[1] = 0.5 * (physicalRoots[0] + physicalRoots[1]);
686 Vroot_[2] = physicalRoots[1];
688 Vroot_[0] = physicalRoots[0];
689 Vroot_[1] = physicalRoots[1];
690 Vroot_[2] = physicalRoots[2];
693 double molarVolLast = Vroot_[0];
695 if (phaseRequested >= FLUID_LIQUID_0) {
696 molarVolLast = Vroot_[0];
697 }
else if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
698 molarVolLast = Vroot_[2];
700 if (volguess > Vroot_[1]) {
701 molarVolLast = Vroot_[2];
703 molarVolLast = Vroot_[0];
706 }
else if (NSolns_ == 1) {
707 if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT || phaseRequested == FLUID_UNDEFINED) {
708 molarVolLast = Vroot_[0];
712 }
else if (NSolns_ == -1) {
713 if (phaseRequested >= FLUID_LIQUID_0 || phaseRequested == FLUID_UNDEFINED || phaseRequested == FLUID_SUPERCRIT) {
714 molarVolLast = Vroot_[0];
715 }
else if (T > tcrit) {
716 molarVolLast = Vroot_[0];
721 molarVolLast = Vroot_[0];
724 return mmw / molarVolLast;
729 double sqt = sqrt(T);
733 double vpb = molarVol +
m_bMix;
734 double vmb = molarVol -
m_bMix;
736 +
m_aMix * (2 * molarVol +
m_bMix) / (sqt * molarVol * molarVol * vpb * vpb);
770 double fac = da_dt() -
m_aMix/(2.0 * T);
788 fmt::memory_buffer b;
789 for (
size_t k = 0; k <
m_kk; k++) {
798 throw CanteraError(
"RedlichKwongMFTP::updateMixingExpressions",
799 "Missing Redlich-Kwong coefficients for species: {}", to_string(b));
806 bCalc =
m_b.matrix().dot(x);
808 aCalc = x.dot((
m_a0 + T *
m_a1) * x);
810 aCalc = x.dot(
m_a0 * x);
814double RedlichKwongMFTP::da_dt()
const
820 return x.dot(
m_a1 * x);
823void RedlichKwongMFTP::calcCriticalConditions(
double& pc,
double& tc,
double& vc)
const
826 double a0 = x.dot(
m_a0 * x);
827 double aT = x.dot(
m_a1 * x);
846 double sqrttc, f, dfdt, deltatc;
852 for (
int j = 0; j < 10; j++) {
856 deltatc = - f / dfdt;
860 throw CanteraError(
"RedlichKwongMFTP::calcCriticalConditions",
870 span<double> Vroot)
const
876 double sqt = sqrt(T);
877 double cn = - (
GasConstant * T * b / pres - a/(pres * sqt) + b * b);
878 double dn = - (a * b / (pres * sqt));
882 double tc = pow(tmp, pp);
886 int nSolnValues =
MixtureFugacityTP::solveCubic(T, pres, a, b, a, Vroot, an, bn, cn, dn, tc, vc);
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.
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
bool empty() const
Return boolean indicating whether AnyMap is empty.
double convert(const string &key, const string &units) const
Convert the item stored by the given key to the units specified in units.
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
A wrapper for a variable whose type is determined at runtime.
Base class for exceptions thrown by Cantera classes.
void getEntropy_R_ref(span< double > er) const override
Returns the vector of nondimensional entropies of the reference state at the current temperature of t...
double critPressure() const override
Critical pressure (Pa).
vector< double > m_h0_RT
Temporary storage for dimensionless reference state enthalpies.
void getStandardChemPotentials(span< double > mu) const override
Get the array of chemical potentials at unit activity.
double critTemperature() const override
Critical temperature (K).
virtual void _updateReferenceStateThermo() const
Updates the reference state thermodynamic functions at the current T of the solution.
void getEnthalpy_RT_ref(span< double > hrt) const override
Returns the vector of nondimensional enthalpies of the reference state at the current temperature of ...
vector< double > moleFractions_
Storage for the current values of the mole fractions of the species.
int solveCubic(double T, double pres, double a, double b, double aAlpha, span< double > Vroot, double an, double bn, double cn, double dn, double tc, double vc) const
Solve the cubic equation of state.
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(span< 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.
vector< double > m_workS
Vector of size m_kk, used as a temporary holding area.
size_t nSpecies() const
Returns the number of species in the phase.
size_t m_kk
Number of species in the phase.
size_t speciesIndex(const string &name, bool raise=true) const
Returns the index of a species named 'name' within the Phase object.
double temperature() const
Temperature (K).
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
string speciesName(size_t k) const
Name of the species with index k.
map< string, shared_ptr< Species > > m_species
Map of Species objects.
double mean_X(span< const double > Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
virtual double density() const
Density (kg/m^3).
shared_ptr< Species > species(const string &name) const
Return the Species object for the named species.
virtual double molarVolume() const
Molar volume (m^3/kmol).
string name() const
Return the name of the phase.
double m_aMix
Value of a in the equation of state.
Eigen::ArrayXd m_dpdni
Vector of derivatives of pressure wrt mole number.
double dpdT_
The derivative of the pressure wrt the temperature.
Eigen::MatrixXd m_a
Current temperature-dependent value of for each species pair.
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.
Eigen::ArrayXd m_Ak
. Length m_kk.
double sresid() const override
Calculate the deviation terms for the total entropy of the mixture from the ideal gas mixture.
void getPartialMolarEnthalpies(span< double > hbar) const override
Return partial molar enthalpies (J/kmol).
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.
void getPartialMolarCp(span< double > cpbar) const override
Get the partial molar heat capacities at constant pressure [J/kmol/K].
static const double omega_b
Omega constant for b.
void getActivityCoefficients(span< double > ac) const override
Get the array of non-dimensional activity coefficients at the current solution temperature,...
void initThermo() override
Initialize the ThermoPhase object after all species have been set up.
void getPartialMolarIntEnergies_TV(span< double > utilde) const override
Get the species molar internal energies associated with the derivatives of total internal energy at c...
double internalPressure() const override
Return the internal pressure [Pa].
Eigen::MatrixXd m_a1
Temperature coefficient in the expression for .
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 liquidVolEst(double T, double &pres) const override
Estimate for the molar volume of the liquid.
Eigen::ArrayXd m_dAkdT
. Length m_kk.
double m_bMix
Value of b in the equation of state.
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.
void getPartialMolarCv_TV(span< double > cvtilde) const override
Get the species molar heat capacities associated with the constant volume partial molar internal ener...
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.
Eigen::MatrixXd m_a0
Constant term in the expression for for each species pair.
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].
void getPartialMolarVolumes(span< double > vbar) const override
Return an array of partial molar volumes for the species in the mixture.
void getPartialMolarEntropies(span< double > sbar) const override
Returns an array of partial molar entropies of the species in the solution.
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 dpdVCalc(double T, double molarVol, double &presCalc) const override
Calculate the pressure and the pressure derivative given the temperature and the molar volume.
Eigen::ArrayXd m_b
Coefficients for each species. Size m_kk.
void getChemPotentials(span< double > mu) const override
Get the species chemical potentials. Units: J/kmol.
int solveCubic(double T, double pres, double a, double b, span< double > Vroot) const
Prepare variables and call the function to solve the cubic equation of state.
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.
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.
void calculateAB(double T, double &aCalc, double &bCalc) const
Calculate the a and the b parameters given the temperature.
void getPartialMolarIntEnergies(span< double > ubar) const override
Return an array of partial molar internal energies for the species in the mixture.
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.
shared_ptr< Solution > root() const
Get the Solution object containing this ThermoPhase object and linked Kinetics and Transport objects.
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.
double convert(double value, const string &src, const string &dest) const
Convert value from the units of src to the units of dest.
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
const double GasConstant
Universal Gas Constant [J/kmol/K].
Namespace for the Cantera kernel.
MappedVector asVectorXd(vector< double > &v)
Convenience wrapper for accessing std::vector as an Eigen VectorXd.
const double SmallNumber
smallest number to compare to zero.
void checkArraySize(const char *procedure, size_t available, size_t required)
Wrapper for throwing ArraySizeError.
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...