diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 0113dfe85..353e66111 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -462,3 +462,43 @@ class TestSofcKinetics(utilities.CanteraTest): anode_bulk.electric_potential]) self.compare(data, '../data/sofc-test.csv') + + +class TestDuplicateReactions(utilities.CanteraTest): + infile = 'duplicate-reactions.cti' + + def check(self, name): + with self.assertRaises(Exception) as cm: + ct.Solution(self.infile, name) + self.assertIn('duplicate reaction', str(cm.exception)) + + def test_forward_multiple(self): + self.check('A') + + def test_opposite_direction1(self): + self.check('B') + + def test_opposite_direction2(self): + self.check('C') + + def test_opposite_direction3(self): + self.check('D') + + def test_opposite_direction4(self): + gas = ct.Solution(self.infile, 'E') + self.assertEqual(gas.n_reactions, 2) + + def test_common_efficiencies(self): + self.check('F') + + def test_disjoint_efficiencies(self): + gas = ct.Solution(self.infile, 'G') + self.assertEqual(gas.n_reactions, 2) + + def test_different_type(self): + gas = ct.Solution(self.infile, 'H') + self.assertEqual(gas.n_reactions, 2) + + def test_declared_duplicate(self): + gas = ct.Solution(self.infile, 'I') + self.assertEqual(gas.n_reactions, 2) diff --git a/src/kinetics/importKinetics.cpp b/src/kinetics/importKinetics.cpp index d37bd809a..46ba2c7cc 100644 --- a/src/kinetics/importKinetics.cpp +++ b/src/kinetics/importKinetics.cpp @@ -1027,15 +1027,13 @@ bool rxninfo::installReaction(int iRxn, const XML_Node& rxnNode, Kinetics& kin, } for (size_t nn = 0; nn < rdata.products.size(); nn++) { rdata.net_stoich[int(rdata.products[nn])+1] += rdata.pstoich[nn]; - participants += 1000000 * static_cast(rdata.products[nn]); + participants += static_cast(rdata.products[nn]); } vector& related = m_participants[participants]; for (size_t mm = 0; mm < related.size(); mm++) { ReactionData& other = *m_rdata[related[mm]]; - if (rdata.reactants.size() != other.reactants.size()) { - continue; // different numbers of reactants - } else if (rdata.reactionType != other.reactionType) { + if (rdata.reactionType != other.reactionType) { continue; // different reaction types } else if (rdata.duplicate && other.duplicate) { continue; // marked duplicates diff --git a/test/data/duplicate-reactions.cti b/test/data/duplicate-reactions.cti new file mode 100644 index 000000000..0cb914912 --- /dev/null +++ b/test/data/duplicate-reactions.cti @@ -0,0 +1,59 @@ +units(length='cm', time='s', quantity='mol', act_energy='cal/mol') + +def make_gas(name, reactions): + ideal_gas(name=name, + elements=" O H Ar ", + species="""h2o2: H2 H O O2 OH H2O HO2 H2O2 AR """, + reactions=reactions, + transport="None") + +make_gas('A', 'A-*') +make_gas('B', 'B-*') +make_gas('C', 'C-*') +make_gas('D', 'D-*') +make_gas('E', 'E-*') +make_gas('F', 'F-*') +make_gas('G', 'G-*') +make_gas('H', 'H-*') +make_gas('I', 'I-*') + +kf = [1e10, 0, 100] + +# Forward multiple (not allowed) +reaction('O + H2 <=> H + OH', kf, id='A-1') +reaction('2 O + 2 H2 <=> 2 H + 2 OH', kf, id='A-2') + +# Opposite direction #1 (not allowed) +reaction('O + H2 <=> H + OH', kf, id='B-1') +reaction('H + OH <=> O + H2', kf, id='B-2') + +# Opposite direction #2 (not allowed) +reaction('H + O2 + AR <=> HO2 + AR', kf, id='C-1') +reaction('HO2 + AR <=> H + O2 + AR', kf, id='C-2') + +# Opposite direction #3 (not allowed) +reaction('H + O2 + AR <=> HO2 + AR', kf, id='D-1') +reaction('HO2 + AR => H + O2 + AR', kf, id='D-2') + +# Opposite direction #4 (allowed) +reaction('H + O2 + AR => HO2 + AR', kf, id='E-1') +reaction('HO2 + AR => H + O2 + AR', kf, id='E-2') + +# Common efficiencies (not allowed) +falloff_reaction('2 OH (+ M) <=> H2O2 (+ M)', kf=kf, kf0=kf, + efficiencies='H2:2.0 H2O:6.0', id='F-1') +falloff_reaction('2 OH (+ M) <=> H2O2 (+ M)', kf=kf, kf0=kf, + efficiencies='AR:0.7 H2:2.0', id='F-2') + +# Disjoint efficiencies (allowed) +falloff_reaction('2 OH (+ H2O) <=> H2O2 (+ H2O)', kf=kf, kf0=kf, id='G-1') +falloff_reaction('2 OH (+ M) <=> H2O2 (+ M)', kf=kf, kf0=kf, + efficiencies='AR:0.7 H2:2.0 H2O:0.0', id='G-2') + +# Different reaction type (allowed) +reaction('O + H2 <=> H + OH', kf, id='H-1') +three_body_reaction('O + H2 + M <=> H + OH + M', kf, id='H-2') + +# Declared duplicate (allowed) +reaction('O + H2 <=> H + OH', kf, options='duplicate', id='I-1') +reaction('H + OH <=> O + H2', kf, options='duplicate', id='I-2')