From b18517c26bd8a3794cfb6b7483514731ed790de6 Mon Sep 17 00:00:00 2001 From: Jongyoon Bae Date: Fri, 29 Oct 2021 21:44:24 -0400 Subject: [PATCH] commit prior to rebasing --- .../thermo/CoverageDependentSurfPhase.h | 601 ++++++++++------ src/thermo/CoverageDependentSurfPhase.cpp | 657 ++++++++++++------ src/thermo/ThermoFactory.cpp | 4 +- 3 files changed, 858 insertions(+), 404 deletions(-) diff --git a/include/cantera/thermo/CoverageDependentSurfPhase.h b/include/cantera/thermo/CoverageDependentSurfPhase.h index abce0d842b6..cb1794f6f64 100644 --- a/include/cantera/thermo/CoverageDependentSurfPhase.h +++ b/include/cantera/thermo/CoverageDependentSurfPhase.h @@ -18,89 +18,79 @@ namespace Cantera { -//! Modifications to an SurfPhase thermodynamic quantities based on a surface species -//! coverage with a linear model. -struct LinearDependency +//! Set of parameters modifying SurfPhase enthalpy and entropy based on fractional surface coverages +//! using a polynomial model. Linear model is a subset of the polynomial model. +struct PolynomialDependency { //! Constructor //! @param name_k_ name of species which thermodynamics are calculated for [integer] //! @param name_j_ name of species which thermodynamics are dependent on [integer] - //! @param enthalpy_ slope of enthalpy as a function of coverage [J/kmol] - //! @param entropy_ slope of entropy as a function of coverage [J/kmol/K] - - LinearDependency(std::string name_k_, std::string name_j_, - double enthalpy_, double entropy_): - name_k(name_k_), name_j(name_j_), - enthalpy(enthalpy_), entropy(entropy_) {} - LinearDependency() {} + /*! @param enthalpy_coeffs_ array of polynomial coefficients describing enthalpy change + * containing 1st-order, 2nd-order, 3rd-order, and 4th-order + * coefficients as a function of coverage [J/kmol] + */ + /*! @param entropy_coeffs_ array of polynomial coefficients describing entropy change + * containing 1st-order, 2nd-order, 3rd-order, and 4th-order + * coefficients as a function of coverage [J/kmol/K] + */ + PolynomialDependency(std::string name_k_, std::string name_j_, + vector_fp enthalpy_coeffs_, vector_fp entropy_coeffs_): + name_k(name_k_), name_j(name_j_), + enthalpy_coeffs(enthalpy_coeffs_), + entropy_coeffs(entropy_coeffs_) {} + PolynomialDependency() {} std::string name_k; //!< name of species which thermodynamics are calculated for [integer] std::string name_j; //!< name of species which thermodynamics are dependent on [integer] - double enthalpy; //!< slope of enthalpy as a function of coverage [J/kmol] - double entropy; //!< slope of entropy as a function of coverage [J/kmol/K] + vector_fp enthalpy_coeffs; /*!< array of polynomial coefficients describing enthalpy change + * containing 1st-order, 2nd-order, 3rd-order, and 4th-order + * coefficients as a function of coverage [J/kmol] + */ + vector_fp entropy_coeffs; /*!< array of polynomial coefficients describing entropy change + * containing 1st-order, 2nd-order, 3rd-order, and 4th-order + * coefficients as a function of coverage [J/kmol/K] + */ }; -//! Modifications to an SurfPhase thermodynamic quantities based on a surface species -//! coverage with a piecewise linear model. +//! Set of parameters modifying SurfPhase enthalpy and entropy based on fractional surface coverages +//! using a piecewise linear model. struct PiecewiseDependency { //! Constructor //! @param name_k_ name of species which thermodynamics are calculated for [integer] //! @param name_j_ name of species which thermodynamics are dependent on [integer] - /*! @param enthalpy_low_ slope of enthalpy for the first linear region - * as a function of coverage [J/kmol] - */ - /*! @param enthalpy_high_ slope of enthalpy for the second linear region - * as a function of coverage [J/kmol] - */ - /*! @param enthalpy_change_ coverage where the enthalpy depedency - * slope changes [dimensionless] - */ - /*! @param entropy_low_ slope of entropy for the first linear region - * as a function of coverage [J/kmol/K] + /*! @param enthalpy_params_ array of three parameters to calculate coverage-dependent enthalpy: + * slope of enthalpy change in the first region [J/kmol], slope of + * enthalpy change in the second region [J/kmol], and coverage + * dividing first and second region [dimensionless] */ - /*! @param entropy_high_ slope of entropy for the second linear region - * as a function of coverage [J/kmol/K] - */ - /*! @param entropy_change_ coverage where the entropy depedency - * slope changes [dimensionless] + /*! @param entropy_params_ array of three parameters to calculate coverage-dependent entropy: + * slope of entropy change in the first region [J/kmol/K], slope of + * entropy change in the second region [J/kmol/K], and coverage + * dividing first and second region [dimensionless] */ PiecewiseDependency(std::string name_k_, std::string name_j_, - double enthalpy_low_, double enthalpy_high_, - double enthalpy_change_, double entropy_low_, - double entropy_high_, double entropy_change_): + vector_fp enthalpy_params_, vector_fp entropy_params_): name_k(name_k_), name_j(name_j_), - enthalpy_low(enthalpy_low_), - enthalpy_high(enthalpy_high_), - enthalpy_change(enthalpy_change_), - entropy_low(entropy_low_), - entropy_high(entropy_high_), - entropy_change(entropy_change_) {} + enthalpy_params(enthalpy_params_), + entropy_params(entropy_params_) {} PiecewiseDependency() {} std::string name_k; //!< name of species which thermodynamics are calculated for [integer] std::string name_j; //!< name of species which thermodynamics are dependent on [integer] - double enthalpy_low; /*!< slope of enthalpy for the first linear region - * as a function of coverage [J/kmol] - */ - double enthalpy_high; /*!< slope of enthalpy for the second linear region - * as a function of coverage [J/kmol] - */ - double enthalpy_change; /*!< coverage where the enthalpy depedency - * slope changes [dimensionless] - */ - double entropy_low; /*!< slope of entropy for the first linear region - * as a function of coverage [J/kmol] - */ - double entropy_high; /*!< slope of entropy for the second linear region - * as a function of coverage [J/kmol] - */ - double entropy_change; /*!< coverage where the entropy depedency - * slope changes [dimensionless] - */ + vector_fp enthalpy_params; /*!< array of three parameters to calculate coverage-dependent enthalpy: + * slope of enthalpy change in the first region [J/kmol], slope of + * enthalpy change in the second region [J/kmol], and coverage + * dividing first and second region [dimensionless] + */ + vector_fp entropy_params; /*!< array of three parameters to calculate coverage-dependent entropy: + * slope of entropy change in the first region [J/kmol/K], slope of + * entropy change in the second region [J/kmol/K], and coverage + * dividing first and second region [dimensionless] + */ }; -//! Modifications to an SurfPhase thermodynamic quantities based on a surface species -//! coverage with a interpolative model. +//! Set of parameters modifying SurfPhase enthalpy and entropy based on fractional surface coverages +//! using a interpolative model. struct InterpolativeDependency { //! Constructor @@ -110,13 +100,13 @@ struct InterpolativeDependency * enthalpy interpolation [dimensionless] */ /*! @param enthalpies_ array of enthalpies at corresponding coverages - * in enthalpy-coverage [J/kmol] + * in enthalpy-coverages [J/kmol] */ /*! @param entropy_coverages_ array of coverages for coverage-dependent * entropy interpolation [dimensionless] */ /*! @param entropies_ array of entropies at corresponding coverages - * in entropy-coverage [J/kmol/K] + * in entropy-coverages [J/kmol/K] */ InterpolativeDependency(std::string name_k_, std::string name_j_, @@ -131,35 +121,56 @@ struct InterpolativeDependency std::string name_k; //!< name of species which thermodynamics are calculated for [integer] std::string name_j; //!< name of species which thermodynamics are dependent on [integer] vector_fp enthalpy_coverages; /*!< array of coverages for coverage-dependent - * enthalpy interpolation [dimensionless] - */ + * enthalpy interpolation [dimensionless] + */ vector_fp enthalpies; /*!< array of enthalpies at corresponding coverages - * in enthalpy-coverage [J/kmol] + * in enthalpy-coverages [J/kmol] */ vector_fp entropy_coverages; /*!< array of coverages for coverage-dependent * entropy interpolation [dimensionless] */ vector_fp entropies; /*!< array of entropies at corresponding coverages - * enthalpy interpolation [J/kmol/K] + * in entropy-coverages [J/kmol/K] */ }; +//! Set of parameters modifying SurfPhase heat capacity based on fractional surface coverages +//! using a quadratic model. +struct HeatCapacityDependency +{ + //! Constructor + //! @param name_k_ name of species which thermodynamics are calculated for [integer] + //! @param name_j_ name of species which thermodynamics are dependent on [integer] + //! @param cpcov_a_ log model coefficient a [J/kmol/K] + //! @param cpcov_b_ log model coefficient b [J/kmol/K] + + HeatCapacityDependency(std::string name_k_, std::string name_j_, + double cpcov_a_, double cpcov_b_): + name_k(name_k_), name_j(name_j_), + cpcov_a(cpcov_a_), cpcov_b(cpcov_b_) {} + HeatCapacityDependency() {} + std::string name_k; //!< name of species which thermodynamics are calculated for [integer] + std::string name_j; //!< name of species which thermodynamics are dependent on [integer] + double cpcov_a; //!< log model coefficient a [J/kmol/K] + double cpcov_b; //!< log model coefficient b [J/kmol/K] +}; + class CoverageDependentSurfPhase : public SurfPhase { public: //! Constructor. /*! * @param n0 Site Density of the Surface Phase - * Units: kmol m-2. + * Units: kmol m^-2. */ CoverageDependentSurfPhase(doublereal n0 = 1.0); - //! Construct and initialize a SurfPhase ThermoPhase object directly from an - //! ASCII input file + //! Construct and initialize a CoverageDependentSurfPhase ThermoPhase object directly + //! from an ASCII input file /*! * @param infile name of the input file - * @param id name of the phase id in the file. - * If this is blank, the first phase in the file is used. + * @param id name of the phase id in the file + * If this is blank, the first phase in the file is used */ explicit CoverageDependentSurfPhase(const std::string& infile, const std::string& id); @@ -173,100 +184,92 @@ class CoverageDependentSurfPhase : public SurfPhase */ CoverageDependentSurfPhase(XML_Node& xmlphase); - virtual std::string type() const { - return "CovDepSurf"; - } - - //! Set the linear coverage dependece for species + //! Set the polynomial coverage dependece for species /*! - * coverage-dependent enthalpy and entropy are sum of ideal surface species - * thermodyanamics corrected by the dependent quantities those caclulate with - * a linear function of coverages: + * enthalpy and entropy are sum of ideal surface species enthalpy and entropy + * and coverage-dependent enthalpy and entropy which are caclulated with + * a polynomial function of coverages: * - * \f[ \hat h^{cov}_k(\theta) = \sum_j \hat h^{slope}_j \theta_j \f] - * \f[ \hat s^{cov}_k(\theta) = \sum_j \hat s^{slope}_j \theta_j \f] - * - * @param lin_deps list of parameters as a LinearDependency object + * For the linear depedence, + * \f[ h^{cov}_k(\theta) = \sum_j h^{slope}_{k,j} \theta_j \f] + * \f[ s^{cov}_k(\theta) = \sum_j s^{slope}_{k,j} \theta_j \f] + * + * For the polynomial dependence, + * \f[ h^{cov}_k(\theta) = + * \sum_j (h^{1st-order}_{k,j} \theta_j + h^{2nd-order}_{k,j} \theta_j^2 + * + h^{3rd-order}_{k,j} \theta_j^3 + h^{4th-order}_{k,j} \theta_j^4) + * \f] + * \f[ s^{cov}_k(\theta) = + * \sum_j (s^{1st-order}_{k,j} \theta_j + s^{2nd-order}_{k,j} \theta_j^2 + * + s^{3rd-order}_{k,j} \theta_j^3 + s^{4th-order}_{k,j} \theta_j^4) + * \f] + * @param poly_deps list of parameters as a PolynomialDependency object */ - void setLinearDependency(const LinearDependency& lin_deps); + void setPolynomialDependency(const PolynomialDependency& poly_deps); - //! Set the picewise linear coverage dependece for species + //! Set the piecewise linear coverage dependece for species /*! - * coverage-dependent enthalpy and entropy are sum of ideal surface species - * thermodyanamics corrected by the dependent quantities those caclulate with + * enthalpy and entropy are sum of ideal surface species enthalpy and entropy + * and coverage-dependent enthalpy and entropy which are caclulated with * a piecewise linear function of coverages: * - * \f[ \hat h^{cov}_k(\theta) = \sum_j \hat h^{slope}_j \theta_j \f] - * where \f[ \hat h^{slope}_j = \hat h^{slope1}_j \f] - * if \f[ \theta_j \leq \theta^h_{change} \f] or - * \f[ \hat h^{slope}_j = \hat h^{slope2}_j \f] - * if \f[ \theta_j > \theta^h_{change} \f] + * \f[ h^{cov}_k(\theta) = \sum_j h^{low}_{k,j} \theta_j + * \text{, for } \theta_j \leq \theta^{change}_{k,j} + * \f] + * \f[ h^{cov}_k(\theta) = \sum_j h^{high}_{k,j} + * (\theta_j - \theta^{change}_{k,j}) + + * (h^{low}_{k,j} \theta^{change}_{k,j}) \text{, for } + * \theta_j > \theta^{change}_{k,j} + * \f] * - * \f[ \hat s^{cov}_k(\theta) = \sum_j \hat s^{slope}_j \theta_j \f] - * where \f[ \hat s^{slope}_j = \hat s^{slope1}_j \f] - * if \f[ \theta_j \leq \theta^h_{change} \f] or - * \f[ \hat s^{slope}_j = \hat s^{slope2}_j \f] - * if \f[ \theta_j > \theta^s_{change} \f] - * + * \f[ s^{cov}_k(\theta) = \sum_j s^{low}_{k,j} \theta_j + * \text{, for } \theta_j \leq \theta^\text{change}_{k,j} + * \f] + * \f[ s^{cov}_k(\theta) = \sum_j s^{high}_{k,j} + * (\theta_j - \theta^{change}_{k,j}) + + * (s^{low}_{k,j} \theta^{change}_{k,j}) \text{, for } + * \theta_j > \theta^{change}_{k,j} + * \f] * @param plin_deps list of parameters as a PiecewiseDependency object */ void setPiecewiseDependency(const PiecewiseDependency& plin_deps); //! Set the interpolative coverage dependece for species /*! - * coverage-dependent enthalpy and entropy are sum of ideal surface species - * thermodyanamics corrected by the dependent quantities those caclulate with + * enthalpy and entropy are sum of ideal surface species enthalpy and entropy + * and coverage-dependent enthalpy and entropy which are caclulated with * a linearly interpolated function of coverages: * - * \f[ \hat h^{cov}_k(\theta) = \sum_j \hat h^{interpolate}_j \f] - * where \f[ \theta^1_j \leq \theta_j \leq \theta^2_j \f] - * then \f[ \hat h^{interpolate}_j = - * \frac{\hat h^2_j - \hat h^1_j}{\theta^2_j - \theta^1_j} \theta_j - * + \hat h^1_j \f] + * \f[ h^{cov}_k(\theta) = \sum_j \frac{h^{right}_{k,j} + * - h^{left}_{k,j}}{\theta^{right}_{k,j} - \theta^{left}_{k,j}} + * (\theta_j - \theta^{left}_{k,j}) + h^{left}_{k,j} + * \text{, where } \theta^{left}_{k,j} \leq \theta_j < \theta^{right}_{k,j} + * \f] * - * \f[ \hat s^{cov}_k(\theta) = \sum_j \hat s^{interpolate}_j \f] - * where \f[ \theta^1_j \leq \theta_j \leq \theta^2_j \f] - * then \f[ \hat s^{interpolate}_j = - * \frac{\hat s^2_j - \hat s^1_j}{\theta^2_j - \theta^1_j} \theta_j - * + \hat s^1_j \f] - * + * \f[ s^{cov}_k(\theta) = \sum_j \frac{s^{right}_{k,j} + * - s^{left}_{k,j}}{\theta^{right}_{k,j} - \theta^{left}_{k,j}} + * (\theta_j - \theta^{left}_{k,j}) + s^{left}_{k,j} + * \text{, where } \theta^{left}_{k,j} \leq \theta_j < \theta^{right}_{k,j} + * \f] + * * @param int_deps list of parameters as an InterpolativeDependency object */ void setInterpolativeDependency(const InterpolativeDependency& int_deps); - virtual void initThermo(); - virtual bool addSpecies(shared_ptr spec); - - //! Return the Molar Enthalpy. Units: J/kmol. + //! Set the heat capacity coverage dependece for species /*! - * For an ideal solution, - * \f[ - * \hat h(T,P,\theta) = \sum_k X_k (\hat h^0_k(T) + \hat h^{cov}_k(\theta_k)), - * \f] - * and is a function of temperature and coverage. The standard-state - * pure-species Enthalpies \f$ \hat h^0_k(T) \f$ are computed by the species - * thermodynamic property manager. The coverage-dependent pure-species - * Enthalpies \f$ \hat h^{cov}_k(T) \f$ are calculated by one of the three - * models: linear, piecewise linear, or interpolative model. + * heat capacity is sum of ideal surface species heat capacity and coverage-dependent + * heat capacity which is caculated using a function with quadratic depedence on coverages + * and a logarithimic dependence on temperature: + * + * \f[ cp^{cov}_k(\theta) = \sum_j (a_{k,j} ln(T) + b_{k,j}) \theta_j^2 \f] * - * \see MultiSpeciesThermo + * @param cpcov_deps list of parameters as a HeatCapacityDependency object */ - virtual doublereal enthalpy_mole() const; - - //! Return the Molar Entropy. Units: J/kmol-K - /** - * \f[ - * \hat s(T,P,\theta) = \sum_k X_k (\hat s^0_k(T) \hat h^{cov}_k(\theta_k) - * - R \log(\theta_k)) - * \f] - */ - virtual doublereal entropy_mole() const; + void setHeatCapacityDependency(const HeatCapacityDependency& cpcov_deps); - virtual void getChemPotentials(doublereal* mu) const; - virtual void getGibbs_RT(doublereal* grt) const; - virtual void getEnthalpy_RT(doublereal* hrt) const; - virtual void getEntropy_R(doublereal* sr) const; - virtual void getPureGibbs(doublereal* g) const; + virtual void initThermo(); + virtual bool addSpecies(shared_ptr spec); //! @copydoc SurfPhase::setCoverages /*! @@ -285,20 +288,198 @@ class CoverageDependentSurfPhase : public SurfPhase return m_covstateNum; } - //! Convert given enthalpy unit to its default unit of J/kmol. - double convertEnthalpy(double value, const std::string& src) const; + //! Convert given eneregy to the quantity with its default unit of J/kmol. + double convertEnergy(double value, const std::string& src) const; + + //! Convert given energy per temperature to the quantity with its default unit of J/kmol/K. + double convertEnergy_T(double value, const std::string& src) const; + + // Functions calculating reference state thermodyanmic properties-------------- + + //! Return the nondimensionalized reference state enthalpy. + /*! + * Nondimensionalized reference state enthalpy is evaluated at T, P_{ref} and \theta_{ref}: + * + * \f[ + * h^{ref}_k / RT = h_k(T, P_{ref}, \theta_{ref}) / RT + * \f] + */ + virtual void getEnthalpy_RT_ref(doublereal* hrt) const; + //! Return the nondimensionalized reference state entropy. + /*! + * Nondimensionalized reference state entropy is evaluated at T, P_{ref} and \theta_{ref}: + * + * \f[ + * s^{ref}_k / R = s_k(T, P_{ref}, \theta_\text{ref}) / R + * \f] + */ + virtual void getEntropy_R_ref(doublereal* sr) const; + //! Return the nondimensionalized reference state cp. + /*! + * Nondimensionalized reference state cp is evaluated at T, P_{ref} and \theta_{ref}: + * + * \f[ + * cp^{ref}_k / R = cp_k(T, P_{ref}, \theta_\text{ref}) / R + * \f] + */ + virtual void getCp_R_ref(doublereal* cpr) const; + //! Return the nondimensionalized reference state Gibbs free energy. + /*! + * \f[ + * g^{ref}_k / RT = h^{ref}_k /RT - s^{ref}_k /R + * \f] + */ + virtual void getGibbs_RT_ref(doublereal* grt) const; - //! Convert given entropy unit to its default unit of J/kmol/K. - double convertEntropy(double value, const std::string& src) const; + // Functions calculating standard state thermodyanmic properties--------------- + + //! Return the nondimensionalized standard state enthalpy. + /*! + * Nondimensionalized standard state enthalpy is evaluated at T, P and \theta: + * + * \f[ + * h^o_k / RT = (h^{ref}_k + h^{cov}_k(T, P, \theta)) / RT + * + \int_{T_{ref}}^{T} cp^{cov}_k(T, P, \theta) dT / RT + * \f] + */ + virtual void getEnthalpy_RT(doublereal* hrt) const; + //! Return the nondimensionalized standard state entropy. + /*! + * Nondimensionalized standard state entropy is evaluated at T, P and \theta: + * + * \f[ + * s^o_k / R = (s^{ref}_k + s^{cov}_k(T, P, \theta)) / R - ln(1 / \theta_{ref}) + * + \int_{T_{ref}}^{T} cp^{cov}_k(T, P, \theta) / T dT / R + * \f] + */ + virtual void getEntropy_R(doublereal* sr) const; + //! Return the nondimensionalized standard state cp. + /*! + * Nondimensionalized standard state cp is evaluated at T, P and \theta: + * + * \f[ + * cp^o_k / RT = (cp^{ref}_k + cp^{cov}_k(T, P, \theta)) / RT + * \f] + */ + virtual void getCp_R(doublereal* cpr) const; + //! Return the nondimensionalized standard state Gibbs free energy. + /*! + * \f[ + * g^o_k / RT = h^o_k / RT - s^o_k / R + * \f] + */ + virtual void getGibbs_RT(doublereal* grt) const; + //! Return the standard state Gibbs free energy. Units: J/kmol + /*! + * \f[ + * g^o_k = h^o_k - T s^o_k + * \f] + */ + virtual void getPureGibbs(doublereal* g) const; + //! Return the standard state chemical potential. Units: J/kmol + /*! + * \f[ + * \mu^o_k = h^o_k - T s^o_k + * \f] + */ + virtual void getStandardChemPotentials(doublereal* mu0) const; + + // Functions calculating partial molar thermodyanmic properties---------------- + + //! Return the partial molar enthalpy. Units: J/kmol + /*! + * Partial molar enthalpy is evaluated at T, P and \theta: + * + * \f[ + * \overline{h}_k = h^o_k(T, P, \theta) + * \f] + */ + virtual void getPartialMolarEnthalpies(doublereal* hbar) const; + //! Return the partial molar entropy. Units: J/kmol/K + /*! + * Partial molar entropy is evaluated at T, P and \theta: + * + * \f[ + * \overline{s}_k = s^o_k(T, P, \theta) - R ln(\theta) + * \f] + */ + virtual void getPartialMolarEntropies(doublereal* sbar) const; + //! Return the partial molar cp. Units: J/kmol/K + /*! + * Partial molar cp is evaluated at T, P and \theta: + * + * \f[ + * \overline{cp}_k = cp^o_k(T, P, \theta) + * \f] + */ + virtual void getPartialMolarCp(doublereal* cpbar) const; + + //! Return the chemical potential. Units: J/kmol + /** + * \f[ + * \mu_k = mu^o_k(T, P, \theta) + RT ln(\theta) + * \f] + */ + virtual void getChemPotentials(doublereal* mu) const; + + // Functions calculating mixture thermodyanmic properties---------------------- + + //! Return the solution's molar enthalpy. Units: J/kmol + /*! + * Asumming an ideal solution, + * \f[ + * \hat h(T, P, \theta) = \sum_k \theta_k \overline{h}_k(T, \theta) + * = \sum_k \theta_k h^o_k(T, \theta) + * \f] + * + * \see MultiSpeciesThermo + */ + virtual doublereal enthalpy_mole() const; + + //! Return the solution's molar entropy. Units: J/kmol/K + /** + * \f[ + * \hat s(T, P, \theta) = \sum_k \theta_k \overline{s}_k(T, \theta) + * = \sum_k \theta_k s^o_k(T, \theta) - R ln(\theta) + * \f] + */ + virtual doublereal entropy_mole() const; + + //! Return the solution's molar cp. Units: J/kmol/K + /*! + * Asumming an ideal solution, + * \f[ + * \hat{cp} (T, P, \theta) = \sum_k \theta_k \overline{cp}_k(T, \theta) + * = \sum_k \theta_k cp^o_k(T, \theta) + * \f] + * + * \see MultiSpeciesThermo + */ + virtual doublereal cp_mole() const; protected: + //! Temporary storage for the reference state enthalpies. + mutable vector_fp m_h_ref; + + //! Temporary storage for the reference state entropies. + mutable vector_fp m_s_ref; + + //! Temporary storage for the reference state heat capacities. + mutable vector_fp m_cp_ref; + + //! Temporary storage for the reference state chemical potentials. + mutable vector_fp m_mu_ref; + //! Temporary storage for the coverage-dependent enthalpies. mutable vector_fp m_h_cov; //! Temporary storage for the coverage-dependent entropies. mutable vector_fp m_s_cov; - //! Temporary storage for the coverage-dependent Gibbs energies. + //! Temporary storage for the coverage-dependent heat capacities. + mutable vector_fp m_cp_cov; + + //! Temporary storage for the coverage-dependent chemical potentials. mutable vector_fp m_mu_cov; //! Temporary storage for the sum of reference state enthalpies and @@ -309,119 +490,123 @@ class CoverageDependentSurfPhase : public SurfPhase //! coverage-dependent entropies. mutable vector_fp m_entropy; - //! Temporary storage for the sum of reference state Gibbs energies - //! and coverage-dependent Gibbs energies. + //! Temporary storage for the sum of reference state heat capacities and + //! coverage-dependent heat capapcities. + mutable vector_fp m_heatcapacity; + + //! Temporary storage for the sum of reference state chemical potentials + //! and coverage-dependent chemical potentials. mutable vector_fp m_chempot; - //! Apply changes to the state which are needed after the coverage - //! changes. This function is called after any call to setCoverages(), - //! or similar. For phases which need to execute a callback after any - //! change to the coverage, it should be done by overriding this - //! function rather than overriding all of the coverage-setting functions. + /*! Indicate the surface coverages have changed by incrementing the + * coverage state number by one. This function is called whenever + * the setCoverages() or setCoveragesNoNorm() was used to update the + * coverage array. + */ void coverageChanged(); - //! Adjustments to the thermodynamic quantities dependent on surface species - //! coverages. Four coverage parameters (species_k, species_j, enthalpy, - //! entropy) are used for each species on which the thermodynamics depends. - mutable std::vector m_LinearDependency; + //! Array of enthalpy and entropy coverage dependency parameters used in + //! the linear and polynomial models. + mutable std::vector m_PolynomialDependency; - //! Adjustments to the thermodynamic quantities dependent on surface species - //! coverages. Eight coverage parameters (species_k, species_j, enthalpy_low, - //! enthalpy_change, enthalpy_high, entropy_low, entropy_change, entropy_high) - //! are used for each species on which the thermodynamics depends. + //! Array of enthalpy and entropy coverage dependency parameters used in + //! the piecewise linear model. mutable std::vector m_PiecewiseDependency; - //! Adjustments to the thermodynamic quantities dependent on surface species - //! coverages. Six coverage parameters (species_k, species_j, enthalpy_coverage, - //! enthalpy, entropy_coverage, entropy) are used for each species on which - //! the thermodynamics depends. + //! Array of enthalpy and entropy coverage dependency parameters used in + //! the interpolative model. mutable std::vector m_InterpolativeDependency; - //! Temporary storage for the linear model enthalpy slope. - mutable double m_linear_h; - - //! Temporary storage for the linear model entropy slope. - mutable double m_linear_s; - - //! Temporary storage for the piecewise linear model enthalpy slope low. - mutable double m_piecewise_hlow; + //! Array of heat capacity coverage dependency parameters. + mutable std::vector m_HeatCapacityDependency; - //! Temporary storage for the piecewise linear model enthalpy - //! slope change coverage. - mutable double m_piecewise_hchange; + //! Storage for the linear and polynomial model coverage dependency + //! parameters for enthalpy. + mutable vector_fp m_polynomial_h; - //! Temporary storage for the piecewise linear model enthalpy slope high. - mutable double m_piecewise_hhigh; + //! Storage for the linear and polynomial model coverage dependency + //! parameters for entropy. + mutable vector_fp m_polynomial_s; - //! Temporary storage for the piecewise linear model entropy slope low. - mutable double m_piecewise_slow; + //! Storage for the piecewise linear model coverage dependency parameters + //! for enthalpy. + mutable vector_fp m_piecewise_h; - //! Temporary storage for the piecewise linear model - //! entropy slope change coverage. - mutable double m_piecewise_schange; + //! Storage for the piecewise linear model coverage dependency parameters + //! for entropy. + mutable vector_fp m_piecewise_s; - //! Temporary storage for the piecewise linear model entropy slope high. - mutable double m_piecewise_shigh; - - //! Temporary storage for the interpolative model coverage array for - //! enthalpy. + //! Storage for the interpolative model coverage array for enthalpy. mutable vector_fp m_interpolative_hcov; - //! Temporary storage for the interpolative model enthalpy array. + //! Storage for the interpolative model enthalpy array. mutable vector_fp m_interpolative_h; - //! Temporary storage for the interpolative model coverage array for - //! entropy. + //! Storage for the interpolative model coverage array for entropy. mutable vector_fp m_interpolative_scov; - //! Temporary storage for the interpolative model entropy array. + //! Storage for the interpolative model entropy array. mutable vector_fp m_interpolative_s; + //! Storage for the coverage-dependent heat capacity log model coefficients. + mutable double m_cpcov_a, m_cpcov_b; + private: - //! Coverage State Change variable. Whenever the coverage vector changes, - //! this int is incremented. + //! Coverage state change variable. Whenever the coverage array changes, + //! this number is incremented. int m_covstateNum; //! Last value of the coverage state number processed. mutable int m_covstateNumlast; - //! Flag indicating linear coverage-dependent model is being used. - mutable bool m_has_linear_dependency; + //! Flag indicating polynomial coverage-dependent model is being used. + mutable bool m_has_polynomial_dependency; //! Flag indicating piecewise linear coverage-dependent model is being used. mutable bool m_has_piecewise_dependency; - //! Flag indicating Interpolative coverage-dependent model is being used. + //! Flag indicating interpolative coverage-dependent model is being used. mutable bool m_has_interpolative_dependency; - //! Update the species reference state thermodynamic functions + //! Flag indicating coverage-dependent heat capacity is being calculated. + mutable bool m_has_heatcapacity_dependency; + + //! Flag indicating user has defined the reference coverage. + mutable bool m_has_ref_coverage; + + //! Storage for the user-defined reference state coverage which has to be + //! greater than 0.0 and less than or equal to 1.0. + mutable double m_theta_ref; + + //! Update the species ideal reference state thermodynamic functions /*! - * The polynomials for the standard state functions are only reevaluated if - * the temperature has changed. + * The polynomials for the ideal surface reference functions are only + * reevaluated if the temperature has changed. * * @param force Boolean, which if true, forces a reevaluation of the thermo * polynomials. default = false. */ - void _updateIdealSurfThermo(bool force=false) const; + void _updateReferenceThermo(bool force=false) const; - //! Update the species non-ideal, coverage-dependent thermodynamic functions + //! Update the species coverage-dependent thermodynamic functions /*! - * The coverage-dependent functions are only reevaluated if the coverage has - * changed by its corresponding model: linear, piecewise linear, - * or interpolative model. + * The coverage-dependent enthalpy and entropy are only reevaluated + * if the coverage has changed. The coverage-dependent heat capacity + * is only reevaluated if the coverage or temperature has changed. * * @param force Boolean, which if true, forces a reevaluation of the * coverage-dependent functions. default = false. */ - void _updateNonidealSurfThermo(bool force=false) const; + void _updateCovDepThermo(bool force=false) const; - //! Update the total (ideal + non-ideal) thermodynamic functions + //! Update the total (reference state + coverage-dependent) thermodynamic functions /*! * Calls subroutines for ideal species thermodynamic update as well as - * non-ideal species thermodynamic update + * coverage-dependent species thermodynamic update * * @param force Boolean, which if true, forces a reevaluation of the - * coverage-dependent functions. default = false. + * reference state as well as coverage-dependent functions. + * default = false. */ void _updateThermo(bool force=false) const; diff --git a/src/thermo/CoverageDependentSurfPhase.cpp b/src/thermo/CoverageDependentSurfPhase.cpp index def32a3e7ca..d1fae805f8d 100644 --- a/src/thermo/CoverageDependentSurfPhase.cpp +++ b/src/thermo/CoverageDependentSurfPhase.cpp @@ -24,9 +24,11 @@ namespace Cantera CoverageDependentSurfPhase::CoverageDependentSurfPhase(doublereal n0): m_covstateNum(-1), m_covstateNumlast(0), - m_has_linear_dependency(false), + m_has_polynomial_dependency(false), m_has_piecewise_dependency(false), - m_has_interpolative_dependency(false) + m_has_interpolative_dependency(false), + m_has_heatcapacity_dependency(false), + m_has_ref_coverage(false) { setSiteDensity(n0); setNDim(2); @@ -36,9 +38,11 @@ CoverageDependentSurfPhase::CoverageDependentSurfPhase(const std::string& infile const std::string& id_): m_covstateNum(-1), m_covstateNumlast(0), - m_has_linear_dependency(false), + m_has_polynomial_dependency(false), m_has_piecewise_dependency(false), - m_has_interpolative_dependency(false) + m_has_interpolative_dependency(false), + m_has_heatcapacity_dependency(false), + m_has_ref_coverage(false) { initThermoFile(infile, id_); } @@ -46,31 +50,33 @@ CoverageDependentSurfPhase::CoverageDependentSurfPhase(const std::string& infile CoverageDependentSurfPhase::CoverageDependentSurfPhase(XML_Node& xmlphase): m_covstateNum(-1), m_covstateNumlast(0), - m_has_linear_dependency(false), + m_has_polynomial_dependency(false), m_has_piecewise_dependency(false), - m_has_interpolative_dependency(false) + m_has_interpolative_dependency(false), + m_has_heatcapacity_dependency(false), + m_has_ref_coverage(false) { importPhase(xmlphase, this); } -void CoverageDependentSurfPhase::setLinearDependency(const LinearDependency& - lin_deps) +void CoverageDependentSurfPhase::setPolynomialDependency(const PolynomialDependency& + poly_deps) { - size_t k = speciesIndex(lin_deps.name_k); - size_t j = speciesIndex(lin_deps.name_j); + size_t k = speciesIndex(poly_deps.name_k); + size_t j = speciesIndex(poly_deps.name_j); if (k == npos) { - throw CanteraError("CoverageDependentSurfPhase::setLinearDependency", - "Unknown species '{}'.", lin_deps.name_k); + throw CanteraError("CoverageDependentSurfPhase::setPolynomialDependency", + "Unknown species '{}'.", poly_deps.name_k); } if (j == npos) { - throw CanteraError("CoverageDependentSurfPhase::setLinearDependency", - "Unknown species '{}'.", lin_deps.name_k); + throw CanteraError("CoverageDependentSurfPhase::setPolynomialDependency", + "Unknown species '{}'.", poly_deps.name_k); } - m_LinearDependency.push_back(lin_deps); - m_has_linear_dependency = true; + m_PolynomialDependency.push_back(poly_deps); + m_has_polynomial_dependency = true; } void CoverageDependentSurfPhase::setPiecewiseDependency(const PiecewiseDependency& @@ -78,6 +84,8 @@ void CoverageDependentSurfPhase::setPiecewiseDependency(const PiecewiseDependenc { size_t k = speciesIndex(plin_deps.name_k); size_t j = speciesIndex(plin_deps.name_j); + double hcov_change = plin_deps.enthalpy_params[2]; + double scov_change = plin_deps.entropy_params[2]; if (k == npos) { throw CanteraError("CoverageDependentSurfPhase::setPiecewiseDependency", @@ -88,7 +96,12 @@ void CoverageDependentSurfPhase::setPiecewiseDependency(const PiecewiseDependenc throw CanteraError("CoverageDependentSurfPhase::setPiecewiseDependency", "Unknown species '{}'.", plin_deps.name_j); } - + + if (hcov_change <= 0.0 || hcov_change > 1.0 || scov_change <= 0.0 || scov_change > 1.0) { + throw CanteraError("CoverageDependentSurfPhase::setPiecewiseDependency", + "Coverage where slope changes must be greater than 0.0 and less than or equal to 1.0."); + } + m_PiecewiseDependency.push_back(plin_deps); m_has_piecewise_dependency = true; } @@ -103,13 +116,7 @@ void CoverageDependentSurfPhase::setInterpolativeDependency(const if (k == npos) { throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", "Unknown species '{}'.", int_deps.name_k); - } - - if (j == npos) { - throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", - "Unknown species '{}'.", int_deps.name_j); - } - + } if (int_deps.enthalpy_coverages.size() != int_deps.enthalpies.size()) { throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", "Sizes of coverages array and enthalpies array are not equal."); @@ -122,7 +129,19 @@ void CoverageDependentSurfPhase::setInterpolativeDependency(const } hcov_last = hcov_now; } + if (int_deps.enthalpy_coverages.front() != 0.0) { + throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", + "The first element of enthalpy-coverages array must be 0.0."); + } + if (int_deps.enthalpy_coverages.back() != 1.0) { + throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", + "The last element of enthalpy-coverages array must be 1.0."); + } + if (j == npos) { + throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", + "Unknown species '{}'.", int_deps.name_j); + } if (int_deps.entropy_coverages.size() != int_deps.entropies.size()) { throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", "Sizes of coverages array and entropies array are not equal."); @@ -135,11 +154,39 @@ void CoverageDependentSurfPhase::setInterpolativeDependency(const } scov_last = scov_now; } + if (int_deps.entropy_coverages.front() != 0.0) { + throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", + "The first element of entropy-coverages array must be 0.0."); + } + if (int_deps.entropy_coverages.back() != 1.0) { + throw CanteraError("CoverageDependentSurfPhase::setInterpolativeDependency", + "The last element of entropy-coverages array must be 1.0."); + } m_InterpolativeDependency.push_back(int_deps); m_has_interpolative_dependency = true; } +void CoverageDependentSurfPhase::setHeatCapacityDependency(const HeatCapacityDependency& + cpcov_deps) +{ + size_t k = speciesIndex(cpcov_deps.name_k); + size_t j = speciesIndex(cpcov_deps.name_j); + + if (k == npos) { + throw CanteraError("CoverageDependentSurfPhase::setHeatCapacityDependency", + "Unknown species '{}'.", cpcov_deps.name_k); + } + + if (j == npos) { + throw CanteraError("CoverageDependentSurfPhase::setHeatCapacityDependency", + "Unknown species '{}'.", cpcov_deps.name_j); + } + + m_HeatCapacityDependency.push_back(cpcov_deps); + m_has_heatcapacity_dependency = true; +} + void CoverageDependentSurfPhase::initThermo() { if (m_input.hasKey("site-density")) { @@ -147,96 +194,153 @@ void CoverageDependentSurfPhase::initThermo() setSiteDensity(m_input.convert("site-density", Units(1.0, 0, -static_cast(m_ndim), 0, 0, 0, 1))); } + if (m_input.hasKey("reference-state-coverage")) { + m_theta_ref = m_input["reference-state-coverage"].as(); + if (m_theta_ref <= 0.0 || m_theta_ref > 1.0) { + throw CanteraError("CoverageDependentSurfPhase::initThermo", + "Reference state coverage must be greater than 0.0 and less than or equal to 1.0."); + } + m_has_ref_coverage = true; + } for (auto& item : m_species) { // Read enthalpy and entropy dependencies from species 'input' information - // (i.e. as specified in a YAML input file) for both self and cross + // (i.e. as specified in a YAML input file) for both self- and cross- // interactions. if (item.second->input.hasKey("coverage-dependencies")) { auto cov_map = item.second->input["coverage-dependencies"]; for (const auto& item2 : cov_map) { - if (item2.first == "model") { - continue; - } auto cov_map2 = item2.second.as(); - if (cov_map2.hasKey("enthalpy")) { - // For linear model - auto enthalpy = cov_map2["enthalpy"].as(); - if (cov_map2.hasKey("enthalpy-unit")) { - auto enthalpy_unit = cov_map2["enthalpy-unit"].as(); - m_linear_h = convertEnthalpy(enthalpy, enthalpy_unit); - } else { - m_linear_h = enthalpy; - } - - auto entropy = cov_map2["entropy"].as(); - if (cov_map2.hasKey("entropy-unit")) { - auto entropy_unit = cov_map2["entropy-unit"].as(); - m_linear_s = convertEntropy(entropy, entropy_unit); + // For linear and polynomial model + if (cov_map2["model"] == "Linear" || cov_map2["model"] == "Polynomial") { + m_polynomial_h = {0.0, 0.0, 0.0, 0.0}; + m_polynomial_s = {0.0, 0.0, 0.0, 0.0}; + if (cov_map2.hasKey("enthalpy")) { + auto enthalpy = cov_map2["enthalpy"].as(); + m_polynomial_h[0] = enthalpy; } else { - m_linear_s = entropy; + if (cov_map2.hasKey("enthalpy-1st-order")) { + auto h_a = cov_map2["enthalpy-1st-order"].as(); + m_polynomial_h[0] = h_a; + } + if (cov_map2.hasKey("enthalpy-2nd-order")) { + auto h_b = cov_map2["enthalpy-2nd-order"].as(); + m_polynomial_h[1] = h_b; + } + if (cov_map2.hasKey("enthalpy-3rd-order")) { + auto h_c = cov_map2["enthalpy-3rd-order"].as(); + m_polynomial_h[2] = h_c; + } + if (cov_map2.hasKey("enthalpy-4th-order")) { + auto h_d = cov_map2["enthalpy-4th-order"].as(); + m_polynomial_h[3] = h_d; + } } - - LinearDependency lin_deps(item.first, item2.first, - m_linear_h, m_linear_s); - setLinearDependency(lin_deps); - } else if (cov_map2.hasKey("enthalpy-low")) { - // For piecewise linear model - auto enthalpy_low = cov_map2["enthalpy-low"].as(); - auto enthalpy_high = cov_map2["enthalpy-high"].as(); if (cov_map2.hasKey("enthalpy-unit")) { auto enthalpy_unit = cov_map2["enthalpy-unit"].as(); - m_piecewise_hlow = convertEnthalpy(enthalpy_low, enthalpy_unit); - m_piecewise_hhigh = convertEnthalpy(enthalpy_high, enthalpy_unit); + for (size_t i = 0; i < 4; i++) { + m_polynomial_h[i] = convertEnergy(m_polynomial_h[i], enthalpy_unit); + } + } + + if (cov_map2.hasKey("entropy")) { + auto entropy = cov_map2["entropy"].as(); + m_polynomial_s[0] = entropy; } else { - m_piecewise_hlow = enthalpy_low; - m_piecewise_hhigh = enthalpy_high; + if (cov_map2.hasKey("entropy-1st-order")) { + auto s_a = cov_map2["entropy-1st-order"].as(); + m_polynomial_s[0] = s_a; + } + if (cov_map2.hasKey("entropy-2nd-order")) { + auto s_b = cov_map2["entropy-2nd-order"].as(); + m_polynomial_s[1] = s_b; + } + if (cov_map2.hasKey("entropy-3rd-order")) { + auto s_c = cov_map2["entropy-3rd-order"].as(); + m_polynomial_s[2] = s_c; + } + if (cov_map2.hasKey("entropy-4th-order")) { + auto s_d = cov_map2["entropy-4th-order"].as(); + m_polynomial_s[3] = s_d; + } } - m_piecewise_hchange = cov_map2["enthalpy-change"].as(); - - auto entropy_low = cov_map2["entropy-low"].as(); - auto entropy_high = cov_map2["entropy-high"].as(); if (cov_map2.hasKey("entropy-unit")) { auto entropy_unit = cov_map2["entropy-unit"].as(); - m_piecewise_slow = convertEntropy(entropy_low, entropy_unit); - m_piecewise_shigh = convertEntropy(entropy_high, entropy_unit); - } else { - m_piecewise_slow = entropy_low; - m_piecewise_shigh = entropy_high; + for (size_t i = 0; i < 4; i++) { + m_polynomial_s[i] = convertEnergy_T(m_polynomial_s[i], entropy_unit); + } + } + + PolynomialDependency poly_deps(item.first, item2.first, + m_polynomial_h, m_polynomial_s); + setPolynomialDependency(poly_deps); + // For piecewise linear model + } else if (cov_map2["model"] == "Piecewise-Linear") { + m_piecewise_h = {0.0, 0.0, 0.5}; + m_piecewise_s = {0.0, 0.0, 0.5}; + if (cov_map2.hasKey("enthalpy-low")) { + auto enthalpy_low = cov_map2["enthalpy-low"].as(); + auto enthalpy_high = cov_map2["enthalpy-high"].as(); + auto enthalpy_change = cov_map2["enthalpy-change"].as(); + if (cov_map2.hasKey("enthalpy-unit")) { + auto enthalpy_unit = cov_map2["enthalpy-unit"].as(); + enthalpy_low = convertEnergy(enthalpy_low, enthalpy_unit); + enthalpy_high = convertEnergy(enthalpy_high, enthalpy_unit); + } + m_piecewise_h[0] = enthalpy_low; + m_piecewise_h[1] = enthalpy_high; + m_piecewise_h[2] = enthalpy_change; } - m_piecewise_schange = cov_map2["entropy-change"].as(); + + if (cov_map2.hasKey("entropy-low")) { + auto entropy_low = cov_map2["entropy-low"].as(); + auto entropy_high = cov_map2["entropy-high"].as(); + auto entropy_change = cov_map2["entropy-change"].as(); + if (cov_map2.hasKey("entropy-unit")) { + auto entropy_unit = cov_map2["entropy-unit"].as(); + entropy_low = convertEnergy_T(entropy_low, entropy_unit); + entropy_high = convertEnergy_T(entropy_high, entropy_unit); + } + m_piecewise_s[0] = entropy_low; + m_piecewise_s[1] = entropy_high; + m_piecewise_s[2] = entropy_change; + } PiecewiseDependency plin_deps(item.first, item2.first, - m_piecewise_hlow, - m_piecewise_hhigh, - m_piecewise_hchange, - m_piecewise_slow, - m_piecewise_shigh, - m_piecewise_schange); + m_piecewise_h, + m_piecewise_s); setPiecewiseDependency(plin_deps); - } else if (cov_map2.hasKey("enthalpy-coverages")) { - // For interpolative model - m_interpolative_hcov = cov_map2["enthalpy-coverages"].as(); - auto enthalpies = cov_map2["enthalpies"].as(); - if (cov_map2.hasKey("enthalpy-unit")) { - m_interpolative_h.resize(enthalpies.size()); - auto enthalpy_unit = cov_map2["enthalpy-unit"].as(); - for (size_t k = 0; k < enthalpies.size(); k++) { - m_interpolative_h[k] = convertEnthalpy(enthalpies[k], enthalpy_unit); + // For interpolative model + } else if (cov_map2["model"] == "Interpolative") { + m_interpolative_hcov = {0.0, 1.0}; + m_interpolative_h = {0.0, 0.0}; + m_interpolative_scov = {0.0, 1.0}; + m_interpolative_s = {0.0, 0.0}; + if (cov_map2.hasKey("enthalpy-coverages")) { + m_interpolative_hcov = cov_map2["enthalpy-coverages"].as(); + auto enthalpies = cov_map2["enthalpies"].as(); + if (cov_map2.hasKey("enthalpy-unit")) { + m_interpolative_h.resize(enthalpies.size()); + auto enthalpy_unit = cov_map2["enthalpy-unit"].as(); + for (size_t k = 0; k < enthalpies.size(); k++) { + m_interpolative_h[k] = convertEnergy(enthalpies[k], enthalpy_unit); + } + } else { + m_interpolative_h = enthalpies; } - } else { - m_interpolative_h = enthalpies; } - m_interpolative_scov = cov_map2["entropy-coverages"].as(); - auto entropies = cov_map2["entropies"].as(); - if (cov_map2.hasKey("entropy-unit")) { - m_interpolative_s.resize(entropies.size()); - auto entropy_unit = cov_map2["entropy-unit"].as(); - for (size_t k = 0; k < entropies.size(); k++) { - m_interpolative_s[k] = convertEntropy(entropies[k], entropy_unit); + if (cov_map2.hasKey("entropy-coverages")) { + m_interpolative_scov = cov_map2["entropy-coverages"].as(); + auto entropies = cov_map2["entropies"].as(); + if (cov_map2.hasKey("entropy-unit")) { + m_interpolative_s.resize(entropies.size()); + auto entropy_unit = cov_map2["entropy-unit"].as(); + for (size_t k = 0; k < entropies.size(); k++) { + m_interpolative_s[k] = convertEnergy_T(entropies[k], entropy_unit); + } + } else { + m_interpolative_s = entropies; } - } else { - m_interpolative_s = entropies; } InterpolativeDependency int_deps(item.first, item2.first, @@ -247,56 +351,126 @@ void CoverageDependentSurfPhase::initThermo() setInterpolativeDependency(int_deps); } else { throw CanteraError("CoverageDependentSurfPhase::initThermo", - "Unrecognized coverage-dependencies format between '{}' and '{}'.", + "Unrecognized coverage-dependencies model between '{}' and '{}'.", item.first, item2.first); } + // For coverage-dependent heat capacity parameters, if present + if (cov_map2.hasKey("heat-capacity-a")) { + auto cpcov_a = cov_map2["heat-capacity-a"].as(); + auto cpcov_b = cov_map2["heat-capacity-b"].as(); + if (cov_map2.hasKey("heat-capacity-unit")){ + auto cpcov_unit = cov_map2["heat-capacity-unit"].as(); + m_cpcov_a = convertEnergy_T(cpcov_a, cpcov_unit); + m_cpcov_b = convertEnergy_T(cpcov_b, cpcov_unit); + } else { + m_cpcov_a = cpcov_a; + m_cpcov_b = cpcov_b; + } + HeatCapacityDependency cpcov_deps(item.first, item2.first, + m_cpcov_a, m_cpcov_b); + setHeatCapacityDependency(cpcov_deps); + } } } } } -doublereal CoverageDependentSurfPhase::enthalpy_mole() const +bool CoverageDependentSurfPhase::addSpecies(shared_ptr spec) { - if (m_n0 <= 0.0) { - return 0.0; + bool added = SurfPhase::addSpecies(spec); + if (added) { + m_h_ref.push_back(0.0); + m_s_ref.push_back(0.0); + m_cp_ref.push_back(0.0); + m_mu_ref.push_back(0.0); + m_h_cov.push_back(0.0); + m_s_cov.push_back(0.0); + m_cp_cov.push_back(0.0); + m_mu_cov.push_back(0.0); + m_enthalpy.push_back(0.0); + m_entropy.push_back(0.0); + m_heatcapacity.push_back(0.0); + m_chempot.push_back(0.0); } - _updateThermo(); - return mean_X(m_enthalpy); + return added; } -doublereal CoverageDependentSurfPhase::entropy_mole() const +void CoverageDependentSurfPhase::setCoverages(const doublereal* theta) { - _updateThermo(); - doublereal entropy = 0.0; - for (size_t k = 0; k < m_kk; k++) { - entropy += moleFraction(k) * (m_entropy[k] - - GasConstant * log(std::max(concentration(k) * size(k)/m_n0, SmallNumber))); + SurfPhase::setCoverages(theta); + coverageChanged(); +} + +void CoverageDependentSurfPhase::setCoveragesNoNorm(const doublereal* theta) +{ + SurfPhase::setCoveragesNoNorm(theta); + coverageChanged(); +} + +double CoverageDependentSurfPhase::convertEnergy(double value, + const std::string& src) const +{ + // Convert to J/kmol + Units usrc(src); + if (usrc.convertible(Units("J/kmol"))) { + value *= usrc.factor(); + } else if (src == "eV" || src == "meV" ) { + value *= Avogadro * usrc.factor(); + } else { + throw CanteraError("CoverageDependentSurfPhase::convertEnergy", + "Don't understand units '{}' as energy", src); } - return entropy; + return value; } -void CoverageDependentSurfPhase::getChemPotentials(doublereal* mu) const +double CoverageDependentSurfPhase::convertEnergy_T(double value, + const std::string& src) const { - _updateThermo(); - copy(m_chempot.begin(), m_chempot.end(), mu); - getActivityConcentrations(m_work.data()); - for (size_t k = 0; k < m_kk; k++) { - mu[k] += RT() * (log(m_work[k]) - logStandardConc(k)); + // Convert to J/kmol/K + Units usrc(src); + if (usrc.convertible(Units("J/kmol/K"))) { + value *= usrc.factor(); + } else if (src == "eV/K" || src == "meV/K") { + value *= Avogadro * usrc.factor(); + } else { + throw CanteraError("CoverageDependentSurfPhase::convertEnergy_T", + "Don't understand units '{}' as energy over temperature", src); } + return value; } -void CoverageDependentSurfPhase::getPureGibbs(doublereal* g) const +void CoverageDependentSurfPhase::coverageChanged() { + m_covstateNum++; +} + +// Functions calculating reference state thermodyanmic properties-------------- + +void CoverageDependentSurfPhase::getGibbs_RT_ref(doublereal* grt) const { - _updateThermo(); - copy(m_chempot.begin(), m_chempot.end(), g); + _updateReferenceThermo(); + scale(m_mu_ref.begin(), m_mu_ref.end(), grt, 1.0/RT()); } -void CoverageDependentSurfPhase::getGibbs_RT(doublereal* grt) const +void CoverageDependentSurfPhase::getEnthalpy_RT_ref(doublereal* hrt) const { - _updateThermo(); - scale(m_chempot.begin(), m_chempot.end(), grt, 1.0/RT()); + _updateReferenceThermo(); + scale(m_h_ref.begin(), m_h_ref.end(), hrt, 1.0/RT()); +} + +void CoverageDependentSurfPhase::getEntropy_R_ref(doublereal* sr) const +{ + _updateReferenceThermo(); + scale(m_s_ref.begin(), m_s_ref.end(), sr, 1.0/GasConstant); +} + +void CoverageDependentSurfPhase::getCp_R_ref(doublereal* cpr) const +{ + _updateReferenceThermo(); + scale(m_cp_ref.begin(), m_cp_ref.end(), cpr, 1.0/GasConstant); } +// Functions calculating standard state thermodyanmic properties--------------- + void CoverageDependentSurfPhase::getEnthalpy_RT(doublereal* hrt) const { _updateThermo(); @@ -307,108 +481,181 @@ void CoverageDependentSurfPhase::getEntropy_R(doublereal* sr) const { _updateThermo(); scale(m_entropy.begin(), m_entropy.end(), sr, 1.0/GasConstant); + if (m_has_ref_coverage) { + double tmp = -log(m_theta_ref); + for (size_t k = 0; k < m_kk; k++) { + sr[k] -= tmp; + } + } } -bool CoverageDependentSurfPhase::addSpecies(shared_ptr spec) +void CoverageDependentSurfPhase::getCp_R(doublereal* cpr) const { - bool added = SurfPhase::addSpecies(spec); - if (added) { - m_h_cov.push_back(0.0); - m_s_cov.push_back(0.0); - m_mu_cov.push_back(0.0); - m_enthalpy.push_back(0.0); - m_entropy.push_back(0.0); - m_chempot.push_back(0.0); + _updateThermo(); + scale(m_heatcapacity.begin(), m_heatcapacity.end(), cpr, 1.0/GasConstant); +} + +void CoverageDependentSurfPhase::getGibbs_RT(doublereal* grt) const +{ + _updateThermo(); + scale(m_chempot.begin(), m_chempot.end(), grt, 1.0/RT()); + if (m_has_ref_coverage) { + double tmp = -log(m_theta_ref); + for (size_t k = 0; k < m_kk; k++) { + grt[k] += tmp; + } } - return added; } -void CoverageDependentSurfPhase::coverageChanged() { - m_covstateNum++; +void CoverageDependentSurfPhase::getPureGibbs(doublereal* g) const +{ + getGibbs_RT(g); + for (size_t k = 0; k < m_kk; k++) { + g[k] *= RT(); + } } -void CoverageDependentSurfPhase::setCoverages(const doublereal* theta) +void CoverageDependentSurfPhase::getStandardChemPotentials(doublereal* mu0) const { - SurfPhase::setCoverages(theta); - coverageChanged(); + _updateThermo(); + copy(m_chempot.begin(), m_chempot.end(), mu0); + if (m_has_ref_coverage) { + double tmp = RT() * -log(m_theta_ref); + for (size_t k = 0; k < m_kk; k++) { + mu0[k] += tmp; + } + } } -void CoverageDependentSurfPhase::setCoveragesNoNorm(const doublereal* theta) +// Functions calling partial molar thermodyanmic properties---------------- + +void CoverageDependentSurfPhase::getPartialMolarEnthalpies(doublereal* hbar) const { - SurfPhase::setCoveragesNoNorm(theta); - coverageChanged(); + _updateThermo(); + copy(m_enthalpy.begin(), m_enthalpy.end(), hbar); } -double CoverageDependentSurfPhase::convertEnthalpy(double value, - const std::string& src) const +void CoverageDependentSurfPhase::getPartialMolarEntropies(doublereal* sbar) const { - // Convert to J/kmol - Units usrc(src); - if (usrc.convertible(Units("J/kmol"))) { - value *= usrc.factor(); - } else if (usrc.convertible(Units("eV"))) { - value *= Avogadro * usrc.factor(); + _updateThermo(); + copy(m_entropy.begin(), m_entropy.end(), sbar); + if (m_has_ref_coverage) { + for (size_t k = 0; k < m_kk; k++) { + sbar[k] -= GasConstant * log(std::max(concentration(k) * size(k)/m_n0, + SmallNumber) / m_theta_ref); + } + } else { + for (size_t k = 0; k < m_kk; k++) { + sbar[k] -= GasConstant * log(std::max(concentration(k) * size(k)/m_n0, + SmallNumber)); + } + } +} + +void CoverageDependentSurfPhase::getPartialMolarCp(doublereal* cpbar) const +{ + _updateThermo(); + copy(m_heatcapacity.begin(), m_heatcapacity.end(), cpbar); +} + + +void CoverageDependentSurfPhase::getChemPotentials(doublereal* mu) const +{ + _updateThermo(); + copy(m_chempot.begin(), m_chempot.end(), mu); + if (m_has_ref_coverage) { + for (size_t k = 0; k < m_kk; k++) { + mu[k] += RT() * log(std::max(concentration(k) * size(k)/m_n0, + SmallNumber) / m_theta_ref); + } } else { - throw CanteraError("CoverageDependentSurfPhase::convertEnthalpy", - "Don't understand units '{}' as an enthalpy", src); + for (size_t k = 0; k < m_kk; k++) { + mu[k] += RT() * log(std::max(concentration(k) * size(k)/m_n0, + SmallNumber)); + } } - return value; } -double CoverageDependentSurfPhase::convertEntropy(double value, - const std::string& src) const +// Functions calculating mixture thermodyanmic properties-------------------------- + +doublereal CoverageDependentSurfPhase::enthalpy_mole() const { - // Convert to J/kmol/K - Units usrc(src); - if (usrc.convertible(Units("J/kmol/K"))) { - value *= usrc.factor(); - } else if (usrc.convertible(Units("eV/K"))) { - value *= Avogadro * usrc.factor(); + if (m_n0 <= 0.0) { + return 0.0; + } + _updateThermo(); + return mean_X(m_enthalpy); +} + +doublereal CoverageDependentSurfPhase::entropy_mole() const +{ + _updateThermo(); + doublereal entropy = 0.0; + if (m_has_ref_coverage) { + for (size_t k = 0; k < m_kk; k++) { + entropy += moleFraction(k) * (m_entropy[k] - + GasConstant * log(std::max(concentration(k) * size(k)/m_n0, + SmallNumber) / m_theta_ref)); + } } else { - throw CanteraError("CoverageDependentSurfPhase::convertEntropy", - "Don't understand units '{}' as an entropy", src); + for (size_t k = 0; k < m_kk; k++) { + entropy += moleFraction(k) * (m_entropy[k] - + GasConstant * log(std::max(concentration(k) * size(k)/m_n0, + SmallNumber))); + } } - return value; + return entropy; +} + +doublereal CoverageDependentSurfPhase::cp_mole() const +{ + _updateThermo(); + return mean_X(m_heatcapacity); } -void CoverageDependentSurfPhase::_updateIdealSurfThermo(bool force) const +void CoverageDependentSurfPhase::_updateReferenceThermo(bool force) const { doublereal tnow = temperature(); if (m_tlast != tnow || force) { - m_spthermo.update(tnow, m_cp0.data(), m_h0.data(), m_s0.data()); - m_tlast = tnow; + m_spthermo.update(tnow, m_cp_ref.data(), m_h_ref.data(), m_s_ref.data()); for (size_t k = 0; k < m_kk; k++) { - m_h0[k] *= GasConstant * tnow; - m_s0[k] *= GasConstant; - m_cp0[k] *= GasConstant; - m_mu0[k] = m_h0[k] - tnow*m_s0[k]; + m_h_ref[k] *= GasConstant * tnow; + m_s_ref[k] *= GasConstant; + m_cp_ref[k] *= GasConstant; + m_mu_ref[k] = m_h_ref[k] - tnow * m_s_ref[k]; } - m_tlast = tnow; } } -void CoverageDependentSurfPhase::_updateNonidealSurfThermo(bool force) const +void CoverageDependentSurfPhase::_updateCovDepThermo(bool force) const { int covstateNumnow = statecovNumber(); + doublereal tnow = temperature(); if (m_covstateNumlast != covstateNumnow || force) { for (size_t k = 0; k < m_kk; k++) { m_h_cov[k] = 0.0; m_s_cov[k] = 0.0; } - - doublereal tnow = temperature(); vector_fp cov(m_kk, 0.0); SurfPhase::getCoverages(cov.data()); - // For linear model - if (m_has_linear_dependency) { - for (auto& item : m_LinearDependency) { + // For linear and polynomial model + if (m_has_polynomial_dependency) { + for (auto& item : m_PolynomialDependency) { size_t k = speciesIndex(item.name_k); size_t j = speciesIndex(item.name_j); - double enthalpy_slope = item.enthalpy; - double entropy_slope = item.entropy; - m_h_cov[k] += enthalpy_slope * cov[j]; - m_s_cov[k] += entropy_slope * cov[j]; + for (size_t i = 0; i < 4; i++) { + double h_coeff = item.enthalpy_coeffs[i]; + if (h_coeff != 0.0) { + double expo = double (i+1); + m_h_cov[k] += h_coeff * pow(cov[j], expo); + } + double s_coeff = item.entropy_coeffs[i]; + if (s_coeff != 0.0) { + double expo = double (i+1); + m_s_cov[k] += s_coeff * pow(cov[j], expo); + } + } } } @@ -417,10 +664,9 @@ void CoverageDependentSurfPhase::_updateNonidealSurfThermo(bool force) const for (auto& item : m_PiecewiseDependency) { size_t k = speciesIndex(item.name_k); size_t j = speciesIndex(item.name_j); - - double h_slope_low = item.enthalpy_low; - double h_slope_high = item.enthalpy_high; - double h_cov_change = item.enthalpy_change; + double h_slope_low = item.enthalpy_params[0]; + double h_slope_high = item.enthalpy_params[1]; + double h_cov_change = item.enthalpy_params[2]; if (cov[j] <= h_cov_change) { m_h_cov[k] += h_slope_low * cov[j]; } else { @@ -428,9 +674,9 @@ void CoverageDependentSurfPhase::_updateNonidealSurfThermo(bool force) const * (cov[j] - h_cov_change) + (h_cov_change * h_slope_low); } - double s_slope_low = item.entropy_low; - double s_slope_high = item.entropy_high; - double s_cov_change = item.entropy_change; + double s_slope_low = item.entropy_params[0]; + double s_slope_high = item.entropy_params[1]; + double s_cov_change = item.entropy_params[2]; if (cov[j] <= s_cov_change) { m_s_cov[k] += s_slope_low * cov[j]; } else { @@ -446,12 +692,10 @@ void CoverageDependentSurfPhase::_updateNonidealSurfThermo(bool force) const for (auto& item : m_InterpolativeDependency) { size_t k = speciesIndex(item.name_k); size_t j = speciesIndex(item.name_j); - vector_fp h_covs = item.enthalpy_coverages; size_t i_inter = 0; for (size_t i = 0; i < (h_covs.size()-1); i++) { - if (h_covs[i] <= cov[j] and - cov[j] <= h_covs[i+1]) { + if (h_covs[i] <= cov[j] && cov[j] <= h_covs[i+1]) { i_inter = i; break; } @@ -466,8 +710,7 @@ void CoverageDependentSurfPhase::_updateNonidealSurfThermo(bool force) const vector_fp s_covs = item.entropy_coverages; i_inter = 0; for (size_t i = 0; i < (s_covs.size()-1); i++) { - if (s_covs[i] <= cov[j] and - cov[j] <= s_covs[i+1]) { + if (s_covs[i] <= cov[j] && cov[j] <= s_covs[i+1]) { i_inter = i; break; } @@ -480,23 +723,49 @@ void CoverageDependentSurfPhase::_updateNonidealSurfThermo(bool force) const + s_left; } } + } - for (size_t k = 0; k < m_kk; k++) { - m_mu_cov[k] = m_h_cov[k] - tnow*m_s_cov[k]; + if (m_covstateNumlast != covstateNumnow || m_tlast != tnow || force) { + // For coverage-depedent heat capacity + if (m_has_heatcapacity_dependency) { + for (size_t k = 0; k < m_kk; k++) { + m_cp_cov[k] = 0.0; + } + vector_fp cov(m_kk, 0.0); + SurfPhase::getCoverages(cov.data()); + + for (auto& item : m_HeatCapacityDependency) { + size_t k = speciesIndex(item.name_k); + size_t j = speciesIndex(item.name_j); + double a = item.cpcov_a; + double b = item.cpcov_b; + m_cp_cov[k] += (a * log(tnow) + b) * cov[j] * cov[j]; + double int_cp_tnow = tnow * (a * log(tnow) - a + b); + double int_cp_298 = 298.15 * (a * log(298.15) - a + b); + m_h_cov[k] += (int_cp_tnow - int_cp_298) * cov[j] * cov[j]; + double int_cp_T_tnow = log(tnow) * (a * log(tnow) + 2 * b); + double int_cp_T_298 = log(298.15) * (a * log(298.15) + 2 * b); + m_s_cov[k] += 0.5 * (int_cp_T_tnow - int_cp_T_298) * cov[j] * cov[j]; + } } - m_covstateNumlast = covstateNumnow; } + for (size_t k = 0; k < m_kk; k++) { + m_mu_cov[k] = m_h_cov[k] - tnow * m_s_cov[k]; + } + m_covstateNumlast = covstateNumnow; + m_tlast = tnow; } void CoverageDependentSurfPhase::_updateThermo(bool force) const { - _updateIdealSurfThermo(force); - _updateNonidealSurfThermo(force); + _updateReferenceThermo(force); + _updateCovDepThermo(force); for (size_t k = 0; k < m_kk; k++) { - m_enthalpy[k] = m_h0[k] + m_h_cov[k]; - m_entropy[k] = m_s0[k] + m_s_cov[k]; - m_chempot[k] = m_mu0[k] + m_mu_cov[k]; + m_enthalpy[k] = m_h_ref[k] + m_h_cov[k]; + m_entropy[k] = m_s_ref[k] + m_s_cov[k]; + m_heatcapacity[k] = m_cp_ref[k] + m_cp_cov[k]; + m_chempot[k] = m_mu_ref[k] + m_mu_cov[k]; } } diff --git a/src/thermo/ThermoFactory.cpp b/src/thermo/ThermoFactory.cpp index 497ba67b0a6..3a8618cdeb0 100644 --- a/src/thermo/ThermoFactory.cpp +++ b/src/thermo/ThermoFactory.cpp @@ -55,8 +55,8 @@ ThermoFactory::ThermoFactory() addAlias("ideal-surface", "Surface"); addAlias("ideal-surface", "Surf"); reg("coverage-dependent-surface", []() { return new CoverageDependentSurfPhase(); }); - addAlias("coverage-dependent-surface", "CoverageDependentSurface"); - addAlias("coverage-dependent-surface", "CovDepSurf"); + //addAlias("coverage-dependent-surface", "CoverageDependentSurface"); + //addAlias("coverage-dependent-surface", "CovDepSurf"); reg("edge", []() { return new EdgePhase(); }); addAlias("edge", "Edge"); reg("electron-cloud", []() { return new MetalPhase(); });