Skip to content

Commit

Permalink
[Kinetics] Add option for Motz & Wise correction to sticking reactions
Browse files Browse the repository at this point in the history
  • Loading branch information
speth committed Jul 28, 2016
1 parent 593ab8a commit 1e5eb8c
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 2 deletions.
1 change: 1 addition & 0 deletions include/cantera/kinetics/InterfaceKinetics.h
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ class InterfaceKinetics : public Kinetics
size_t index; //!< index of the sticking reaction in the full reaction list
double order; //!< exponent applied to site density term
double multiplier; //!< multiplicative factor in rate expression
bool use_motz_wise; //!< 'true' if Motz & Wise correction is being used
};

//! Data for sticking reactions
Expand Down
6 changes: 6 additions & 0 deletions include/cantera/kinetics/Reaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ class InterfaceReaction : public ElementaryReaction
//! rather than the forward rate constant
bool is_sticking_coefficient;

//! Set to true if `rate` is a sticking coefficient which should be
//! translated into a rate coefficient using the correction factor developed
//! by Motz & Wise for reactions with high (near-unity) sticking
//! coefficients. Defaults to 'false'.
bool use_motz_wise_correction;

//! For reactions with multiple non-surface species, the sticking species
//! needs to be explicitly identified.
std::string sticking_species;
Expand Down
1 change: 1 addition & 0 deletions interfaces/cython/cantera/_cantera.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera":
cdef cppclass CxxInterfaceReaction "Cantera::InterfaceReaction" (CxxElementaryReaction):
stdmap[string, CxxCoverageDependency] coverage_deps
cbool is_sticking_coefficient
cbool use_motz_wise_correction
string sticking_species

cdef extern from "cantera/kinetics/FalloffFactory.h" namespace "Cantera":
Expand Down
24 changes: 24 additions & 0 deletions interfaces/cython/cantera/ctml_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,17 @@ def _write(self, s, level = 0):
_valexport = ''
_valfmt = ''

# default for Motz & Wise correction
_motz_wise = None

def enable_motz_wise():
global _motz_wise
_motz_wise = True

def disable_motz_wise():
global _motz_wise
_motz_wise = False

def export_species(filename, fmt = 'CSV'):
global _valexport
global _valfmt
Expand Down Expand Up @@ -354,6 +365,8 @@ def write(outName=None):

r = x.addChild('reactionData')
r['id'] = 'reaction_data'
if _motz_wise is not None:
r['motz_wise'] = str(_motz_wise).lower()
for rx in _reactions:
rx.build(r)

Expand Down Expand Up @@ -1067,6 +1080,15 @@ def build(self, p, name='', a=None):
addFloat(c, 'e', cov[3], fmt = '%f', defunits = _ue)

class stick(Arrhenius):
def __init__(self, *args, **kwargs):
"""
:param motz_wise: 'True' if the Motz & Wise correction should be used,
'False' if not. If unspecified, use the mechanism default (set using
the functions `enable_motz_wise` or `disable_motz_wise`).
"""
self.motz_wise = kwargs.pop('motz_wise', None)
Arrhenius.__init__(self, *args, **kwargs)

def build(self, p, name=''):
a = p.addChild('Arrhenius')
a['type'] = 'stick'
Expand All @@ -1077,6 +1099,8 @@ def build(self, p, name=''):
+ str(ngas) + ': ' + str(self.gas_species))

a['species'] = self.gas_species[0]
if self.motz_wise is not None:
a['motz_wise'] = str(self.motz_wise).lower()
self.unit_factor = 1.0
Arrhenius.build(self, p, name, a)

Expand Down
14 changes: 14 additions & 0 deletions interfaces/cython/cantera/reaction.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,20 @@ cdef class InterfaceReaction(ElementaryReaction):
cdef CxxInterfaceReaction* r = <CxxInterfaceReaction*>self.reaction
r.is_sticking_coefficient = stick

property use_motz_wise_correction:
"""
Get/Set a boolean indicating whether to use the correction factor
developed by Motz & Wise for reactions with high (near-unity) sticking
coefficients when converting the sticking coefficient to a rate
coefficient.
"""
def __get__(self):
cdef CxxInterfaceReaction* r = <CxxInterfaceReaction*>self.reaction
return r.use_motz_wise_correction
def __set__(self, mw):
cdef CxxInterfaceReaction* r = <CxxInterfaceReaction*>self.reaction
r.use_motz_wise_correction = mw

property sticking_species:
"""
The name of the sticking species. Needed only for reactions with
Expand Down
25 changes: 25 additions & 0 deletions interfaces/cython/cantera/test/test_kinetics.py
Original file line number Diff line number Diff line change
Expand Up @@ -967,3 +967,28 @@ def test_modify_sticking(self):
surf.modify_reaction(2, R)
k2 = surf.forward_rate_constants[2]
self.assertNear(k1, 4*k2)

def test_motz_wise(self):
# Motz & Wise off for all reactions
gas1 = ct.Solution('ptcombust.xml', 'gas')
surf1 = ct.Interface('ptcombust.xml', 'Pt_surf', [gas1])
surf1.coverages = 'O(S):0.1, PT(S):0.5, H(S):0.4'
gas1.TP = surf1.TP

# Motz & Wise correction on for some reactions
gas2 = ct.Solution('../data/ptcombust-motzwise.cti', 'gas')
surf2 = ct.Interface('../data/ptcombust-motzwise.cti', 'Pt_surf', [gas2])
surf2.TPY = surf1.TPY

k1 = surf1.forward_rate_constants
k2 = surf2.forward_rate_constants

# M&W toggled on (globally) for reactions 2 and 7
self.assertNear(2.0 * k1[2], k2[2]) # sticking coefficient = 1.0
self.assertNear(1.6 * k1[7], k2[7]) # sticking coefficient = 0.75

# M&W toggled off (locally) for reaction 4
self.assertNear(k1[4], k2[4])

# M&W toggled on (locally) for reaction 9
self.assertNear(2.0 * k1[9], k2[9]) # sticking coefficient = 1.0
8 changes: 6 additions & 2 deletions src/kinetics/InterfaceKinetics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,14 +791,15 @@ SurfaceArrhenius InterfaceKinetics::buildSurfaceArrhenius(
}

if (!replace) {
m_stickingData.emplace_back(
StickData{i, surface_order, multiplier});
m_stickingData.emplace_back(StickData{i, surface_order, multiplier,
r.use_motz_wise_correction});
} else {
// Modifying an existing sticking reaction.
for (auto& item : m_stickingData) {
if (item.index == i) {
item.order = surface_order;
item.multiplier = multiplier;
item.use_motz_wise = r.use_motz_wise_correction;
break;
}
}
Expand Down Expand Up @@ -1007,6 +1008,9 @@ void InterfaceKinetics::applyStickingCorrection(double T, double* kf)

for (size_t n = 0; n < m_stickingData.size(); n++) {
const StickData& item = m_stickingData[n];
if (item.use_motz_wise) {
kf[item.index] /= 1 - 0.5 * kf[item.index];
}
kf[item.index] *= factors[n] * sqrt(T) * item.multiplier;
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/kinetics/Reaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ ChebyshevReaction::ChebyshevReaction(const Composition& reactants_,

InterfaceReaction::InterfaceReaction()
: is_sticking_coefficient(false)
, use_motz_wise_correction(false)
{
reaction_type = INTERFACE_RXN;
}
Expand Down Expand Up @@ -481,6 +482,19 @@ void setupInterfaceReaction(InterfaceReaction& R, const XML_Node& rxn_node)
if (lowercase(arr["type"]) == "stick") {
R.is_sticking_coefficient = true;
R.sticking_species = arr["species"];

if (lowercase(arr["motz_wise"]) == "true") {
R.use_motz_wise_correction = true;
} else if (lowercase(arr["motz_wise"]) == "false") {
R.use_motz_wise_correction = false;
} else {
// Default value for all reactions
XML_Node* parent = rxn_node.parent();
if (parent && parent->name() == "reactionData"
&& lowercase((*parent)["motz_wise"]) == "true") {
R.use_motz_wise_correction = true;
}
}
}
std::vector<XML_Node*> cov = arr.getChildren("coverage");
for (const auto& node : cov) {
Expand Down
119 changes: 119 additions & 0 deletions test/data/ptcombust-motzwise.cti
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
units(length = "cm", time = "s", quantity = "mol", act_energy = "J/mol")

enable_motz_wise()

ideal_gas(name = "gas",
elements = "O H C N Ar",
species = """gri30: H2 H O O2 OH
H2O HO2 H2O2
C CH CH2 CH2(S) CH3 CH4 CO CO2
HCO CH2O CH2OH CH3O CH3OH C2H C2H2 C2H3
C2H4 C2H5 C2H6 HCCO CH2CO HCCOH AR N2""",
transport = 'Mix',
reactions = 'gri30: all',
options = ['skip_undeclared_elements',
'skip_undeclared_species'],
initial_state = state(temperature = 300.0, pressure = OneAtm,
mole_fractions = 'CH4:0.095, O2:0.21, AR:0.79')
)

ideal_interface(name = "Pt_surf",
elements = " Pt H O C ",
species = """ ptcombust: PT(S) H(S) H2O(S) OH(S) CO(S)
CO2(S) CH3(S) CH2(S)s CH(S) C(S) O(S) """,
phases = "gas",
site_density = 2.7063e-9,
reactions = "all",
initial_state = state(temperature = 900.0,
coverages = 'O(S):0.0, PT(S):0.5, H(S):0.5')
)

# Reaction 1
surface_reaction("H2 + 2 PT(S) => 2 H(S)", [4.45790E+10, 0.5, 0],
order = "PT(S):1")

# Reaction 2
surface_reaction( "2 H(S) => H2 + 2 PT(S)",
Arrhenius(3.70000E+21, 0, 67400,
coverage = ['H(S)', 0.0, 0.0, -6000.0]))

# Reaction 3
surface_reaction( "H + PT(S) => H(S)", stick(1.00000E+00, 0, 0))

# Reaction 4
surface_reaction( "O2 + 2 PT(S) => 2 O(S)", Arrhenius(1.80000E+21, -0.5, 0),
options = 'duplicate')

# Reaction 5
surface_reaction( "O2 + 2 PT(S) => 2 O(S)", stick(2.30000E-02, 0, 0, motz_wise=False),
options = 'duplicate')

# Reaction 6
surface_reaction( "2 O(S) => O2 + 2 PT(S)",
Arrhenius(3.70000E+21, 0, 213200,
coverage = ['O(S)', 0.0, 0.0, -60000.0]) )

# Reaction 7
surface_reaction( "O + PT(S) => O(S)", stick(1.00000E+00, 0, 0))

# Reaction 8
surface_reaction( "H2O + PT(S) => H2O(S)", stick(7.50000E-01, 0, 0))

# Reaction 9
surface_reaction( "H2O(S) => H2O + PT(S)", [1.00000E+13, 0, 40300])

# Reaction 10
surface_reaction( "OH + PT(S) => OH(S)", stick(1.00000E+00, 0, 0, motz_wise=True))

# Reaction 11
surface_reaction( "OH(S) => OH + PT(S)", [1.00000E+13, 0, 192800])

# Reaction 12
surface_reaction( "H(S) + O(S) <=> OH(S) + PT(S)", [3.70000E+21, 0, 11500])

# Reaction 13
surface_reaction( "H(S) + OH(S) <=> H2O(S) + PT(S)", [3.70000E+21, 0, 17400])

# Reaction 14
surface_reaction( "OH(S) + OH(S) <=> H2O(S) + O(S)", [3.70000E+21, 0, 48200])

# Reaction 15
surface_reaction( "CO + PT(S) => CO(S)", [1.61800E+20, 0.5, 0],
order = "PT(S):2")

# Reaction 16
surface_reaction( "CO(S) => CO + PT(S)", [1.00000E+13, 0, 125500])

# Reaction 17
surface_reaction( "CO2(S) => CO2 + PT(S)", [1.00000E+13, 0, 20500])

# Reaction 18
surface_reaction( "CO(S) + O(S) => CO2(S) + PT(S)", [3.70000E+21, 0, 105000])

# Reaction 19
surface_reaction( "CH4 + 2 PT(S) => CH3(S) + H(S)", [4.63340E+20, 0.5, 0],
order = "PT(S):2.3")

# Reaction 20
surface_reaction( "CH3(S) + PT(S) => CH2(S)s + H(S)",
[3.70000E+21, 0, 20000])

# Reaction 21
surface_reaction( "CH2(S)s + PT(S) => CH(S) + H(S)", [3.70000E+21, 0, 20000])

# Reaction 22
surface_reaction( "CH(S) + PT(S) => C(S) + H(S)", [3.70000E+21, 0, 20000])

# Reaction 23
surface_reaction( "C(S) + O(S) => CO(S) + PT(S)", [3.70000E+21, 0, 62800])

# Reaction 24
surface_reaction( "CO(S) + PT(S) => C(S) + O(S)", [1.00000E+18, 0, 184000])

# Reaction 25 (12/28/2009 HKM added: This is a fictious rxn that is added for numerical stability.
# The issue is that if multiple surface species have a negative concentration, the
# Jacobian for the surface problem will go singular due to the way negative concentrations
# are truncated within Cantera. Adding in unimolecular desorption rxns with neglibigle real
# effects alleviates the problem.)
surface_reaction( "C(S) => C + PT(S)", [3.7E7, 0, 62800])

0 comments on commit 1e5eb8c

Please sign in to comment.