[Python] Add ability to instantiate objects from YAML files

This commit is contained in:
Ray Speth 2019-01-05 23:47:05 -05:00
parent 8a0eed3be5
commit 59b0f64e26
3 changed files with 84 additions and 3 deletions

View file

@ -5,6 +5,7 @@
from libcpp.vector cimport vector
from libcpp.string cimport string
from libcpp.map cimport map as stdmap
from libcpp.unordered_map cimport unordered_map
from libcpp.pair cimport pair
from libcpp cimport bool as cbool
from cpython cimport bool as pybool
@ -43,6 +44,21 @@ cdef extern from "cantera/base/xml.h" namespace "Cantera":
XML_Node* findID(string)
int nChildren()
cdef extern from "cantera/base/AnyMap.h" namespace "Cantera":
cdef cppclass CxxAnyValue "Cantera::AnyValue"
cdef cppclass CxxAnyMap "Cantera::AnyMap":
CxxAnyMap()
CxxAnyValue& operator[](string) except +translate_exception
string keys_str()
cdef cppclass CxxAnyValue "Cantera::AnyValue":
CxxAnyValue()
unordered_map[string, CxxAnyMap*] asMap(string) except +translate_exception
CxxAnyMap AnyMapFromYamlFile "Cantera::AnyMap::fromYamlFile" (string) except +translate_exception
CxxAnyMap AnyMapFromYamlString "Cantera::AnyMap::fromYamlString" (string) except +translate_exception
cdef extern from "cantera/base/stringUtils.h" namespace "Cantera":
cdef Composition parseCompString(string) except +translate_exception
@ -610,10 +626,12 @@ cdef extern from "cantera/zerodim.h" namespace "Cantera":
cdef extern from "cantera/thermo/ThermoFactory.h" namespace "Cantera":
cdef CxxThermoPhase* newPhase(string, string) except +translate_exception
cdef CxxThermoPhase* newPhase(XML_Node&) except +translate_exception
cdef shared_ptr[CxxThermoPhase] newPhase(CxxAnyMap&, CxxAnyMap&) except +translate_exception
cdef CxxThermoPhase* newThermoPhase(string) except +translate_exception
cdef extern from "cantera/kinetics/KineticsFactory.h" namespace "Cantera":
cdef CxxKinetics* newKineticsMgr(XML_Node&, vector[CxxThermoPhase*]) except +translate_exception
cdef shared_ptr[CxxKinetics] newKinetics(vector[CxxThermoPhase*], CxxAnyMap&, CxxAnyMap&) except +translate_exception
cdef CxxKinetics* CxxNewKinetics "Cantera::newKineticsMgr" (string) except +translate_exception
cdef extern from "cantera/transport/TransportFactory.h" namespace "Cantera":

View file

@ -3,8 +3,8 @@
cdef class _SolutionBase:
def __cinit__(self, infile='', phaseid='', phases=(), origin=None,
source=None, thermo=None, species=(), kinetics=None,
reactions=(), **kwargs):
source=None, yaml=None, thermo=None, species=(),
kinetics=None, reactions=(), **kwargs):
# Shallow copy of an existing Solution (for slicing support)
cdef _SolutionBase other
if origin is not None:
@ -21,7 +21,9 @@ cdef class _SolutionBase:
self._selected_species = other._selected_species.copy()
return
if infile or source:
if infile.endswith('.yml') or infile.endswith('.yaml') or yaml:
self._init_yaml(infile, phaseid, phases, yaml)
elif infile or source:
self._init_cti_xml(infile, phaseid, phases, source)
elif thermo and species:
self._init_parts(thermo, species, kinetics, phases, reactions)
@ -37,6 +39,55 @@ cdef class _SolutionBase:
if isinstance(self, Transport):
assert self.transport is not NULL
def _init_yaml(self, infile, phaseid, phases, source):
"""
Instantiate a set of new Cantera C++ objects from a YAML
phase definition
"""
cdef CxxAnyMap root
if infile:
root = AnyMapFromYamlFile(stringify(infile))
elif source:
root = AnyMapFromYamlString(stringify(source))
phaseNodes = root[stringify("phases")].asMap(stringify("name"))
phaseNames = []
for item in phaseNodes:
phaseNames.append(pystr(item.first))
if not phaseNames:
raise ValueError("YAML document doesn't contain any phase definitions")
if phaseid:
if phaseid in phaseNames:
phaseNode = phaseNodes[stringify(phaseid)]
else:
raise ValueError("YAML document doesn't contain"
" a phase named '{}'".format(phaseid))
else:
phaseNode = phaseNodes[stringify(phaseNames[0])]
# Thermo
if isinstance(self, ThermoPhase):
self._thermo = newPhase(deref(phaseNode), root)
self.thermo = self._thermo.get()
else:
self.thermo = NULL
# Kinetics
cdef vector[CxxThermoPhase*] v
cdef _SolutionBase phase
if isinstance(self, Kinetics):
v.push_back(self.thermo)
for phase in phases:
# adjacent bulk phases for a surface phase
v.push_back(phase.thermo)
self._kinetics = newKinetics(v, deref(phaseNode), root)
self.kinetics = self._kinetics.get()
else:
self.kinetics = NULL
def _init_cti_xml(self, infile, phaseid, phases, source):
"""
Instantiate a set of new Cantera C++ objects from a CTI or XML

View file

@ -919,6 +919,18 @@ ideal_gas(name='spam', elements='O H',
with self.assertRaisesRegex(ct.CanteraError, 'reaction is unbalanced'):
ct.Solution('h2o2_unbalancedReaction.xml')
def test_yaml_ideal_gas_simple(self):
gas = ct.ThermoPhase('ideal-gas.yaml', 'simple')
self.check(gas, 'simple', 500, 10 * ct.one_atm, 3, 2)
def test_yaml_ideal_gas_remote_species(self):
gas = ct.ThermoPhase('ideal-gas.yaml', 'species-remote')
self.check(gas, 'species-remote', 300, ct.one_atm, 4, 2)
def test_yaml_duplicate(self):
with self.assertRaisesRegex(ct.CanteraError, 'duplicate'):
gas = ct.ThermoPhase('ideal-gas.yaml', 'duplicate-species')
class TestSpecies(utilities.CanteraTest):
def setUp(self):