12#include <boost/algorithm/string.hpp>
13#include <boost/math/tools/roots.hpp>
15namespace bmt = boost::math::tools;
36 m_kappa[k] = 0.37464 + 1.54226*w - 0.26992*w*w;
38 m_kappa[k] = 0.374642 + 1.487503*w - 0.164423*w*w + 0.016666*w*w*w;
45 double sqt_alpha = 1 + m_kappa[k] * (1 - sqt_T_r);
46 m_alpha[k] = sqt_alpha*sqt_alpha;
49 double aAlpha_k = a*m_alpha[k];
50 m_aAlpha_binary(k,k) = aAlpha_k;
53 for (
size_t j = 0; j <
m_kk; j++) {
57 double a0kj = sqrt(m_a_coeffs(j,j) * a);
58 double aAlpha_j = a*m_alpha[j];
59 double a_Alpha = sqrt(aAlpha_j*aAlpha_k);
60 if (m_a_coeffs(j, k) == 0) {
61 m_a_coeffs(j, k) = a0kj;
62 m_aAlpha_binary(j, k) = a_Alpha;
63 m_a_coeffs(k, j) = a0kj;
64 m_aAlpha_binary(k, j) = a_Alpha;
71 const string& species_j,
double a0)
76 m_a_coeffs(ki, kj) = m_a_coeffs(kj, ki) = a0;
80 double alpha_ij = m_alpha[ki] * m_alpha[kj];
81 m_aAlpha_binary(ki, kj) = m_aAlpha_binary(kj, ki) = a0*alpha_ij;
114 double denom = mv * mv + 2 * mv *
m_b -
m_b *
m_b;
127 double vpb2 = mv + (1 +
Sqrt2) *
m_b;
128 double vmb2 = mv + (1 -
Sqrt2) *
m_b;
129 double vmb = mv -
m_b;
132 for (
size_t k = 0; k <
m_kk; k++) {
134 for (
size_t i = 0; i <
m_kk; i++) {
140 double denom2 =
m_b * (mv * mv + 2 * mv *
m_b -
m_b *
m_b);
142 for (
size_t k = 0; k <
m_kk; k++) {
144 ac[k] = (-RT_ * log(pres * mv/ RT_) + RT_ * log(mv / vmb)
145 + RT_ * m_b_coeffs[k] / vmb
146 - (num /denom) * log(vpb2/vmb2)
150 for (
size_t k = 0; k <
m_kk; k++) {
151 ac[k] = exp(ac[k]/ RT_);
161 for (
size_t k = 0; k <
m_kk; k++) {
163 mu[k] += RT_ * (log(xx));
167 double vmb = mv -
m_b;
168 double vpb2 = mv + (1 +
Sqrt2) *
m_b;
169 double vmb2 = mv + (1 -
Sqrt2) *
m_b;
171 for (
size_t k = 0; k <
m_kk; k++) {
173 for (
size_t i = 0; i <
m_kk; i++) {
180 double denom2 =
m_b * (mv * mv + 2 * mv *
m_b -
m_b *
m_b);
182 for (
size_t k = 0; k <
m_kk; k++) {
185 mu[k] += RT_ * log(pres/refP) - RT_ * log(pres * mv / RT_)
186 + RT_ * log(mv / vmb) + RT_ * m_b_coeffs[k] / vmb
187 - (num /denom) * log(vpb2/vmb2)
198 tmp.resize(
m_kk,0.0);
203 double vmb = mv -
m_b;
204 double vpb2 = mv + (1 +
Sqrt2) *
m_b;
205 double vmb2 = mv + (1 -
Sqrt2) *
m_b;
208 for (
size_t k = 0; k <
m_kk; k++) {
211 for (
size_t i = 0; i <
m_kk; i++) {
212 double grad_aAlpha = m_dalphadT[i]/m_alpha[i] + m_dalphadT[k]/m_alpha[k];
218 double denom = mv * mv + 2 * mv *
m_b -
m_b *
m_b;
219 double denom2 = denom * denom;
221 for (
size_t k = 0; k <
m_kk; k++) {
222 m_dpdni[k] = RT_ / vmb + RT_ * m_b_coeffs[k] / (vmb * vmb)
231 for (
size_t k = 0; k <
m_kk; k++) {
232 fac4 = T*tmp[k] -2 *
m_pp[k];
233 double hE_v = mv *
m_dpdni[k] - RT_
234 - m_b_coeffs[k] / fac3 * log(vpb2 / vmb2) * fac
235 + (mv * m_b_coeffs[k]) / (
m_b * denom) * fac
236 + 1 / (2 *
Sqrt2 *
m_b) * log(vpb2 / vmb2) * fac4;
237 hbar[k] = hbar[k] + hE_v;
248 for (
size_t k = 0; k <
m_kk; k++) {
249 sbar[k] = (sbar[k] -
m_workS[k])/T;
259 for (
size_t k = 0; k <
m_kk; k++) {
260 ubar[k] = ubar[k] - p*
m_workS[k];
271 for (
size_t k = 0; k <
m_kk; k++) {
273 for (
size_t i = 0; i <
m_kk; i++) {
279 double vmb = mv -
m_b;
280 double vpb = mv +
m_b;
281 double fac = mv * mv + 2 * mv *
m_b -
m_b *
m_b;
282 double fac2 = fac * fac;
285 for (
size_t k = 0; k <
m_kk; k++) {
286 double num = RT_ + RT_ *
m_b/ vmb + RT_ * m_b_coeffs[k] / vmb
287 + RT_ *
m_b * m_b_coeffs[k] /(vmb * vmb) - 2 * mv *
m_pp[k] / fac
291 vbar[k] = num / denom;
299 }
else if (a <= 0.0) {
311 m_b_coeffs.push_back(0.0);
313 m_kappa.push_back(0.0);
315 m_alpha.push_back(0.0);
316 m_dalphadT.push_back(0.0);
317 m_d2alphadT2.push_back(0.0);
319 m_partialMolarVolumes.push_back(0.0);
330 std::unordered_map<string, AnyMap*> dbSpecies;
335 if (m_a_coeffs(k, k) != 0.0) {
338 bool foundCoeffs =
false;
339 if (data.hasKey(
"equation-of-state") &&
340 data[
"equation-of-state"].hasMapWhere(
"model",
"Peng-Robinson"))
344 auto eos = data[
"equation-of-state"].getMapWhere(
345 "model",
"Peng-Robinson");
346 if (eos.hasKey(
"a") && eos.hasKey(
"b") && eos.hasKey(
"acentric-factor")) {
347 double a0 = eos.convert(
"a",
"Pa*m^6/kmol^2");
348 double b = eos.convert(
"b",
"m^3/kmol");
350 double w = eos[
"acentric-factor"].asDouble();
355 if (eos.hasKey(
"binary-a")) {
358 for (
auto& [name2, coeff] : binary_a) {
359 double a0 = units.
convert(coeff,
"Pa*m^6/kmol^2");
370 double Tc = NAN, Pc = NAN, omega_ac = NAN;
371 if (data.hasKey(
"critical-parameters")) {
374 auto& critProps = data[
"critical-parameters"].as<
AnyMap>();
375 Tc = critProps.
convert(
"critical-temperature",
"K");
376 Pc = critProps.convert(
"critical-pressure",
"Pa");
377 omega_ac = critProps[
"acentric-factor"].asDouble();
381 if (critPropsDb.
empty()) {
383 dbSpecies = critPropsDb[
"species"].asMap(
"name");
387 auto ucName = boost::algorithm::to_upper_copy(
name);
388 if (dbSpecies.count(ucName)) {
389 auto& spec = *dbSpecies.at(ucName);
390 auto& critProps = spec[
"critical-parameters"].as<
AnyMap>();
391 Tc = critProps.
convert(
"critical-temperature",
"K");
392 Pc = critProps.convert(
"critical-pressure",
"Pa");
393 omega_ac = critProps[
"acentric-factor"].asDouble();
405 "No Peng-Robinson model parameters or critical properties found for "
406 "species '{}'",
name);
418 auto& eosNode = speciesNode[
"equation-of-state"].getMapWhere(
419 "model",
"Peng-Robinson",
true);
420 eosNode[
"a"].setQuantity(m_a_coeffs(k, k),
"Pa*m^6/kmol^2");
421 eosNode[
"b"].setQuantity(m_b_coeffs[k],
"m^3/kmol");
424 auto& critProps = speciesNode[
"critical-parameters"];
427 critProps[
"critical-temperature"].setQuantity(Tc,
"K");
428 critProps[
"critical-pressure"].setQuantity(Pc,
"Pa");
436 auto& eosNode = speciesNode[
"equation-of-state"].getMapWhere(
437 "model",
"Peng-Robinson",
true);
440 bin_a[
species].setQuantity(coeff,
"Pa*m^6/kmol^2");
442 eosNode[
"binary-a"] = std::move(bin_a);
449 double hh =
m_b / molarV;
452 double vpb = molarV + (1.0 +
Sqrt2) *
m_b;
453 double vmb = molarV + (1.0 -
Sqrt2) *
m_b;
454 double fac = alpha_1 / (2.0 *
Sqrt2 *
m_b);
455 double sresid_mol_R = log(zz*(1.0 - hh)) + fac * log(vpb / vmb) /
GasConstant;
465 double vpb = molarV + (1 +
Sqrt2) *
m_b;
466 double vmb = molarV + (1 -
Sqrt2) *
m_b;
467 double fac = 1 / (2.0 *
Sqrt2 *
m_b);
474 double v =
m_b * 1.1;
475 double atmp, btmp, aAlphatmp;
477 double pres = std::max(
psatEst(T), presGuess);
479 bool foundLiq =
false;
481 while (m < 100 && !foundLiq) {
482 int nsol =
solveCubic(T, pres, atmp, btmp, aAlphatmp, Vroot);
483 if (nsol == 1 || nsol == 2) {
510 if (rhoGuess == -1.0) {
511 if (phaseRequested >= FLUID_LIQUID_0) {
513 rhoGuess = mmw / lqvol;
520 double volGuess = mmw / rhoGuess;
523 double molarVolLast = m_Vroot[0];
525 if (phaseRequested >= FLUID_LIQUID_0) {
526 molarVolLast = m_Vroot[0];
527 }
else if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
528 molarVolLast = m_Vroot[2];
530 if (volGuess > m_Vroot[1]) {
531 molarVolLast = m_Vroot[2];
533 molarVolLast = m_Vroot[0];
536 }
else if (m_NSolns == 1) {
537 if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT
538 || phaseRequested == FLUID_UNDEFINED)
540 molarVolLast = m_Vroot[0];
544 }
else if (m_NSolns == -1) {
545 if (phaseRequested >= FLUID_LIQUID_0 || phaseRequested == FLUID_UNDEFINED
546 || phaseRequested == FLUID_SUPERCRIT)
548 molarVolLast = m_Vroot[0];
549 }
else if (T > tcrit) {
550 molarVolLast = m_Vroot[0];
555 molarVolLast = m_Vroot[0];
558 return mmw / molarVolLast;
570 auto resid = [
this, T](
double v) {
575 boost::uintmax_t maxiter = 100;
576 auto [lower, upper] = bmt::toms748_solve(
577 resid, Vroot[0], Vroot[1], bmt::eps_tolerance<double>(48), maxiter);
580 return mmw / (0.5 * (lower + upper));
592 auto resid = [
this, T](
double v) {
597 boost::uintmax_t maxiter = 100;
598 auto [lower, upper] = bmt::toms748_solve(
599 resid, Vroot[1], Vroot[2], bmt::eps_tolerance<double>(48), maxiter);
602 return mmw / (0.5 * (lower + upper));
607 double denom = molarVol * molarVol + 2 * molarVol *
m_b -
m_b *
m_b;
608 double vpb = molarVol +
m_b;
609 double vmb = molarVol -
m_b;
638 double vmb = mv -
m_b;
639 double denom = mv * mv + 2 * mv *
m_b -
m_b *
m_b;
648 for (
size_t j = 0; j <
m_kk; j++) {
650 double sqt_alpha = 1 + m_kappa[j] * (1 - sqrt(temp / critTemp_j));
651 m_alpha[j] = sqt_alpha*sqt_alpha;
655 for (
size_t i = 0; i <
m_kk; i++) {
656 for (
size_t j = 0; j <
m_kk; j++) {
657 m_aAlpha_binary(i, j) = sqrt(m_alpha[i] * m_alpha[j]) * m_a_coeffs(i,j);
668 for (
size_t i = 0; i <
m_kk; i++) {
670 for (
size_t j = 0; j <
m_kk; j++) {
679 double daAlphadT = 0.0, k, Tc, sqtTr, coeff1, coeff2;
680 for (
size_t i = 0; i <
m_kk; i++) {
684 coeff1 = 1 / (Tc*sqtTr);
687 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
690 for (
size_t i = 0; i <
m_kk; i++) {
691 for (
size_t j = 0; j <
m_kk; j++) {
693 * m_aAlpha_binary(i, j)
694 * (m_dalphadT[i] / m_alpha[i] + m_dalphadT[j] / m_alpha[j]);
702 for (
size_t i = 0; i <
m_kk; i++) {
705 double coeff1 = 1 / (Tcrit_i*sqt_Tr);
706 double coeff2 = sqt_Tr - 1;
708 double k = m_kappa[i];
709 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
710 m_d2alphadT2[i] = (k*k + k) * coeff1 / (2*sqt_Tr*sqt_Tr*Tcrit_i);
714 double d2aAlphadT2 = 0.0;
715 for (
size_t i = 0; i <
m_kk; i++) {
716 double alphai = m_alpha[i];
717 for (
size_t j = 0; j <
m_kk; j++) {
718 double alphaj = m_alpha[j];
719 double alphaij = alphai * alphaj;
720 double term1 = m_d2alphadT2[i] / alphai + m_d2alphadT2[j] / alphaj;
721 double term2 = 2 * m_dalphadT[i] * m_dalphadT[j] / alphaij;
722 double term3 = m_dalphadT[i] / alphai + m_dalphadT[j] / alphaj;
724 * m_aAlpha_binary(i, j)
725 * (term1 + term2 - 0.5 * term3 * term3);
731void PengRobinson::calcCriticalConditions(
double& pc,
double& tc,
double& vc)
const
751 double Vroot[3])
const
756 double aAlpha_p = aAlpha / pres;
758 double bn = (b - RT_p);
759 double cn = -(2 * RT_p * b - aAlpha_p + 3 * bsqr);
760 double dn = (bsqr * RT_p + bsqr * b - aAlpha_p * b);
767 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.
virtual void resize(size_t n, size_t m, double v=0.0)
Resize the array, and fill the new entries with 'v'.
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 setTemperature(const double temp) override
Set the temperature of the phase.
virtual double psatEst(double TKelvin) const
Estimate for the saturation pressure.
void getStandardVolumes(double *vol) const override
Get the molar volumes of each species in their standard states at the current T and P of the solution...
vector< double > m_cp0_R
Temporary storage for dimensionless reference state heat capacities.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
double z() const
Calculate the value of z.
void getEnthalpy_RT_ref(double *hrt) const override
Returns the vector of nondimensional enthalpies of the reference state at the current temperature of ...
An error indicating that an unimplemented function has been called.
double thermalExpansionCoeff() const override
Return the volumetric thermal expansion coefficient. Units: 1/K.
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.
void setSpeciesCoeffs(const string &species, double a, double b, double w)
Set the pure fluid interaction parameters for a species.
double sresid() const override
Calculate the deviation terms for the total entropy of the mixture from the ideal gas mixture.
double soundSpeed() const override
Return the speed of sound. Units: m/s.
double pressure() const override
Return the thermodynamic pressure (Pa).
map< string, map< string, double > > m_binaryParameters
Explicitly-specified binary interaction parameters, to enable serialization.
void getSpeciesParameters(const string &name, AnyMap &speciesNode) const override
Get phase-specific parameters of a Species object such that an identical one could be reconstructed a...
static const double omega_b
Omega constant: b0 (= omega_b) used in Peng-Robinson equation of state.
vector< double > m_pp
Temporary storage - length = m_kk.
void setBinaryCoeffs(const string &species_i, const string &species_j, double a)
Set values for the interaction parameter between two species.
vector< double > m_dpdni
Vector of derivatives of pressure with respect to mole number.
PengRobinson(const string &infile="", const string &id="")
Construct and initialize a PengRobinson object directly from an input file.
void initThermo() override
Initialize the ThermoPhase object after all species have been set up.
double densSpinodalGas() const override
Return the value of the density at the gas spinodal point (on the gas side) for the current temperatu...
double m_dpdT
The derivative of the pressure with respect to the temperature.
void getPartialMolarVolumes(double *vbar) const override
Return an array of partial molar volumes for the species in the mixture.
double cv_mole() const override
Molar heat capacity at constant volume and composition [J/kmol/K].
double liquidVolEst(double T, double &pres) const override
Estimate for the molar volume of the liquid.
int solveCubic(double T, double pres, double a, double b, double aAlpha, double Vroot[3]) const
Prepare variables and call the function to solve the cubic equation of state.
double daAlpha_dT() const
Calculate temperature derivative .
void calculatePressureDerivatives() const
Calculate and at the current conditions.
double isothermalCompressibility() const override
Returns the isothermal compressibility. Units: 1/Pa.
double hresid() const override
Calculate the deviation terms for the total enthalpy of the mixture from the ideal gas mixture.
double m_aAlpha_mix
Value of in the equation of state.
double m_a
Value of in the equation of state.
static const double omega_a
Omega constant: a0 (= omega_a) used in Peng-Robinson equation of state.
static const double omega_vc
Omega constant for the critical molar volume.
vector< CoeffSource > m_coeffSource
For each species, specifies the source of the a, b, and omega coefficients.
void calculateAB(double &aCalc, double &bCalc, double &aAlpha) const
Calculate the , , and parameters given the temperature.
void getPartialMolarIntEnergies(double *ubar) const override
Return an array of partial molar internal energies for the species in the mixture.
void updateMixingExpressions() override
Update the , , and parameters.
double cp_mole() const override
Molar heat capacity at constant pressure and composition [J/kmol/K].
double m_b
Value of in the equation of state.
void getPartialMolarCp(double *cpbar) const override
Calculate species-specific molar specific heats.
double standardConcentration(size_t k=0) const override
Returns the standard concentration , which is used to normalize the generalized concentration.
AnyMap getAuxiliaryData() override
Return parameters used by the Peng-Robinson equation of state.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
double dpdVCalc(double T, double molarVol, double &presCalc) const override
Calculate the pressure and the pressure derivative given the temperature and the molar volume.
void getActivityCoefficients(double *ac) const override
Get the array of non-dimensional activity coefficients at the current solution temperature,...
double d2aAlpha_dT2() const
Calculate second derivative .
double densityCalc(double T, double pressure, int phase, double rhoguess) override
Calculates the density given the temperature and the pressure and a guess at the density.
void getPartialMolarEntropies(double *sbar) const override
Returns an array of partial molar entropies of the species in the solution.
double m_dpdV
The derivative of the pressure with respect to the volume.
double speciesCritTemperature(double a, double b) const
Calculate species-specific critical temperature.
vector< double > m_acentric
acentric factor for each species, length m_kk
vector< double > m_workS
Vector of size m_kk, used as a temporary holding area.
size_t m_kk
Number of species in the phase.
double temperature() const
Temperature (K).
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
map< string, shared_ptr< Species > > m_species
Map of Species objects.
size_t speciesIndex(const string &name) const
Returns the index of a species named 'name' within the Phase object.
double moleFraction(size_t k) const
Return the mole fraction of a single species.
double mean_X(const double *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
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 RT() const
Return the Gas Constant multiplied by the current temperature.
AnyMap parameters(bool withInput=true) const
Returns the parameters of a ThermoPhase object such that an identical one could be reconstructed usin...
virtual AnyMap getAuxiliaryData()
Return intermediate or model-specific parameters used by particular derived classes.
void initThermoFile(const string &inputFile, const string &id)
Initialize a ThermoPhase object using an input file.
virtual void getSpeciesParameters(const string &name, AnyMap &speciesNode) const
Get phase-specific parameters of a Species object such that an identical one could be reconstructed a...
virtual double refPressure() const
Returns the reference pressure in Pa.
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].
const double Sqrt2
Sqrt(2)
Namespace for the Cantera kernel.
const double SmallNumber
smallest number to compare to zero.
Contains declarations for string manipulation functions within Cantera.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...