From a4ba47594c2cf8c9a190f29c3cb22028e34a4315 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Mon, 20 Apr 2015 18:32:49 -0400 Subject: [PATCH] [Python] Add functions for creating Species objects from CTI/XML --- include/cantera/thermo/Species.h | 11 +++ interfaces/cython/cantera/_cantera.pxd | 5 +- interfaces/cython/cantera/test/test_thermo.py | 83 ++++++++++++++++--- interfaces/cython/cantera/thermo.pyx | 74 +++++++++++++++++ src/thermo/Species.cpp | 15 ++++ 5 files changed, 177 insertions(+), 11 deletions(-) diff --git a/include/cantera/thermo/Species.h b/include/cantera/thermo/Species.h index f2651211d..f2d53c1b3 100644 --- a/include/cantera/thermo/Species.h +++ b/include/cantera/thermo/Species.h @@ -53,6 +53,17 @@ public: //! Create a new Species object from a 'species' XML_Node. shared_ptr newSpecies(const XML_Node& species_node); +//! Generate Species 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 Species objects from either a file or a string, +//! respectively, where the string or file is formatted as either CTI or XML. +std::vector > getSpecies(const XML_Node& node); + } #endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 2f1f7f987..a302b6d67 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -53,7 +53,7 @@ cdef extern from "cantera/thermo/SpeciesThermoFactory.h": cdef CxxSpeciesThermo* CxxNewSpeciesThermo "Cantera::newSpeciesThermoInterpType"\ (int, double, double, double, double*) except + -cdef extern from "cantera/thermo/Species.h": +cdef extern from "cantera/thermo/Species.h" namespace "Cantera": cdef cppclass CxxSpecies "Cantera::Species": CxxSpecies() CxxSpecies(string, stdmap[string,double]) @@ -64,6 +64,9 @@ cdef extern from "cantera/thermo/Species.h": double charge double size + cdef shared_ptr[CxxSpecies] CxxNewSpecies "newSpecies" (XML_Node&) + cdef vector[shared_ptr[CxxSpecies]] CxxGetSpecies "getSpecies" (XML_Node&) + cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": cdef cppclass CxxThermoPhase "Cantera::ThermoPhase": CxxThermoPhase() diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index 6563536c4..c4b6fbd9c 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -627,6 +627,9 @@ ideal_gas(name='spam', elements='O H', class TestSpecies(utilities.CanteraTest): + def setUp(self): + self.gas = ct.Solution('h2o2.xml') + def test_standalone(self): s = ct.Species('CH4', {'C':1, 'H':4}) @@ -642,22 +645,82 @@ class TestSpecies(utilities.CanteraTest): self.assertEqual(s.charge, 0.0) def test_index_accessor(self): - gas = ct.Solution('h2o2.xml') - - for k in range(gas.n_species): - s = gas.species(k) - self.assertEqual(s.name, gas.species_name(k)) + for k in range(self.gas.n_species): + s = self.gas.species(k) + self.assertEqual(s.name, self.gas.species_name(k)) for m,n in s.composition.items(): - self.assertEqual(n, gas.n_atoms(k,m)) + self.assertEqual(n, self.gas.n_atoms(k,m)) def test_name_accessor(self): - gas = ct.Solution('h2o2.xml') - - for name in gas.species_names: - s = gas.species(name) + for name in self.gas.species_names: + s = self.gas.species(name) self.assertEqual(s.name, name) + def test_fromCti(self): + h2_cti = """ + species( + name="H2", + atoms="H:2", + thermo=( + NASA([200.00, 1000.00], + [2.344331120E+00, 7.980520750E-03, -1.947815100E-05, + 2.015720940E-08, -7.376117610E-12, -9.179351730E+02, + 6.830102380E-01]), + NASA([1000.00, 3500.00], + [3.337279200E+00, -4.940247310E-05, 4.994567780E-07, + -1.795663940E-10, 2.002553760E-14, -9.501589220E+02, + -3.205023310E+00]) + ), + transport=gas_transport(geom="linear", + diam=2.92, + well_depth=38.00, + polar=0.79, + rot_relax=280.00), + note = "TPIS78" + )""" + s1 = self.gas.species('H2') + s2 = ct.Species.fromCti(h2_cti) + self.assertEqual(s2.name, 'H2') + self.assertEqual(s1.composition, s2.composition) + self.assertEqual(s1.thermo.cp(350), s2.thermo.cp(350)) + + def test_fromXml(self): + import xml.etree.ElementTree as ET + root = ET.parse('../../build/data/h2o2.xml').getroot() + h2_node = root.find('.//species[@name="H2"]') + h2_string = ET.tostring(h2_node) + + s1 = self.gas.species('H2') + s2 = ct.Species.fromXml(h2_string) + + self.assertEqual(s2.name, 'H2') + self.assertEqual(s1.composition, s2.composition) + self.assertEqual(s1.thermo.cp(350), s2.thermo.cp(350)) + + def test_listFromFile_cti(self): + S = ct.Species.listFromFile('h2o2.cti') + self.assertEqual({sp.name for sp in S}, + set(self.gas.species_names)) + + def test_listFromFile_xml(self): + S = ct.Species.listFromFile('h2o2.xml') + self.assertEqual({sp.name for sp in S}, + set(self.gas.species_names)) + + def test_listFromCti(self): + S = ct.Species.listFromCti(open('../../build/data/h2o2.cti').read()) + + self.assertEqual({sp.name for sp in S}, + set(self.gas.species_names)) + + def test_listFromXml(self): + S = ct.Species.listFromXml(open('../../build/data/h2o2.xml').read()) + + self.assertEqual({sp.name for sp in S}, + set(self.gas.species_names)) + + class TestSpeciesThermo(utilities.CanteraTest): def setUp(self): diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 255af5f8b..61dd4490e 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -36,6 +36,80 @@ cdef class Species: self._species = other self.species = self._species.get() + @staticmethod + def fromCti(text): + """ + Create a Species object from its CTI string representation. + """ + cxx_species = CxxGetSpecies(deref(CxxGetXmlFromString(stringify(text)))) + assert cxx_species.size() == 1, cxx_species.size() + species = Species(init=False) + species._assign(cxx_species[0]) + return species + + @staticmethod + def fromXml(text): + """ + Create a Species object from its XML string representation. + """ + cxx_species = CxxNewSpecies(deref(CxxGetXmlFromString(stringify(text)))) + species = Species(init=False) + species._assign(cxx_species) + return species + + @staticmethod + def listFromFile(filename): + """ + Create a list of Species objects from all of the species 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_species = CxxGetSpecies(deref(CxxGetXmlFile(stringify(filename)))) + species = [] + for a in cxx_species: + b = Species(init=False) + b._assign(a) + species.append(b) + return species + + @staticmethod + def listFromXml(text): + """ + Create a list of Species objects from all the species 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_species = CxxGetSpecies(deref(CxxGetXmlFromString(stringify(text)))) + species = [] + for a in cxx_species: + b = Species(init=False) + b._assign(a) + species.append(b) + return species + + @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_species = CxxGetSpecies(deref(CxxGetXmlFromString(stringify(text)))) + species = [] + for a in cxx_species: + b = Species(init=False) + b._assign(a) + species.append(b) + return species + property name: def __get__(self): return pystr(self.species.name) diff --git a/src/thermo/Species.cpp b/src/thermo/Species.cpp index 99717ce98..49b9008d0 100644 --- a/src/thermo/Species.cpp +++ b/src/thermo/Species.cpp @@ -82,4 +82,19 @@ shared_ptr newSpecies(const XML_Node& species_node) return s; } +std::vector > getSpecies(const XML_Node& node) +{ + std::vector > all_species; + std::vector species_nodes = + node.child("speciesData").getChildren("species"); + + for (std::vector::iterator iter = species_nodes.begin(); + iter != species_nodes.end(); + ++iter) + { + all_species.push_back(newSpecies(**iter)); + } + return all_species; +} + }