diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 12afaeb33..e0590a9d6 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -651,6 +651,9 @@ class TestDuplicateReactions(utilities.CanteraTest): gas = ct.Solution(self.infile, 'I') self.assertEqual(gas.n_reactions, 2) + def test_unmatched_duplicate(self): + self.check('J') + class TestReaction(utilities.CanteraTest): @classmethod diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index a316b91d2..cf722ae8a 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -12,6 +12,7 @@ #include "cantera/kinetics/Kinetics.h" #include "cantera/kinetics/Reaction.h" #include "cantera/base/stringUtils.h" +#include using namespace std; @@ -156,6 +157,12 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const //! Map of (key indicating participating species) to reaction numbers std::map > participants; std::vector > net_stoich; + std::unordered_set unmatched_duplicates; + for (size_t i = 0; i < m_reactions.size(); i++) { + if (m_reactions[i]->duplicate) { + unmatched_duplicates.insert(i); + } + } for (size_t i = 0; i < m_reactions.size(); i++) { // Get data about this reaction @@ -178,10 +185,13 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const vector& related = participants[key]; for (size_t m = 0; m < related.size(); m++) { Reaction& other = *m_reactions[related[m]]; - if (R.reaction_type != other.reaction_type) { + if (R.duplicate && other.duplicate) { + // marked duplicates + unmatched_duplicates.erase(i); + unmatched_duplicates.erase(related[m]); + continue; + } else if (R.reaction_type != other.reaction_type) { continue; // different reaction types - } else if (R.duplicate && other.duplicate) { - continue; // marked duplicates } doublereal c = checkDuplicateStoich(net_stoich[i], net_stoich[m]); if (c == 0) { @@ -223,7 +233,7 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const } } if (throw_err) { - throw CanteraError("installReaction", + throw CanteraError("Kinetics::checkDuplicates", "Undeclared duplicate reactions detected:\n" "Reaction {}: {}\nReaction {}: {}\n", i+1, other.equation(), m+1, R.equation()); @@ -233,6 +243,16 @@ std::pair Kinetics::checkDuplicates(bool throw_err) const } participants[key].push_back(i); } + if (unmatched_duplicates.size()) { + size_t i = *unmatched_duplicates.begin(); + if (throw_err) { + throw CanteraError("Kinetics::checkDuplicates", + "No duplicate found for declared duplicate reaction number {}" + " ({})", i, m_reactions[i]->equation()); + } else { + return {i, i}; + } + } return {npos, npos}; } diff --git a/test/data/duplicate-reactions.cti b/test/data/duplicate-reactions.cti index 0cb914912..4774a74b2 100644 --- a/test/data/duplicate-reactions.cti +++ b/test/data/duplicate-reactions.cti @@ -16,6 +16,7 @@ make_gas('F', 'F-*') make_gas('G', 'G-*') make_gas('H', 'H-*') make_gas('I', 'I-*') +make_gas('J', ['C-1', 'F-1', 'I-1']) # unmatched duplicate kf = [1e10, 0, 100]