48 double h_nonideal =
hresid();
49 return h_ideal + h_nonideal;
57 double s_nonideal =
sresid();
58 return s_ideal + s_nonideal;
68 for (
size_t k = 0; k <
m_kk; k++) {
69 g[k] =
RT() * (g[k] + tmp);
83 for (
size_t k = 0; k <
m_kk; k++) {
93 for (
size_t k = 0; k <
m_kk; k++) {
102 for (
size_t i = 0; i <
m_kk; i++) {
116 for (
size_t i = 0; i <
m_kk; i++) {
156 for (
size_t i = 0; i <
m_kk; i++) {
183 updateMixingExpressions();
219 if (
iState_ < FLUID_LIQUID_0) {
224 if (
iState_ >= FLUID_LIQUID_0) {
234 if (
iState_ >= FLUID_LIQUID_0) {
255 updateMixingExpressions();
262 for (
size_t k = 0; k <
m_kk; k++) {
289 double lpr = -0.8734*tt*tt - 3.4522*tt + 4.2918;
290 return pcrit*exp(lpr);
299 int phase,
double rhoguess)
303 if (rhoguess == -1.0) {
305 if (TKelvin > tcrit) {
308 if (phase == FLUID_GAS || phase == FLUID_SUPERCRIT) {
310 }
else if (phase >= FLUID_LIQUID_0) {
312 rhoguess = mmw / lqvol;
322 double molarVolBase = mmw / rhoguess;
323 double molarVolLast = molarVolBase;
328 double molarVolSpinodal = vc;
332 bool gasSide = molarVolBase > vc;
341 for (
int n = 0; n < 200; n++) {
346 double dpdVBase =
dpdVCalc(TKelvin, molarVolBase, presBase);
351 if (dpdVBase >= 0.0) {
352 if (TKelvin > tcrit) {
354 "T > tcrit unexpectedly");
361 if (molarVolBase >= vc) {
362 molarVolSpinodal = molarVolBase;
363 molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
365 molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
368 if (molarVolBase <= vc) {
369 molarVolSpinodal = molarVolBase;
370 molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
372 molarVolBase = 0.5 * (molarVolLast + molarVolSpinodal);
379 if (fabs(presBase-presPa) < 1.0E-30 + 1.0E-8 * presPa) {
385 double dpdV = dpdVBase;
387 dpdV = dpdVBase * 1.5;
392 double delMV = - (presBase - presPa) / dpdV;
393 if ((!gasSide || delMV < 0.0) && fabs(delMV) > 0.2 * molarVolBase) {
394 delMV = delMV / fabs(delMV) * 0.2 * molarVolBase;
397 if (TKelvin < tcrit) {
399 if (delMV < 0.0 && -delMV > 0.5 * (molarVolBase - molarVolSpinodal)) {
400 delMV = - 0.5 * (molarVolBase - molarVolSpinodal);
403 if (delMV > 0.0 && delMV > 0.5 * (molarVolSpinodal - molarVolBase)) {
404 delMV = 0.5 * (molarVolSpinodal - molarVolBase);
409 molarVolLast = molarVolBase;
410 molarVolBase += delMV;
412 if (fabs(delMV/molarVolBase) < 1.0E-14) {
418 if (molarVolBase <= 0.0) {
419 molarVolBase = std::min(1.0E-30, fabs(delMV*1.0E-4));
424 double densBase = 0.0;
428 "Process did not converge");
430 densBase = mmw / molarVolBase;
435void MixtureFugacityTP::updateMixingExpressions()
440 double& densGasGuess,
double& liqGRT,
double& gasGRT)
443 double densLiq =
densityCalc(TKelvin, pres, FLUID_LIQUID_0, densLiqGuess);
444 if (densLiq <= 0.0) {
447 densLiqGuess = densLiq;
452 double densGas =
densityCalc(TKelvin, pres, FLUID_GAS, densGasGuess);
453 if (densGas <= 0.0) {
456 "Error occurred trying to find gas density at (T,P) = {} {}",
461 densGasGuess = densGas;
476 return FLUID_SUPERCRIT;
478 double tmid = tcrit - 100.;
486 double densLiqTmid = mmw / molVolLiqTmid;
487 double densGasTmid = mmw / molVolGasTmid;
488 double densMidTmid = 0.5 * (densLiqTmid + densGasTmid);
489 double rhoMid = rhocrit + (t - tcrit) * (rhocrit - densMidTmid) / (tcrit - tmid);
492 int iStateGuess = FLUID_LIQUID_0;
494 iStateGuess = FLUID_GAS;
496 double molarVol = mmw / rho;
499 double dpdv =
dpdVCalc(t, molarVol, presCalc);
512 double molarVolLiquid;
517 double& molarVolLiquid)
547 double RhoLiquidGood = mw / volLiquid;
548 double RhoGasGood = pres * mw / (
GasConstant * TKelvin);
549 double delGRT = 1.0E6;
550 double liqGRT, gasGRT;
554 double presLiquid = 0.;
556 double presBase = pres;
557 bool foundLiquid =
false;
558 bool foundGas =
false;
560 double densLiquid =
densityCalc(TKelvin, presBase, FLUID_LIQUID_0, RhoLiquidGood);
561 if (densLiquid > 0.0) {
564 RhoLiquidGood = densLiquid;
567 for (
int i = 0; i < 50; i++) {
569 densLiquid =
densityCalc(TKelvin, pres, FLUID_LIQUID_0, RhoLiquidGood);
570 if (densLiquid > 0.0) {
573 RhoLiquidGood = densLiquid;
580 double densGas =
densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
581 if (densGas <= 0.0) {
586 RhoGasGood = densGas;
589 for (
int i = 0; i < 50; i++) {
591 densGas =
densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
595 RhoGasGood = densGas;
601 if (foundGas && foundLiquid && presGas != presLiquid) {
602 pres = 0.5 * (presLiquid + presGas);
605 for (
int i = 0; i < 50; i++) {
606 densLiquid =
densityCalc(TKelvin, pres, FLUID_LIQUID_0, RhoLiquidGood);
607 if (densLiquid <= 0.0) {
611 RhoLiquidGood = densLiquid;
614 densGas =
densityCalc(TKelvin, pres, FLUID_GAS, RhoGasGood);
615 if (densGas <= 0.0) {
619 RhoGasGood = densGas;
622 if (goodGas && goodLiq) {
625 if (!goodLiq && !goodGas) {
626 pres = 0.5 * (pres + presLiquid);
628 if (goodLiq || goodGas) {
629 pres = 0.5 * (presLiquid + presGas);
633 if (!foundGas || !foundLiquid) {
634 warn_user(
"MixtureFugacityTP::calculatePsat",
635 "could not find a starting pressure; exiting.");
638 if (presGas != presLiquid) {
639 warn_user(
"MixtureFugacityTP::calculatePsat",
640 "could not find a starting pressure; exiting");
645 double presLast = pres;
646 double RhoGas = RhoGasGood;
647 double RhoLiquid = RhoLiquidGood;
650 for (
int i = 0; i < 20; i++) {
651 int stab =
corr0(TKelvin, pres, RhoLiquid, RhoGas, liqGRT, gasGRT);
654 delGRT = liqGRT - gasGRT;
655 double delV = mw * (1.0/RhoLiquid - 1.0/RhoGas);
656 double dp = - delGRT *
GasConstant * TKelvin / delV;
658 if (fabs(dp) > 0.1 * pres) {
666 }
else if (stab == -1) {
668 if (presLast > pres) {
669 pres = 0.5 * (presLast + pres);
674 }
else if (stab == -2) {
675 if (presLast < pres) {
676 pres = 0.5 * (presLast + pres);
682 molarVolGas = mw / RhoGas;
683 molarVolLiquid = mw / RhoLiquid;
685 if (fabs(delGRT) < 1.0E-8) {
691 molarVolGas = mw / RhoGas;
692 molarVolLiquid = mw / RhoLiquid;
700 molarVolLiquid = molarVolGas;
722 for (
size_t k = 0; k <
m_kk; k++) {
727 throw CanteraError(
"MixtureFugacityTP::_updateReferenceStateThermo",
728 "negative reference pressure");
736 calcCriticalConditions(pc, tc, vc);
743 calcCriticalConditions(pc, tc, vc);
750 calcCriticalConditions(pc, tc, vc);
757 calcCriticalConditions(pc, tc, vc);
764 calcCriticalConditions(pc, tc, vc);
769void MixtureFugacityTP::calcCriticalConditions(
double& pc,
double& tc,
double& vc)
const
775 double aAlpha, span<double> Vroot,
double an,
776 double bn,
double cn,
double dn,
double tc,
double vc)
const
778 checkArraySize(
"MixtureFugacityTP::solveCubic: Vroot", Vroot.size(), 3);
779 fill(Vroot.begin(), Vroot.end(), 0.0);
782 "negative temperature T = {}", T);
786 double xN = - bn /(3 * an);
789 double deltaNumerator = bn * bn - 3 * an * cn;
790 double delta2 = deltaNumerator / (9 * an * an);
795 double ratio1 = 3.0 * an * cn / (bn * bn);
797 if (fabs(ratio1) < 1.0E-7) {
799 if (fabs(ratio2) < 1.0E-5 && fabs(ratio3) < 1.0E-5) {
802 for (
int i = 0; i < 10; i++) {
803 double znew = zz / (zz - ratio2) - ratio3 / (zz + ratio1);
804 double deltaz = znew - zz;
806 if (fabs(deltaz) < 1.0E-14) {
816 int nSolnValues = -1;
817 double h2 = 4. * an * an * delta2 * delta2 * delta2;
819 delta = sqrt(delta2);
822 double h = 2.0 * an * delta * delta2;
823 double yTerm1 = 2.0 * bn * bn * bn / (27.0 * an * an);
824 double yTerm2 = -bn * cn / (3.0 * an);
825 double yN = yTerm1 + yTerm2 + dn;
826 double disc = yN * yN - h2;
834 double cancellationTol = 64 * std::numeric_limits<double>::epsilon();
835 double deltaScale = max(fabs(bn * bn), fabs(3 * an * cn));
836 double yScale = max({fabs(yTerm1), fabs(yTerm2), fabs(dn)});
837 bool tripleRoot = (fabs(deltaNumerator) <= cancellationTol * deltaScale
838 && fabs(yN) <= cancellationTol * yScale);
845 if (!tripleRoot && fabs(fabs(h) - fabs(yN)) < 1.0E-10) {
848 "value of yN and h are too high, unrealistic roots may be obtained");
856 }
else if (tripleRoot) {
861 }
else if (fabs(disc) < 1e-14) {
865 }
else if (disc > 1e-14) {
871 auto physicalRoot = [b](
double v) {
873 double vmin = std::max(0.0, b * (1.0 + 1e-12));
874 return std::isfinite(v) && v > vmin;
878 double tmpD = sqrt(disc);
879 double tmp1 = (- yN + tmpD) / (2.0 * an);
885 double tmp2 = (- yN - tmpD) / (2.0 * an);
891 double p1 = pow(tmp1, 1./3.);
892 double p2 = pow(tmp2, 1./3.);
893 double alpha = xN + sgn1 * p1 + sgn2 * p2;
897 }
else if (disc < 0.0) {
899 double val = acos(-yN / h);
900 double theta = val / 3.0;
901 double twoThirdPi = 2. *
Pi / 3.;
902 double alpha = xN + 2. * delta * cos(theta);
903 double beta = xN + 2. * delta * cos(theta + twoThirdPi);
904 double gamma = xN + 2. * delta * cos(theta + 2.0 * twoThirdPi);
909 for (
int i = 0; i < 3; i++) {
910 tmp = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
911 if (fabs(tmp) > 1.0E-4) {
912 for (
int j = 0; j < 3; j++) {
913 if (j != i && fabs(Vroot[i] - Vroot[j]) < 1.0E-4 * (fabs(Vroot[i]) + fabs(Vroot[j]))) {
914 warn_user(
"MixtureFugacityTP::solveCubic",
915 "roots have merged for T = {}, p = {}: {}, {}",
916 T, pres, Vroot[i], Vroot[j]);
921 }
else if (disc == 0.0) {
929 tmp = cbrt(yN / (2 * an));
931 if (fabs(tmp - delta) > 1.0E-9) {
933 "Inconsistency in solver: solver is ill-conditioned.");
935 Vroot[1] = xN + delta;
936 Vroot[0] = xN - 2.0*delta;
938 tmp = cbrt(yN / (2 * an));
941 if (fabs(tmp + delta) > 1.0E-9) {
943 "Inconsistency in solver: solver is ill-conditioned.");
946 Vroot[0] = xN + delta;
947 Vroot[1] = xN - 2.0*delta;
953 double res, dresdV = 0.0;
954 for (
int i = 0; i < nSolnValues; i++) {
955 if (!physicalRoot(Vroot[i])) {
960 for (
int n = 0; n < 20; n++) {
961 res = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
962 if (fabs(res) < 1.0E-14) {
965 dresdV = 3.0 * an * Vroot[i] * Vroot[i] + 2.0 * bn * Vroot[i] + cn;
966 double del = - res / dresdV;
968 if (fabs(del) / (fabs(Vroot[i]) + fabs(del)) < 1.0E-14) {
971 double res2 = an * Vroot[i] * Vroot[i] * Vroot[i] + bn * Vroot[i] * Vroot[i] + cn * Vroot[i] + dn;
972 if (fabs(res2) < fabs(res)) {
976 Vroot[i] += 0.1 * del;
979 if ((fabs(res) > 1.0E-14) && (fabs(res) > 1.0E-14 * fabs(dresdV) * fabs(Vroot[i]))) {
981 "root failed to converge for T = {}, p = {} with "
982 "V = {}", T, pres, Vroot[i]);
985 if (nSolnValues == 1 && !physicalRoot(Vroot[0])) {
987 "single real root is non-physical for T = {}, p = {} "
988 "(V = {}, b = {})", T, pres, Vroot[0], b);
991 if (nSolnValues == 1) {
1000 if (Vroot[0] < xN) {
1008 if (nSolnValues == 2 && delta > 1e-14) {
Header file for a derived class of ThermoPhase that handles non-ideal mixtures based on the fugacity ...
#define FLUID_UNSTABLE
Various states of the Fugacity object.
Base class for exceptions thrown by Cantera classes.
int iState_
Current state of the fluid.
void getGibbs_ref(span< double > g) const override
Returns the vector of the Gibbs function of the reference state at the current temperature of the sol...
int reportSolnBranchActual() const
Report the solution branch which the solution is actually on.
double enthalpy_mole() const override
Molar enthalpy. Units: J/kmol.
void getCp_R(span< double > cpr) const override
Get the nondimensional Heat Capacities at constant pressure for the standard state of the species at ...
int standardStateConvention() const override
This method returns the convention used in specification of the standard state, of which there are cu...
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...
vector< double > m_g0_RT
Temporary storage for dimensionless reference state Gibbs energies.
void getIntEnergy_RT(span< double > urt) const override
Returns the vector of nondimensional internal Energies of the standard state at the current temperatu...
double critPressure() const override
Critical pressure (Pa).
double critDensity() const override
Critical density (kg/m3).
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).
void getGibbs_RT(span< double > grt) const override
Get the nondimensional Gibbs functions for the species at their standard states of solution at the cu...
void getCp_R_ref(span< double > cprt) const override
Returns the vector of nondimensional constant pressure heat capacities of the reference state at the ...
virtual void _updateReferenceStateThermo() const
Updates the reference state thermodynamic functions at the current T of the solution.
double satPressure(double TKelvin) override
Calculate the saturation pressure at the current mixture content for the given temperature.
double critCompressibility() const override
Critical compressibility (unitless).
void setPressure(double p) override
Set the internally stored pressure (Pa) at constant temperature and composition.
void getEnthalpy_RT_ref(span< double > hrt) const override
Returns the vector of nondimensional enthalpies of the reference state at the current temperature of ...
void getEnthalpy_RT(span< double > hrt) const override
Get the nondimensional Enthalpy functions for the species at their standard states at the current T a...
double calculatePsat(double TKelvin, double &molarVolGas, double &molarVolLiquid)
Calculate the saturation pressure at the current mixture content for the given temperature.
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 getEntropy_R(span< double > sr) const override
Get the array of nondimensional Enthalpy functions for the standard state species at the current T an...
void setTemperature(const double temp) override
Set the temperature of the phase.
vector< double > m_s0_R
Temporary storage for dimensionless reference state entropies.
virtual double dpdVCalc(double TKelvin, double molarVol, double &presCalc) const
Calculate the pressure and the pressure derivative given the temperature and the molar volume.
double entropy_mole() const override
Molar entropy. Units: J/kmol/K.
virtual double densityCalc(double TKelvin, double pressure, int phaseRequested, double rhoguess)
Calculates the density given the temperature and the pressure and a guess at the density.
double critVolume() const override
Critical volume (m3/kmol).
virtual double psatEst(double TKelvin) const
Estimate for the saturation pressure.
void getGibbs_RT_ref(span< double > grt) const override
Returns the vector of nondimensional Gibbs Free Energies of the reference state at the current temper...
virtual double sresid() const
Calculate the deviation terms for the total entropy of the mixture from the ideal gas mixture.
int forcedState_
Force the system to be on a particular side of the spinodal curve.
virtual double liquidVolEst(double TKelvin, double &pres) const
Estimate for the molar volume of the liquid.
int forcedSolutionBranch() const
Report the solution branch which the solution is restricted to.
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...
void compositionChanged() override
Apply changes to the state which are needed after the composition changes.
vector< double > m_cp0_R
Temporary storage for dimensionless reference state heat capacities.
int corr0(double TKelvin, double pres, double &densLiq, double &densGas, double &liqGRT, double &gasGRT)
Utility routine in the calculation of the saturation pressure.
void setForcedSolutionBranch(int solnBranch)
Set the solution branch to force the ThermoPhase to exist on one branch or another.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
virtual double hresid() const
Calculate the deviation terms for the total enthalpy of the mixture from the ideal gas mixture.
void getActivityConcentrations(span< double > c) const override
This method returns an array of generalized concentrations.
double z() const
Calculate the value of z.
void getStandardVolumes_ref(span< double > vol) const override
Get the molar volumes of the species reference states at the current T and P_ref of the solution.
int phaseState(bool checkState=false) const
Returns the Phase State flag for the current state of the object.
virtual void update(double T, span< double > cp_R, span< double > h_RT, span< double > s_R) const
Compute the reference-state properties for all species.
An error indicating that an unimplemented function has been called.
void getMoleFractions(span< double > x) const
Get the species mole fraction vector.
size_t m_kk
Number of species in the phase.
virtual void setState_TD(double t, double rho)
Set the internally stored temperature (K) and density (kg/m^3)
double temperature() const
Temperature (K).
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
virtual void setDensity(const double density_)
Set the internally stored density (kg/m^3) of the phase.
double sum_xlogx() const
Evaluate .
double mean_X(span< const double > Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
double moleFraction(size_t k) const
Return the mole fraction of a single species.
virtual double density() const
Density (kg/m^3).
virtual void compositionChanged()
Apply changes to the state which are needed after the composition changes.
virtual void setTemperature(double temp)
Set the internally stored temperature of the phase (K).
virtual double pressure() const
Return the thermodynamic pressure (Pa).
virtual void setState_TP(double t, double p)
Set the temperature (K) and pressure (Pa)
double RT() const
Return the Gas Constant multiplied by the current temperature.
double m_tlast
last value of the temperature processed by reference state
MultiSpeciesThermo m_spthermo
Pointer to the calculation manager for species reference-state thermodynamic properties.
virtual double refPressure() const
Returns the reference pressure in Pa.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
virtual double gibbs_mole() const
Molar Gibbs function. Units: J/kmol.
virtual void getActivityCoefficients(span< double > ac) const
Get the array of non-dimensional molar-based activity coefficients at the current solution temperatur...
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
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].
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Namespace for the Cantera kernel.
const int cSS_CONVENTION_TEMPERATURE
Standard state uses the molar convention.
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...