From c994917bae0a2d226b846a5202e0a9a1e6d4d911 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Mon, 27 Apr 2015 18:00:42 -0400 Subject: [PATCH] [Kinetics] Add functions for creating Reaction objects from CTI/XML --- include/cantera/kinetics/Reaction.h | 21 +++++++ interfaces/cython/cantera/_cantera.pxd | 3 + interfaces/cython/cantera/reaction.pyx | 55 +++++++++++++++++++ .../cython/cantera/test/test_kinetics.py | 49 +++++++++++++++++ src/kinetics/Reaction.cpp | 15 +++++ 5 files changed, 143 insertions(+) diff --git a/include/cantera/kinetics/Reaction.h b/include/cantera/kinetics/Reaction.h index ffdbfb64d..6c9cfa2f4 100644 --- a/include/cantera/kinetics/Reaction.h +++ b/include/cantera/kinetics/Reaction.h @@ -255,6 +255,27 @@ public: //! Create a new Reaction object for the reaction defined in `rxn_node` shared_ptr newReaction(const XML_Node& rxn_node); + +//! Create Reaction objects for all nodes in an XML document. +//! +//! The `` nodes are assumed to be children of the `` +//! node in an XML document with a `` root node, as in the case of XML +//! files produced by conversion from CTI files. +//! +//! This function can be used in combination with get_XML_File() and +//! get_XML_from_string() to get Reaction objects from either a file or a +//! string, respectively, where the string or file is formatted as either CTI +//! or XML. +//! +//! If Reaction objects are being created from a CTI definition that does not +//! contain corresponding phase definitions, then one of the following must be +//! true, or the resulting rate constants will be incorrect: +//! +//! - The rate constants are expressed in (kmol, meter, second) units +//! - A `units` directive is included **and** all reactions take place in +//! bulk (e.g. gas) phases +std::vector > getReactions(const XML_Node& node); + } #endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index f88224f64..1a5ec007b 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -192,6 +192,9 @@ cdef extern from "cantera/thermo/SurfPhase.h": cdef extern from "cantera/kinetics/Reaction.h" namespace "Cantera": + cdef shared_ptr[CxxReaction] CxxNewReaction "newReaction" (XML_Node&) + cdef vector[shared_ptr[CxxReaction]] CxxGetReactions "getReactions" (XML_Node&) + cdef cppclass CxxArrhenius "Cantera::Arrhenius": CxxArrhenius() CxxArrhenius(double, double, double) diff --git a/interfaces/cython/cantera/reaction.pyx b/interfaces/cython/cantera/reaction.pyx index 50136b19b..52a071bd7 100644 --- a/interfaces/cython/cantera/reaction.pyx +++ b/interfaces/cython/cantera/reaction.pyx @@ -22,6 +22,61 @@ cdef class Reaction: self._reaction = other self.reaction = self._reaction.get() + @staticmethod + def fromCti(text): + """ + Create a Reaction object from its CTI string representation. + """ + cxx_reactions = CxxGetReactions(deref(CxxGetXmlFromString(stringify(text)))) + assert cxx_reactions.size() == 1, cxx_reactions.size() + return wrapReaction(cxx_reactions[0]) + + @staticmethod + def fromXml(text): + """ + Create a Reaction object from its XML string representation. + """ + cxx_reaction = CxxNewReaction(deref(CxxGetXmlFromString(stringify(text)))) + return wrapReaction(cxx_reaction) + + @staticmethod + def listFromFile(filename): + """ + Create a list of Reaction objects from all of the reactions defined in a + CTI or XML file. + + Directories on Cantera's input file path will be searched for the + specified file. + + In the case of an XML file, the nodes are assumed to be + children of the node in a document with a root + node, as in the XML files produced by conversion from CTI files. + """ + cxx_reactions = CxxGetReactions(deref(CxxGetXmlFile(stringify(filename)))) + return [wrapReaction(r) for r in cxx_reactions] + + @staticmethod + def listFromXml(text): + """ + Create a list of Reaction objects from all the reaction defined in an + XML string. The nodes are assumed to be children of the + node in a document with a root node, as in the XML + files produced by conversion from CTI files. + """ + cxx_reactions = CxxGetReactions(deref(CxxGetXmlFromString(stringify(text)))) + return [wrapReaction(r) for r in cxx_reactions] + + @staticmethod + def listFromCti(text): + """ + Create a list of Species objects from all the species defined in a CTI + string. + """ + # Currently identical to listFromXml since get_XML_from_string is able + # to distinguish between CTI and XML. + cxx_reactions = CxxGetReactions(deref(CxxGetXmlFromString(stringify(text)))) + return [wrapReaction(r) for r in cxx_reactions] + property reactant_string: def __get__(self): return pystr(self.reaction.reactantString()) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 8bc9049be..3723c6de0 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -510,3 +510,52 @@ class TestDuplicateReactions(utilities.CanteraTest): def test_declared_duplicate(self): gas = ct.Solution(self.infile, 'I') self.assertEqual(gas.n_reactions, 2) + + +class TestReaction(utilities.CanteraTest): + @classmethod + def setUpClass(cls): + cls.gas = ct.Solution('h2o2.xml') + + def test_fromCti(self): + r = ct.Reaction.fromCti('''three_body_reaction('2 O + M <=> O2 + M', + [1.200000e+11, -1.0, 0.0], efficiencies='AR:0.83 H2:2.4 H2O:15.4')''') + + self.assertTrue(isinstance(r, ct.ThirdBodyReaction)) + self.assertEqual(r.reactants['O'], 2) + self.assertEqual(r.products['O2'], 1) + self.assertEqual(r.efficiencies['H2O'], 15.4) + self.assertEqual(r.rate.temperature_exponent, -1.0) + + def test_fromXml(self): + import xml.etree.ElementTree as ET + root = ET.parse('../../build/data/h2o2.xml').getroot() + rxn_node = root.find('.//reaction[@id="0001"]') + r = ct.Reaction.fromXml(ET.tostring(rxn_node)) + + self.assertTrue(isinstance(r, ct.ThirdBodyReaction)) + self.assertEqual(r.reactants['O'], 2) + self.assertEqual(r.products['O2'], 1) + self.assertEqual(r.efficiencies['H2O'], 15.4) + self.assertEqual(r.rate.temperature_exponent, -1.0) + + def test_listFromFile(self): + R = ct.Reaction.listFromFile('h2o2.xml') + eq1 = [r.equation for r in R] + eq2 = [self.gas.reaction(i).equation + for i in range(self.gas.n_reactions)] + self.assertEqual(eq1, eq2) + + def test_listFromCti(self): + R = ct.Reaction.listFromCti(open('../../build/data/h2o2.cti').read()) + eq1 = [r.equation for r in R] + eq2 = [self.gas.reaction(i).equation + for i in range(self.gas.n_reactions)] + self.assertEqual(eq1, eq2) + + def test_listFromXml(self): + R = ct.Reaction.listFromCti(open('../../build/data/h2o2.xml').read()) + eq1 = [r.equation for r in R] + eq2 = [self.gas.reaction(i).equation + for i in range(self.gas.n_reactions)] + self.assertEqual(eq1, eq2) diff --git a/src/kinetics/Reaction.cpp b/src/kinetics/Reaction.cpp index 7bd3359e6..997a260f5 100644 --- a/src/kinetics/Reaction.cpp +++ b/src/kinetics/Reaction.cpp @@ -673,4 +673,19 @@ shared_ptr newReaction(const XML_Node& rxn_node) } } +std::vector > getReactions(const XML_Node& node) +{ + std::vector > all_reactions; + std::vector reaction_nodes = + node.child("reactionData").getChildren("reaction"); + + for (std::vector::iterator iter = reaction_nodes.begin(); + iter != reaction_nodes.end(); + ++iter) + { + all_reactions.push_back(newReaction(**iter)); + } + return all_reactions; +} + }