Skip to content

Commit 5e343e0

Browse files
committed
[yaml_cantera1] handle negative A factors (also fixes to_cantera)
We add handlers to the to_cantera methods to set the allow_negative_pre_exponential_factor property of the Cantera object, which Cantera then correctly passes to the input_data dictionary. We also have a safety-net in the yaml writing stage, but maybe this is unnecessary and slows us a bit. It doesn't seem to get triggered by the unit tests.
1 parent eb6a252 commit 5e343e0

5 files changed

Lines changed: 92 additions & 6 deletions

File tree

rmgpy/kinetics/arrhenius.pyx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,10 @@ cdef class Arrhenius(KineticsModel):
272272
if arrhenius_class:
273273
return ct.Arrhenius(A, b, E)
274274
else:
275-
return ct.ArrheniusRate(A, b, E)
275+
rate = ct.ArrheniusRate(A, b, E)
276+
if A < 0:
277+
rate.allow_negative_pre_exponential_factor = True
278+
return rate
276279

277280
def set_cantera_kinetics(self, ct_reaction, species_list):
278281
"""
@@ -784,7 +787,10 @@ cdef class ArrheniusBM(KineticsModel):
784787
Ea = self._E0.value_si * 1000 # convert from J/mol to J/kmol
785788
w = self._w0.value_si * 1000 # convert from J/mol to J/kmol
786789

787-
return ct.BlowersMaselRate(A, b, Ea, w)
790+
rate = ct.BlowersMaselRate(A, b, Ea, w)
791+
if A < 0:
792+
rate.allow_negative_pre_exponential_factor = True
793+
return rate
788794

789795
def set_cantera_kinetics(self, ct_reaction, species_list):
790796
"""

rmgpy/kinetics/falloff.pyx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,12 @@ cdef class Lindemann(PDepKineticsModel):
236236

237237
high_rate = self.arrheniusHigh.to_cantera_kinetics(arrhenius_class=True)
238238
low_rate = self.arrheniusLow.to_cantera_kinetics(arrhenius_class=True)
239-
return ct.LindemannRate(low=low_rate, high=high_rate)
239+
rate = ct.LindemannRate()
240+
if high_rate.pre_exponential_factor < 0 or low_rate.pre_exponential_factor < 0:
241+
rate.allow_negative_pre_exponential_factor = True
242+
rate.high_rate = high_rate
243+
rate.low_rate = low_rate
244+
return rate
240245

241246

242247
################################################################################
@@ -415,4 +420,10 @@ cdef class Troe(PDepKineticsModel):
415420

416421
high = self.arrheniusHigh.to_cantera_kinetics(arrhenius_class=True)
417422
low = self.arrheniusLow.to_cantera_kinetics(arrhenius_class=True)
418-
return ct.TroeRate(high=high, low=low, falloff_coeffs=falloff)
423+
rate = ct.TroeRate()
424+
if high.pre_exponential_factor < 0 or low.pre_exponential_factor < 0:
425+
rate.allow_negative_pre_exponential_factor = True
426+
rate.high_rate = high
427+
rate.low_rate = low
428+
rate.falloff_coeffs = falloff
429+
return rate

rmgpy/kinetics/surface.pyx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,10 @@ cdef class StickingCoefficient(KineticsModel):
299299
b = self._n.value_si
300300
E = self._Ea.value_si * 1000 # convert from J/mol to J/kmol
301301

302-
return ct.StickingArrheniusRate(A, b, E)
302+
rate = ct.StickingArrheniusRate(A, b, E)
303+
if A < 0:
304+
rate.allow_negative_pre_exponential_factor = True
305+
return rate
303306

304307

305308
def set_cantera_kinetics(self, ct_reaction, species_list):
@@ -654,7 +657,10 @@ cdef class SurfaceArrhenius(Arrhenius):
654657

655658
b = self._n.value_si
656659
E = self._Ea.value_si * 1000 # convert from J/mol to J/kmol
657-
return ct.InterfaceArrheniusRate(A, b, E)
660+
rate = ct.InterfaceArrheniusRate(A, b, E)
661+
if A < 0:
662+
rate.allow_negative_pre_exponential_factor = True
663+
return rate
658664

659665
def set_cantera_kinetics(self, ct_reaction, species_list):
660666
"""

rmgpy/yaml_cantera1.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,30 @@ def reaction_to_dicts(obj, spcs):
459459
# spurious 'efficiencies' entry in input_data.
460460
# see https://github.com/Cantera/cantera/issues/2115
461461
reaction_data.pop("efficiencies", None)
462+
463+
# This next block is hopefully unnecessary, and probably slows us a little
464+
# so maybe should be removed. For now it is here for safety.
465+
for rate_key in (
466+
"rate-constant",
467+
"high-P-rate-constant",
468+
"low-P-rate-constant",
469+
"sticking-coefficient",
470+
):
471+
if reaction_data.get(rate_key, {}).get("A", 0) < 0:
472+
if not reaction_data.get("negative-A", False):
473+
logging.warning(
474+
"%s\n%s",
475+
"Reaction did not have the negative-A flag set to True.",
476+
yaml.dump(
477+
reaction_data,
478+
Dumper=Dumper,
479+
default_flow_style=False,
480+
sort_keys=False,
481+
).rstrip(),
482+
)
483+
reaction_data["negative-A"] = True
484+
break
485+
462486
# Convert any AnyMap objects to regular dicts before appending
463487
reaction_data = _convert_anymap_to_dict(reaction_data)
464488

test/rmgpy/yaml_cantera1Test.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from rmgpy.transport import TransportData
4444
from rmgpy.kinetics import (
4545
Arrhenius,
46+
Lindemann,
4647
PDepArrhenius,
4748
Troe,
4849
ThirdBody,
@@ -164,6 +165,13 @@ def test_reaction_to_dicts_arrhenius_equation_and_rate(self):
164165
assert np.isclose(d["rate-constant"]["b"], 0.5)
165166
assert np.isclose(d["rate-constant"]["Ea"], 10e6) # 10 kJ/mol → 1e7 J/kmol
166167

168+
def test_reaction_to_dicts_negative_a_arrhenius(self):
169+
"""Negative Arrhenius A factors are marked for Cantera."""
170+
kin = Arrhenius(A=(-1e13, "s^-1"), n=0.5, Ea=(10, "kJ/mol"), T0=(1, "K"))
171+
rxn = Reaction(reactants=[self.h2], products=[self.h, self.h], kinetics=kin)
172+
entries = reaction_to_dicts(rxn, self.all_gas)
173+
assert entries[0]["negative-A"] is True
174+
167175
def test_reaction_to_dicts_thirdbody(self):
168176
"""ThirdBody: type is three-body, equation uses M, efficiencies map present."""
169177
kin = ThirdBody(
@@ -234,6 +242,24 @@ def test_reaction_to_dicts_troe(self):
234242
assert "efficiencies" in d
235243
assert np.isclose(d["efficiencies"]["Ar(3)"], 2.0)
236244

245+
def test_reaction_to_dicts_negative_a_falloff(self):
246+
"""Falloff rates with negative high- and low-pressure A factors are marked for Cantera."""
247+
kin = Lindemann(
248+
arrheniusHigh=Arrhenius(
249+
A=(-1e14, "s^-1"), n=0, Ea=(10, "kJ/mol"), T0=(1, "K")
250+
),
251+
arrheniusLow=Arrhenius(
252+
A=(-1e20, "cm^3/(mol*s)"), n=0, Ea=(10, "kJ/mol"), T0=(1, "K")
253+
),
254+
efficiencies={self.ar.molecule[0]: 2.0},
255+
)
256+
rxn = Reaction(reactants=[self.h], products=[self.h], kinetics=kin)
257+
entries = reaction_to_dicts(rxn, self.all_gas)
258+
d = entries[0]
259+
assert d["negative-A"] is True
260+
assert d["low-P-rate-constant"]["A"] < 0
261+
assert np.isclose(d["efficiencies"]["Ar(3)"], 2.0)
262+
237263
# ------------------------------------------------------------------
238264
# reaction_to_dicts — surface kinetics
239265
# ------------------------------------------------------------------
@@ -271,6 +297,19 @@ def test_reaction_to_dicts_sticking_coefficient(self):
271297
assert np.isclose(d["sticking-coefficient"]["A"], 0.1)
272298
assert np.isclose(d["sticking-coefficient"]["Ea"], 0.0)
273299

300+
def test_reaction_to_dicts_negative_a_sticking_coefficient(self):
301+
"""Negative sticking-coefficient A factors are marked for Cantera."""
302+
kin = StickingCoefficient(
303+
A=(-0.1, ""), n=0, Ea=(0, "kJ/mol"), T0=(1, "K")
304+
)
305+
rxn = Reaction(
306+
reactants=[self.h2, self.x, self.x],
307+
products=[self.hx, self.hx],
308+
kinetics=kin,
309+
)
310+
entries = reaction_to_dicts(rxn, self.all_surface)
311+
assert entries[0]["negative-A"] is True
312+
274313
def test_reaction_to_dicts_coverage_dependence(self):
275314
"""Coverage-dependent kinetics: coverage-dependencies block present with correct units.
276315

0 commit comments

Comments
 (0)