diff --git a/include/cantera/thermo/Species.h b/include/cantera/thermo/Species.h index 8c5eb8507..9a7e18b55 100644 --- a/include/cantera/thermo/Species.h +++ b/include/cantera/thermo/Species.h @@ -79,6 +79,9 @@ unique_ptr newSpecies(const AnyMap& node); //! respectively, where the string or file is formatted as either CTI or XML. std::vector > getSpecies(const XML_Node& node); +//! Generate Species objects for each item (an AnyMap) in `items`. +std::vector> getSpecies(const AnyValue& items); + } #endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index e31d19b09..8672eefc9 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -116,6 +116,8 @@ cdef extern from "cantera/thermo/Species.h" namespace "Cantera": cdef shared_ptr[CxxSpecies] CxxNewSpecies "newSpecies" (XML_Node&) cdef vector[shared_ptr[CxxSpecies]] CxxGetSpecies "getSpecies" (XML_Node&) + cdef shared_ptr[CxxSpecies] CxxNewSpecies "newSpecies" (CxxAnyMap&) except +translate_exception + cdef vector[shared_ptr[CxxSpecies]] CxxGetSpecies "getSpecies" (CxxAnyValue&) except +translate_exception cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": cdef cppclass CxxThermoPhase "Cantera::ThermoPhase": diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index a44ad7a4b..ca7ee68f4 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -1022,6 +1022,10 @@ class TestSpecies(utilities.CanteraTest): self.assertEqual({sp.name for sp in S}, set(self.gas.species_names)) + def test_listfromFile_yaml(self): + S = ct.Species.listFromFile('ideal-gas.yaml') + self.assertEqual(S[0].name, 'O2') + def test_listFromCti(self): p = os.path.dirname(__file__) with open(pjoin(p, '..', 'data', 'h2o2.cti')) as f: @@ -1030,6 +1034,20 @@ class TestSpecies(utilities.CanteraTest): self.assertEqual({sp.name for sp in S}, set(self.gas.species_names)) + def test_listFomYaml(self): + yaml = ''' + - name: H2O + composition: {H: 2, O: 1} + thermo: {model: constant-cp, h0: 100} + - name: HO2 + composition: {H: 1, O: 2} + thermo: {model: constant-cp, h0: 200} + ''' + species = ct.Species.listFromYaml(yaml) + self.assertEqual(species[0].name, 'H2O') + self.assertEqual(species[1].composition, {'H': 1, 'O': 2}) + self.assertNear(species[0].thermo.h(300), 100) + def test_listFromXml(self): p = os.path.dirname(__file__) with open(pjoin(p, '..', 'data', 'h2o2.xml')) as f: diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index e8a413f9f..cbbb96d8c 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -37,15 +37,14 @@ cdef class Species: ch4.transport = tran gas = ct.Solution(thermo='IdealGas', species=[ch4]) - The static methods `fromCti`, `fromXml`, `listFromFile`, `listFromCti`, and - `listFromXml` can be used to create `Species` objects from existing - definitions in the CTI or XML formats. All of the following will produce a - list of 53 `Species` objects containing the species defined in the GRI 3.0 - mechanism:: + The static methods `fromYaml`, `fromCti`, `fromXml`, `listFromFile`, + `listFromYaml`, `listFromCti`, and `listFromXml` can be used to create + `Species` objects from existing definitions in the CTI or XML formats. + Either of the following will produce a list of 53 `Species` objects + containing the species defined in the GRI 3.0 mechanism:: - S = ct.Species.listFromFile('gri30.cti') - S = ct.Species.listFromCti(open('path/to/gri30.cti').read()) - S = ct.Species.listFromXml(open('path/to/gri30.xml').read()) + S = ct.Species.listFromFile('gri30.yaml') + S = ct.Species.listFromYaml(open('path/to/gri30.yaml').read()) """ def __cinit__(self, *args, init=True, **kwargs): @@ -96,10 +95,21 @@ cdef class Species: return species @staticmethod - def listFromFile(filename): + def fromYaml(text): + """ + Create a Species object from its YAML string representation. + """ + cxx_species = CxxNewSpecies(AnyMapFromYamlString(stringify(text))) + species = Species(init=False) + species._assign(cxx_species) + return species + + @staticmethod + def listFromFile(filename, section='species'): """ Create a list of Species objects from all of the species defined in a - CTI or XML file. + YAML, CTI or XML file. For YAML files, return species from the section + *section*. Directories on Cantera's input file path will be searched for the specified file. @@ -108,7 +118,12 @@ cdef class Species: 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)))) + if filename.lower().split('.')[-1] in ('yml', 'yaml'): + root = AnyMapFromYamlFile(stringify(filename)) + cxx_species = CxxGetSpecies(root[stringify(section)]) + else: + cxx_species = CxxGetSpecies(deref(CxxGetXmlFile(stringify(filename)))) + species = [] for a in cxx_species: b = Species(init=False) @@ -148,6 +163,21 @@ cdef class Species: species.append(b) return species + @staticmethod + def listFromYaml(text): + """ + Create a list of Species objects from all the species defined in a YAML + string. + """ + root = AnyMapFromYamlString(stringify(text)) + cxx_species = CxxGetSpecies(root[stringify("items")]) + species = [] + for a in cxx_species: + b = Species(init=False) + b._assign(a) + species.append(b) + return species + property name: """ The name of the species. """ def __get__(self): diff --git a/src/base/AnyMap.cpp b/src/base/AnyMap.cpp index a19424136..a391656be 100644 --- a/src/base/AnyMap.cpp +++ b/src/base/AnyMap.cpp @@ -149,7 +149,11 @@ struct convert { } static bool decode(const Node& node, Cantera::AnyMap& target) { target.setLoc(node.Mark().line, node.Mark().column); - if (!node.IsMap()) { + if (node.IsSequence()) { + // Convert a top-level list to a map with the key "items" + target["items"] = node.as(); + return true; + } else if (!node.IsMap()) { std::string text = YAML::Dump(node); if (text.size() > 300) { text.resize(300); diff --git a/src/thermo/Species.cpp b/src/thermo/Species.cpp index adcb7166f..ff45868b7 100644 --- a/src/thermo/Species.cpp +++ b/src/thermo/Species.cpp @@ -127,4 +127,13 @@ std::vector > getSpecies(const XML_Node& node) return all_species; } +std::vector> getSpecies(const AnyValue& items) +{ + std::vector > all_species; + for (const auto& node : items.asVector()) { + all_species.emplace_back(newSpecies(node)); + } + return all_species; +} + }