12#include <boost/math/tools/roots.hpp>
14namespace bmt = boost::math::tools;
31 std::fill_n(m_Vroot, 3, 0.0);
41 "Unknown species '{}'.",
species);
47 m_kappa[k] = 0.37464 + 1.54226*w - 0.26992*w*w;
49 m_kappa[k] = 0.374642 + 1.487503*w - 0.164423*w*w + 0.016666*w*w*w;
56 double sqt_alpha = 1 + m_kappa[k] * (1 - sqt_T_r);
57 m_alpha[k] = sqt_alpha*sqt_alpha;
60 double aAlpha_k = a*m_alpha[k];
61 m_aAlpha_binary(k,k) = aAlpha_k;
64 for (
size_t j = 0; j <
m_kk; j++) {
68 double a0kj = sqrt(m_a_coeffs(j,j) * a);
69 double aAlpha_j = a*m_alpha[j];
70 double a_Alpha = sqrt(aAlpha_j*aAlpha_k);
71 if (m_a_coeffs(j, k) == 0) {
72 m_a_coeffs(j, k) = a0kj;
73 m_aAlpha_binary(j, k) = a_Alpha;
74 m_a_coeffs(k, j) = a0kj;
75 m_aAlpha_binary(k, j) = a_Alpha;
82 const std::string& species_j,
double a0)
87 "Unknown species '{}'.", species_i);
92 "Unknown species '{}'.", species_j);
95 m_a_coeffs(ki, kj) = m_a_coeffs(kj, ki) = a0;
99 double alpha_ij = m_alpha[ki] * m_alpha[kj];
100 m_aAlpha_binary(ki, kj) = m_aAlpha_binary(kj, ki) = a0*alpha_ij;
110 double vpb = mv + (1 +
Sqrt2) *
m_b;
111 double vmb = mv + (1 -
Sqrt2) *
m_b;
133 double denom = mv * mv + 2 * mv *
m_b -
m_b *
m_b;
146 double vpb2 = mv + (1 +
Sqrt2) *
m_b;
147 double vmb2 = mv + (1 -
Sqrt2) *
m_b;
148 double vmb = mv -
m_b;
151 for (
size_t k = 0; k <
m_kk; k++) {
153 for (
size_t i = 0; i <
m_kk; i++) {
159 double denom2 =
m_b * (mv * mv + 2 * mv *
m_b -
m_b *
m_b);
161 for (
size_t k = 0; k <
m_kk; k++) {
163 ac[k] = (-RT_ * log(pres * mv/ RT_) + RT_ * log(mv / vmb)
164 + RT_ * m_b_coeffs[k] / vmb
165 - (num /denom) * log(vpb2/vmb2)
169 for (
size_t k = 0; k <
m_kk; k++) {
170 ac[k] = exp(ac[k]/ RT_);
180 for (
size_t k = 0; k <
m_kk; k++) {
182 mu[k] += RT_ * (log(xx));
186 double vmb = mv -
m_b;
187 double vpb2 = mv + (1 +
Sqrt2) *
m_b;
188 double vmb2 = mv + (1 -
Sqrt2) *
m_b;
190 for (
size_t k = 0; k <
m_kk; k++) {
192 for (
size_t i = 0; i <
m_kk; i++) {
199 double denom2 =
m_b * (mv * mv + 2 * mv *
m_b -
m_b *
m_b);
201 for (
size_t k = 0; k <
m_kk; k++) {
204 mu[k] += RT_ * log(pres/refP) - RT_ * log(pres * mv / RT_)
205 + RT_ * log(mv / vmb) + RT_ * m_b_coeffs[k] / vmb
206 - (num /denom) * log(vpb2/vmb2)
217 tmp.resize(
m_kk,0.0);
222 double vmb = mv -
m_b;
223 double vpb2 = mv + (1 +
Sqrt2) *
m_b;
224 double vmb2 = mv + (1 -
Sqrt2) *
m_b;
227 for (
size_t k = 0; k <
m_kk; k++) {
230 for (
size_t i = 0; i <
m_kk; i++) {
231 double grad_aAlpha = m_dalphadT[i]/m_alpha[i] + m_dalphadT[k]/m_alpha[k];
237 double denom = mv * mv + 2 * mv *
m_b -
m_b *
m_b;
238 double denom2 = denom * denom;
240 for (
size_t k = 0; k <
m_kk; k++) {
241 m_dpdni[k] = RT_ / vmb + RT_ * m_b_coeffs[k] / (vmb * vmb)
250 for (
size_t k = 0; k <
m_kk; k++) {
251 fac4 = T*tmp[k] -2 *
m_pp[k];
252 double hE_v = mv *
m_dpdni[k] - RT_
253 - m_b_coeffs[k] / fac3 * log(vpb2 / vmb2) * fac
254 + (mv * m_b_coeffs[k]) / (
m_b * denom) * fac
255 + 1 / (2 *
Sqrt2 *
m_b) * log(vpb2 / vmb2) * fac4;
256 hbar[k] = hbar[k] + hE_v;
267 for (
size_t k = 0; k <
m_kk; k++) {
268 sbar[k] = (sbar[k] -
m_tmpV[k])/T;
278 for (
size_t k = 0; k <
m_kk; k++) {
279 ubar[k] = ubar[k] - p*
m_tmpV[k];
290 for (
size_t k = 0; k <
m_kk; k++) {
292 for (
size_t i = 0; i <
m_kk; i++) {
298 double vmb = mv -
m_b;
299 double vpb = mv +
m_b;
300 double fac = mv * mv + 2 * mv *
m_b -
m_b *
m_b;
301 double fac2 = fac * fac;
304 for (
size_t k = 0; k <
m_kk; k++) {
305 double num = RT_ + RT_ *
m_b/ vmb + RT_ * m_b_coeffs[k] / vmb
306 + RT_ *
m_b * m_b_coeffs[k] /(vmb * vmb) - 2 * mv *
m_pp[k] / fac
310 vbar[k] = num / denom;
318 }
else if (a <= 0.0) {
330 m_b_coeffs.push_back(0.0);
332 m_kappa.push_back(0.0);
334 m_alpha.push_back(0.0);
335 m_dalphadT.push_back(0.0);
336 m_d2alphadT2.push_back(0.0);
338 m_partialMolarVolumes.push_back(0.0);
349 std::unordered_map<std::string, AnyMap*> dbSpecies;
351 for (
auto& item : m_species) {
352 auto& data = item.second->input;
354 if (m_a_coeffs(k, k) != 0.0) {
357 bool foundCoeffs =
false;
358 if (data.hasKey(
"equation-of-state") &&
359 data[
"equation-of-state"].hasMapWhere(
"model",
"Peng-Robinson"))
363 auto eos = data[
"equation-of-state"].getMapWhere(
364 "model",
"Peng-Robinson");
365 if (eos.hasKey(
"a") && eos.hasKey(
"b") && eos.hasKey(
"acentric-factor")) {
366 double a0 = eos.convert(
"a",
"Pa*m^6/kmol^2");
367 double b = eos.convert(
"b",
"m^3/kmol");
369 double w = eos[
"acentric-factor"].asDouble();
374 if (eos.hasKey(
"binary-a")) {
377 for (
auto& item2 : binary_a) {
378 double a0 = units.
convert(item2.second,
"Pa*m^6/kmol^2");
389 double Tc = NAN, Pc = NAN, omega_ac = NAN;
390 if (data.hasKey(
"critical-parameters")) {
393 auto& critProps = data[
"critical-parameters"].as<
AnyMap>();
394 Tc = critProps.
convert(
"critical-temperature",
"K");
395 Pc = critProps.convert(
"critical-pressure",
"Pa");
396 omega_ac = critProps[
"acentric-factor"].asDouble();
400 if (critPropsDb.
empty()) {
402 dbSpecies = critPropsDb[
"species"].asMap(
"name");
406 auto ucName = boost::algorithm::to_upper_copy(item.first);
407 if (dbSpecies.count(ucName)) {
408 auto& spec = *dbSpecies.at(ucName);
409 auto& critProps = spec[
"critical-parameters"].as<
AnyMap>();
410 Tc = critProps.
convert(
"critical-temperature",
"K");
411 Pc = critProps.convert(
"critical-pressure",
"Pa");
412 omega_ac = critProps[
"acentric-factor"].asDouble();
424 "No Peng-Robinson model parameters or critical properties found for "
425 "species '{}'", item.first);
431 AnyMap& speciesNode)
const
439 auto& eosNode = speciesNode[
"equation-of-state"].getMapWhere(
440 "model",
"Peng-Robinson",
true);
441 eosNode[
"a"].setQuantity(m_a_coeffs(k, k),
"Pa*m^6/kmol^2");
442 eosNode[
"b"].setQuantity(m_b_coeffs[k],
"m^3/kmol");
445 auto& critProps = speciesNode[
"critical-parameters"];
448 critProps[
"critical-temperature"].setQuantity(Tc,
"K");
449 critProps[
"critical-pressure"].setQuantity(Pc,
"Pa");
457 auto& eosNode = speciesNode[
"equation-of-state"].getMapWhere(
458 "model",
"Peng-Robinson",
true);
461 bin_a[item.first].setQuantity(item.second,
"Pa*m^6/kmol^2");
463 eosNode[
"binary-a"] = std::move(bin_a);
470 double hh =
m_b / molarV;
473 double vpb = molarV + (1.0 +
Sqrt2) *
m_b;
474 double vmb = molarV + (1.0 -
Sqrt2) *
m_b;
475 double fac = alpha_1 / (2.0 *
Sqrt2 *
m_b);
476 double sresid_mol_R = log(zz*(1.0 - hh)) + fac * log(vpb / vmb) /
GasConstant;
486 double vpb = molarV + (1 +
Sqrt2) *
m_b;
487 double vmb = molarV + (1 -
Sqrt2) *
m_b;
488 double fac = 1 / (2.0 *
Sqrt2 *
m_b);
495 double v =
m_b * 1.1;
496 double atmp, btmp, aAlphatmp;
498 double pres = std::max(
psatEst(T), presGuess);
500 bool foundLiq =
false;
502 while (m < 100 && !foundLiq) {
503 int nsol =
solveCubic(T, pres, atmp, btmp, aAlphatmp, Vroot);
504 if (nsol == 1 || nsol == 2) {
531 if (rhoGuess == -1.0) {
532 if (phaseRequested >= FLUID_LIQUID_0) {
534 rhoGuess = mmw / lqvol;
541 double volGuess = mmw / rhoGuess;
544 double molarVolLast = m_Vroot[0];
546 if (phaseRequested >= FLUID_LIQUID_0) {
547 molarVolLast = m_Vroot[0];
548 }
else if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT) {
549 molarVolLast = m_Vroot[2];
551 if (volGuess > m_Vroot[1]) {
552 molarVolLast = m_Vroot[2];
554 molarVolLast = m_Vroot[0];
557 }
else if (m_NSolns == 1) {
558 if (phaseRequested == FLUID_GAS || phaseRequested == FLUID_SUPERCRIT
559 || phaseRequested == FLUID_UNDEFINED)
561 molarVolLast = m_Vroot[0];
565 }
else if (m_NSolns == -1) {
566 if (phaseRequested >= FLUID_LIQUID_0 || phaseRequested == FLUID_UNDEFINED
567 || phaseRequested == FLUID_SUPERCRIT)
569 molarVolLast = m_Vroot[0];
570 }
else if (T > tcrit) {
571 molarVolLast = m_Vroot[0];
576 molarVolLast = m_Vroot[0];
579 return mmw / molarVolLast;
591 auto resid = [
this, T](
double v) {
596 boost::uintmax_t maxiter = 100;
597 std::pair<double, double> vv = bmt::toms748_solve(
598 resid, Vroot[0], Vroot[1], bmt::eps_tolerance<double>(48), maxiter);
601 return mmw / (0.5 * (vv.first + vv.second));
613 auto resid = [
this, T](
double v) {
618 boost::uintmax_t maxiter = 100;
619 std::pair<double, double> vv = bmt::toms748_solve(
620 resid, Vroot[1], Vroot[2], bmt::eps_tolerance<double>(48), maxiter);
623 return mmw / (0.5 * (vv.first + vv.second));
628 double denom = molarVol * molarVol + 2 * molarVol *
m_b -
m_b *
m_b;
629 double vpb = molarVol +
m_b;
630 double vmb = molarVol -
m_b;
641 double vmb = mv -
m_b;
642 double denom = mv * mv + 2 * mv *
m_b -
m_b *
m_b;
651 for (
size_t j = 0; j <
m_kk; j++) {
653 double sqt_alpha = 1 + m_kappa[j] * (1 - sqrt(temp / critTemp_j));
654 m_alpha[j] = sqt_alpha*sqt_alpha;
658 for (
size_t i = 0; i <
m_kk; i++) {
659 for (
size_t j = 0; j <
m_kk; j++) {
660 m_aAlpha_binary(i, j) = sqrt(m_alpha[i] * m_alpha[j]) * m_a_coeffs(i,j);
671 for (
size_t i = 0; i <
m_kk; i++) {
673 for (
size_t j = 0; j <
m_kk; j++) {
682 double daAlphadT = 0.0, k, Tc, sqtTr, coeff1, coeff2;
683 for (
size_t i = 0; i <
m_kk; i++) {
687 coeff1 = 1 / (Tc*sqtTr);
690 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
693 for (
size_t i = 0; i <
m_kk; i++) {
694 for (
size_t j = 0; j <
m_kk; j++) {
696 * m_aAlpha_binary(i, j)
697 * (m_dalphadT[i] / m_alpha[i] + m_dalphadT[j] / m_alpha[j]);
705 for (
size_t i = 0; i <
m_kk; i++) {
708 double coeff1 = 1 / (Tcrit_i*sqt_Tr);
709 double coeff2 = sqt_Tr - 1;
711 double k = m_kappa[i];
712 m_dalphadT[i] = coeff1 * (k*k*coeff2 - k);
713 m_d2alphadT2[i] = (k*k + k) * coeff1 / (2*sqt_Tr*sqt_Tr*Tcrit_i);
717 double d2aAlphadT2 = 0.0;
718 for (
size_t i = 0; i <
m_kk; i++) {
719 double alphai = m_alpha[i];
720 for (
size_t j = 0; j <
m_kk; j++) {
721 double alphaj = m_alpha[j];
722 double alphaij = alphai * alphaj;
723 double term1 = m_d2alphadT2[i] / alphai + m_d2alphadT2[j] / alphaj;
724 double term2 = 2 * m_dalphadT[i] * m_dalphadT[j] / alphaij;
725 double term3 = m_dalphadT[i] / alphai + m_dalphadT[j] / alphaj;
727 * m_aAlpha_binary(i, j)
728 * (term1 + term2 - 0.5 * term3 * term3);
734void PengRobinson::calcCriticalConditions(
double& pc,
double& tc,
double& vc)
const
754 double Vroot[3])
const
759 double aAlpha_p = aAlpha / pres;
761 double bn = (b - RT_p);
762 double cn = -(2 * RT_p * b - aAlpha_p + 3 * bsqr);
763 double dn = (bsqr * RT_p + bsqr * b - aAlpha_p * b);
770 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.
double convert(const std::string &key, const std::string &units) const
Convert the item stored by the given key to the units specified in units.
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.
static AnyMap fromYamlFile(const std::string &name, const std::string &parent_name="")
Create an AnyMap from a YAML file.
void resize(size_t n, size_t m, double v=0.0)
Resize the array, and fill the new entries with 'v'.
Base class for exceptions thrown by Cantera classes.
vector_fp m_cp0_R
Temporary storage for dimensionless reference state heat capacities.
virtual bool addSpecies(shared_ptr< Species > spec)
doublereal z() const
Calculate the value of z.
virtual void _updateReferenceStateThermo() const
Updates the reference state thermodynamic functions at the current T of the solution.
virtual void getStandardVolumes(doublereal *vol) const
Get the molar volumes of each species in their standard states at the current T and P 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.
virtual doublereal psatEst(doublereal TKelvin) const
Estimate for the saturation pressure.
virtual double critDensity() const
Critical density (kg/m3).
virtual void setTemperature(const doublereal temp)
Set the temperature of the phase.
vector_fp moleFractions_
Storage for the current values of the mole fractions of the species.
virtual double critTemperature() const
Critical temperature (K).
virtual void getGibbs_ref(doublereal *g) const
Returns the vector of the Gibbs function of the reference state at the current temperature of the sol...
vector_fp m_tmpV
Temporary storage - length = m_kk.
virtual double critPressure() const
Critical pressure (Pa).
virtual void getEnthalpy_RT_ref(doublereal *hrt) const
An error indicating that an unimplemented function has been called.
virtual double cv_mole() const
Molar heat capacity at constant volume. Units: J/kmol/K.
virtual bool addSpecies(shared_ptr< Species > spec)
void setSpeciesCoeffs(const std::string &species, double a, double b, double w)
Set the pure fluid interaction parameters for a species.
virtual double standardConcentration(size_t k=0) const
Returns the standard concentration , which is used to normalize the generalized concentration.
virtual double densityCalc(double T, double pressure, int phase, double rhoguess)
Calculates the density given the temperature and the pressure and a guess at the density.
virtual double pressure() const
Return the thermodynamic pressure (Pa).
static const double omega_b
Omega constant: b0 (= omega_b) used in Peng-Robinson equation of state.
virtual void getPartialMolarEnthalpies(double *hbar) const
Returns an array of partial molar enthalpies for the species in the mixture.
vector_fp m_pp
Temporary storage - length = m_kk.
virtual double densSpinodalLiquid() const
Return the value of the density at the liquid spinodal point (on the liquid side) for the current tem...
virtual void getActivityCoefficients(double *ac) const
Get the array of non-dimensional activity coefficients at the current solution temperature,...
virtual double dpdVCalc(double T, double molarVol, double &presCalc) const
Calculate the pressure and the pressure derivative given the temperature and the molar volume.
virtual double cp_mole() const
Molar heat capacity at constant pressure. Units: J/kmol/K.
std::vector< CoeffSource > m_coeffSource
For each species, specifies the source of the a, b, and omega coefficients.
double m_dpdT
The derivative of the pressure with respect to the temperature.
PengRobinson(const std::string &infile="", const std::string &id="")
Construct and initialize a PengRobinson object directly from an input file.
virtual void updateMixingExpressions()
Update the , , and parameters.
virtual void getChemPotentials(double *mu) const
Get the species chemical potentials. Units: J/kmol.
virtual double liquidVolEst(double T, double &pres) const
Estimate for the molar volume of the liquid.
virtual double densSpinodalGas() const
Return the value of the density at the gas spinodal point (on the gas side) for the current temperatu...
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.
std::map< std::string, std::map< std::string, double > > m_binaryParameters
Explicitly-specified binary interaction parameters, to enable serialization.
virtual void initThermo()
Initialize the ThermoPhase object after all species have been set up.
double daAlpha_dT() const
Calculate temperature derivative .
void calculatePressureDerivatives() const
Calculate and at the current conditions.
virtual void getPartialMolarIntEnergies(double *ubar) const
Return an array of partial molar internal energies for the species in the 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.
void setBinaryCoeffs(const std::string &species_i, const std::string &species_j, double a)
Set values for the interaction parameter between two species.
static const double omega_vc
Omega constant for the critical molar volume.
virtual void getPartialMolarVolumes(double *vbar) const
Return an array of partial molar volumes for the species in the mixture.
void calculateAB(double &aCalc, double &bCalc, double &aAlpha) const
Calculate the , , and parameters given the temperature.
virtual double sresid() const
Calculate the deviation terms for the total entropy of the mixture from the ideal gas mixture.
double m_b
Value of in the equation of state.
virtual void getSpeciesParameters(const std::string &name, AnyMap &speciesNode) const
Get phase-specific parameters of a Species object such that an identical one could be reconstructed a...
vector_fp m_dpdni
Vector of derivatives of pressure with respect to mole number.
virtual double hresid() const
Calculate the deviation terms for the total enthalpy of the mixture from the ideal gas mixture.
double d2aAlpha_dT2() const
Calculate second derivative .
virtual void getPartialMolarEntropies(double *sbar) const
Returns an array of partial molar entropies of the species in the solution.
virtual void getPartialMolarCp(double *cpbar) const
Calculate species-specific molar specific heats.
double m_dpdV
The derivative of the pressure with respect to the volume.
virtual double speciesCritTemperature(double a, double b) const
Calculate species-specific critical temperature.
vector_fp m_acentric
acentric factor for each species, length m_kk
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
void checkSpeciesIndex(size_t k) const
Check that the specified species index is in range.
std::string name() const
Return the name of the phase.
size_t m_kk
Number of species in the phase.
doublereal meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
double moleFraction(size_t k) const
Return the mole fraction of a single species.
doublereal temperature() const
Temperature (K).
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
double molarVolume() const
Molar volume (m^3/kmol).
shared_ptr< Species > species(const std::string &name) const
Return the Species object for the named species.
doublereal RT() const
Return the Gas Constant multiplied by the current temperature.
void initThermoFile(const std::string &inputFile, const std::string &id)
virtual doublereal refPressure() const
Returns the reference pressure in Pa.
virtual void getSpeciesParameters(const std::string &name, AnyMap &speciesNode) const
Get phase-specific parameters of a Species object such that an identical one could be reconstructed a...
double convert(double value, const std::string &src, const std::string &dest) const
Convert value from the units of src to the units of dest.
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
const double Sqrt2
Sqrt(2)
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
const double SmallNumber
smallest number to compare to zero.
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
const double GasConstant
Universal Gas Constant [J/kmol/K].
Contains declarations for string manipulation functions within Cantera.