Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues with "volume" and compressibility of SurfPhase #1350

Merged
merged 9 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions include/cantera/base/AnyMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -709,8 +709,9 @@ class InputFileError : public CanteraError
const std::string& message, const Args&... args)
: CanteraError(
procedure,
formatError(fmt::format(message, args...),
node.m_line, node.m_column, node.m_metadata))
formatError(
(sizeof...(args) == 0) ? message : fmt::format(message, args...),
node.m_line, node.m_column, node.m_metadata))
{
}

Expand All @@ -723,13 +724,13 @@ class InputFileError : public CanteraError
const Args&... args)
: CanteraError(
procedure,
formatError2(fmt::format(message, args...),
node1.m_line, node1.m_column, node1.m_metadata,
node2.m_line, node2.m_column, node2.m_metadata))
formatError2(
(sizeof...(args) == 0) ? message : fmt::format(message, args...),
node1.m_line, node1.m_column, node1.m_metadata,
node2.m_line, node2.m_column, node2.m_metadata))
{
}


virtual std::string getClass() const {
return "InputFileError";
}
Expand Down
2 changes: 1 addition & 1 deletion include/cantera/thermo/LatticeSolidPhase.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ class LatticeSolidPhase : public ThermoPhase
throw NotImplementedError("LatticeSolidPhase::getConcentrations");
}

virtual doublereal concentration(int k) const {
virtual doublereal concentration(size_t k) const {
throw NotImplementedError("LatticeSolidPhase::concentration");
}

Expand Down
14 changes: 10 additions & 4 deletions include/cantera/thermo/Phase.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ class Species;
* An example of this is the function
* Phase::setState_TRY(double t, double dens, const double* y).
*
* For bulk (3-dimensional) phases, the mass density has units of kg/m^3, and the molar
* density and concentrations have units of kmol/m^3, and the units listed in the
* methods of the Phase class assume a bulk phase. However, for surface (2-dimensional)
* phases have units of kg/m^2 and kmol/m^2, respectively. And for edge (1-dimensional)
* phases, these units kg/m and kmol/m.
*
* Class Phase contains methods for saving and restoring the full internal state
* of a given phase. These are saveState() and restoreState(). These functions
* operate on a state vector, which by default uses the first two entries for
Expand Down Expand Up @@ -532,7 +538,7 @@ class Phase
* kmol/m^3. The length of the vector must be greater than
* or equal to the number of species within the phase.
*/
void getConcentrations(double* const c) const;
virtual void getConcentrations(double* const c) const;

//! Concentration of species k.
//! If k is outside the valid range, an exception will be thrown.
Expand All @@ -541,7 +547,7 @@ class Phase
*
* @returns the concentration of species k (kmol m-3).
*/
double concentration(const size_t k) const;
virtual double concentration(const size_t k) const;

//! Set the concentrations to the specified values within the phase.
//! We set the concentrations here and therefore we set the overall density
Expand Down Expand Up @@ -664,11 +670,11 @@ class Phase

//! Molar density (kmol/m^3).
//! @return The molar density of the phase
double molarDensity() const;
virtual double molarDensity() const;

//! Molar volume (m^3/kmol).
//! @return The molar volume of the phase
double molarVolume() const;
virtual double molarVolume() const;

//! Set the internally stored density (kg/m^3) of the phase.
//! Note the density of a phase is an independent variable.
Expand Down
24 changes: 22 additions & 2 deletions include/cantera/thermo/SurfPhase.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ class SurfPhase : public ThermoPhase
return "Surf";
}

virtual bool isCompressible() const {
return false;
}

//! Return the Molar Enthalpy. Units: J/kmol.
/*!
* For an ideal solution,
Expand Down Expand Up @@ -189,8 +193,8 @@ class SurfPhase : public ThermoPhase
*
* @param k Optional parameter indicating the species. The default
* is to assume this refers to species 0.
* @return
* Returns the standard Concentration in units of m3 kmol-1.
* @return the standard concentration in units of kmol/m^2 for surface phases or
* kmol/m for edge phases.
*/
virtual doublereal standardConcentration(size_t k = 0) const;
virtual doublereal logStandardConc(size_t k=0) const;
Expand All @@ -200,6 +204,20 @@ class SurfPhase : public ThermoPhase

virtual bool addSpecies(shared_ptr<Species> spec);

//! Since interface phases have no volume, this returns 0.0.
virtual double molarVolume() const {
return 0.0;
}

//! Since interface phases have no volume, setting this to a value other than 0.0
//! raises an exception.
virtual void setMolarDensity(const double vm) {
if (vm != 0.0) {
throw CanteraError("SurfPhase::setMolarDensity",
"The volume of an interface is zero");
}
}

//! Returns the site density
/*!
* Site density kmol m-2
Expand Down Expand Up @@ -296,6 +314,8 @@ class SurfPhase : public ThermoPhase
virtual void setState(const AnyMap& state);

protected:
virtual void compositionChanged();

//! Surface site density (kmol m-2)
doublereal m_n0;

Expand Down
2 changes: 1 addition & 1 deletion interfaces/cython/cantera/onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ def write_hdf(self, filename, *args, group=None, species='X', mode='a',
`SolutionArray.write_hdf` via `to_solution_array` and requires a working
installation of *h5py* (``h5py`` can be installed using pip or conda).
"""
cols = ('extra', 'T', 'D', species)
cols = ('extra', 'T', 'P', species)
meta = self.settings
meta['date'] = formatdate(localtime=True)
meta['cantera_version'] = __version__
Expand Down
5 changes: 4 additions & 1 deletion interfaces/cython/cantera/thermo.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,10 @@ cdef class ThermoPhase(_SolutionBase):
self._setArray1(thermo_setMoleFractions, X)

property concentrations:
"""Get/Set the species concentrations [kmol/m^3]."""
"""
Get/Set the species concentrations. Units are kmol/m^3 for bulk phases, kmol/m^2
for surface phases, and kmol/m for edge phases.
"""
def __get__(self):
return self._getArray1(thermo_getConcentrations)
def __set__(self, C):
Expand Down
8 changes: 4 additions & 4 deletions samples/f90/demo.f90
Original file line number Diff line number Diff line change
Expand Up @@ -155,19 +155,19 @@ subroutine demo_surf(surf, nsp, nrxn)
type(phase_t) surf
integer :: nsp, nrxn, i
character*40 equation
double precision :: kf(nrxn), ropnet(nrxn)
double precision :: ropf(nrxn), ropnet(nrxn)

write(*,*)
write(*,*) '******** Interface Kinetics Test ********'
write(*,*)

call getFwdRatesOfProgress(surf, kf)
call getFwdRatesOfProgress(surf, ropf)
call getNetRatesOfProgress(surf, ropnet)

write(*,*) 'Equation k_fwd Net rate'
write(*,*) 'Equation Fwd rate Net rate'
do i = 1, nrxn
call getReactionString(surf, i, equation)
write(*,60) equation, kf(i), ropnet(i)
write(*,60) equation, ropf(i), ropnet(i)
60 format(' ',a40, e14.5, e14.5)
end do
end subroutine demo_surf
2 changes: 2 additions & 0 deletions src/base/Units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,8 @@ double UnitSystem::convert(const AnyValue& v, const std::string& dest) const
{
try {
return convert(v, Units(dest));
} catch (InputFileError&) {
throw; // already have input file context from convert(AnyValue, Units)
} catch (CanteraError& err) {
throw InputFileError("UnitSystem::convert", v, err.getMessage());
}
Expand Down
4 changes: 2 additions & 2 deletions src/fortran/cantera_kinetics.f90
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ double precision function ctkin_productStoichCoeff(self, k, i)
ctkin_productstoichcoeff = kin_productstoichcoeff(self%kin_id, k, i)
end function ctkin_productstoichcoeff

integer function ctkin_getReactionType(self, i, buf)
subroutine ctkin_getReactionType(self, i, buf)
implicit none
type(phase_t), intent(inout) :: self
integer, intent(in) :: i
character*(*), intent(out) :: buf
self%err = kin_getreactiontype(self%kin_id, i, buf)
end function ctkin_getReactionType
end subroutine ctkin_getReactionType

subroutine ctkin_getFwdRatesOfProgress(self, fwdROP)
implicit none
Expand Down
4 changes: 4 additions & 0 deletions src/fortran/fct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ typedef Cabinet<Transport> TransportCabinet;

typedef integer status_t;

template<> ThermoCabinet* ThermoCabinet::s_storage; // defined in ct.cpp
template<> KineticsCabinet* KineticsCabinet::s_storage; // defined in ct.cpp
template<> TransportCabinet* TransportCabinet::s_storage; // defined in ct.cpp

namespace {

ThermoPhase* _fph(const integer* n)
Expand Down
7 changes: 6 additions & 1 deletion src/kinetics/solveSP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,14 @@ int solveSP::solveSurfProb(int ifunc, doublereal time_scale, doublereal TKelvin,

void solveSP::updateState(const doublereal* CSolnSP)
{
vector_fp X;
size_t loc = 0;
for (size_t n = 0; n < m_numSurfPhases; n++) {
m_ptrsSurfPhase[n]->setConcentrations(CSolnSP + loc);
X.resize(m_nSpeciesSurfPhase[n]);
for (size_t k = 0; k < X.size(); k++) {
X[k] = CSolnSP[loc + k] / m_ptrsSurfPhase[n]->siteDensity();
}
m_ptrsSurfPhase[n]->setMoleFractions_NoNorm(X.data());
loc += m_nSpeciesSurfPhase[n];
}
}
Expand Down
41 changes: 31 additions & 10 deletions src/thermo/SurfPhase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,8 @@ void SurfPhase::getCp_R(doublereal* cpr) const

void SurfPhase::getStandardVolumes(doublereal* vol) const
{
_updateThermo();
for (size_t k = 0; k < m_kk; k++) {
vol[k] = 1.0/standardConcentration(k);
vol[k] = 0.0;
}
}

Expand Down Expand Up @@ -211,39 +210,55 @@ void SurfPhase::setSiteDensity(doublereal n0)
"Site density must be positive. Got {}", n0);
}
m_n0 = n0;
assignDensity(n0 * meanMolecularWeight());
m_logn0 = log(m_n0);
}

void SurfPhase::setCoverages(const doublereal* theta)
{
double sum = 0.0;
for (size_t k = 0; k < m_kk; k++) {
sum += theta[k];
sum += theta[k] / size(k);
}
if (sum <= 0.0) {
throw CanteraError("SurfPhase::setCoverages",
"Sum of Coverage fractions is zero or negative");
}
for (size_t k = 0; k < m_kk; k++) {
m_work[k] = m_n0*theta[k]/(sum*size(k));
m_work[k] = theta[k] / (sum * size(k));
}
// Call the Phase:: class function setConcentrations.
setConcentrations(m_work.data());
setMoleFractions(m_work.data());
}

void SurfPhase::setCoveragesNoNorm(const doublereal* theta)
{
double sum = 0.0;
double sum2 = 0.0;
for (size_t k = 0; k < m_kk; k++) {
m_work[k] = m_n0*theta[k]/size(k);
sum += theta[k] / size(k);
sum2 += theta[k];
}
setConcentrationsNoNorm(m_work.data());
if (sum <= 0.0) {
throw CanteraError("SurfPhase::setCoverages",
"Sum of Coverage fractions is zero or negative");
}
for (size_t k = 0; k < m_kk; k++) {
m_work[k] = theta[k] * sum2 / (sum * size(k));
}
setMoleFractions_NoNorm(m_work.data());
}

void SurfPhase::getCoverages(doublereal* theta) const
{
getConcentrations(theta);
double sum_X = 0.0;
double sum_X_s = 0.0;
getMoleFractions(theta);
for (size_t k = 0; k < m_kk; k++) {
sum_X += theta[k];
sum_X_s += theta[k] * size(k);
}
for (size_t k = 0; k < m_kk; k++) {
theta[k] *= size(k)/m_n0;
theta[k] *= size(k) * sum_X / sum_X_s;
}
}

Expand Down Expand Up @@ -281,6 +296,12 @@ void SurfPhase::setState(const AnyMap& state) {
ThermoPhase::setState(state);
}

void SurfPhase::compositionChanged()
{
ThermoPhase::compositionChanged();
assignDensity(m_n0 * meanMolecularWeight());
}

void SurfPhase::_updateThermo(bool force) const
{
doublereal tnow = temperature();
Expand Down
7 changes: 0 additions & 7 deletions test/data/consistency-cases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -271,15 +271,12 @@ ideal-surface:
phase: Pt-surf
atol_v: 1e-7
known-failures:
h_eq_u_plus_Pv: "Definition of volume is unclear. See GitHub Issue #1312"
gk_eq_hk_minus_T_sk:
"Implementation of s_k is incorrect. See GitHub Issue #1313"
s_eq_sum_sk_Xk:
"Implementation of s_k is incorrect. See GitHub Issue #1313"
g_eq_sum_gk_Xk:
"chemPotentials does not protect against inf. See GitHub Issue #1314"
dSdv_const_T_eq_dPdT_const_V:
"Compressibility of phase leads to inconsistent results. See GitHub Issue #1312"
states:
- {T: 800, P: 1 atm, coverages: {Pt(s): 0.5, H(s): 0.4, O(s): 0.1}}
- {T: 800, P: 5 atm, coverages: {H(s): 1.0}}
Expand All @@ -291,10 +288,6 @@ ideal-edge:
file: surface-phases.yaml
phase: TPB
atol_v: 1e3 # site density of 5e-18 kmol/m = linear molar volume of 2e17 m/kmol
known-failures:
h_eq_u_plus_Pv: "Definition of volume is unclear. See GitHub Issue #1312"
dSdv_const_T_eq_dPdT_const_V:
"Compressibility of phase leads to inconsistent results. See GitHub Issue #1312"
states:
- {T: 300, P: 1 atm}
- {T: 900, P: 20 atm}
Expand Down
28 changes: 28 additions & 0 deletions test/data/surface-phases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ phases:
site-density: 2.7063e-9 mol/cm^2
state: {T: 900 K, P: 1 atm, coverages: {Pt(s): 0.5, H(s): 0.4, O(s): 0.1}}

- name: Pt-multi-sites
thermo: ideal-surface
adjacent-phases: [gas]
kinetics: surface
reactions: [Pt-reactions, Pt-multisite-reactions]
species: [{Pt-surf-species: all}, {multi-site-species: all}]
site-density: 2.7063e-9 mol/cm^2
state:
T: 900 K
P: 1 atm
coverages: {Pt(s): 0.35, H(s): 0.4, O(s): 0.1, O2(s): 0.15}

- name: TPB
thermo: edge
species: [{tpb-species: [(tpb)]}]
Expand Down Expand Up @@ -102,6 +114,19 @@ Pt-surf-species:
- [1.9454180E+00, 9.1761647E-04, -1.1226719E-07, -9.9099624E-11,
2.4307699E-14, -1.4005187E+04, -1.1531663E+01]

multi-site-species:
- name: O2(s)
composition: {O: 2, Pt: 2}
sites: 2
thermo:
model: NASA7
temperature-ranges: [300, 1000, 3000]
data:
- [-9.4986904E-01, 7.4042305E-03, -1.0451424E-06, -6.1120420E-09,
3.3787992E-12, -1.3209912E+04, 3.6137905E+00]
- [1.9454180E+00, 9.1761647E-04, -1.1226719E-07, -9.9099624E-11,
2.4307699E-14, -1.4005187E+04, -1.1531663E+01]


tpb-species:
- name: (tpb)
Expand All @@ -120,6 +145,9 @@ Pt-reactions:
- equation: H + Pt(s) => H(s)
sticking-coefficient: [1.0, 0.0, 0.0]

Pt-multisite-reactions:
- equation: 2 O(s) <=> O2(s)
rate-constant: {A: 3.1e9 cm^2/mol/s, b: 0.0, Ea: 0.0}

graphite-anode-species:
- name: EC(e)
Expand Down
Loading