diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index c318ec906..73a5f585a 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -38,6 +38,21 @@ cdef extern from "cantera/cython/funcWrapper.h": CxxFunc1(callback_wrapper, void*) double eval(double) except +translate_exception +cdef extern from "cantera/base/smart_ptr.h": + cppclass shared_ptr "Cantera::shared_ptr" [T]: + T* get() + void reset(T*) + +cdef extern from "cantera/thermo/Species.h": + cdef cppclass CxxSpecies "Cantera::Species": + CxxSpecies() + CxxSpecies(string, stdmap[string,double]) + + string name + stdmap[string,double] composition + double charge + double size + cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": cdef cppclass CxxThermoPhase "Cantera::ThermoPhase": @@ -75,6 +90,8 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": # species properties size_t nSpecies() + shared_ptr[CxxSpecies] species(string) except + + shared_ptr[CxxSpecies] species(size_t) except + size_t speciesIndex(string) except + string speciesName(size_t) except + double nAtoms(size_t, size_t) except + @@ -588,6 +605,12 @@ ctypedef void (*transportMethod2d)(CxxTransport*, size_t, double*) except + ctypedef void (*kineticsMethod1d)(CxxKinetics*, double*) except + # classes +cdef class Species: + cdef shared_ptr[CxxSpecies] _species + cdef CxxSpecies* species + + cdef _assign(self, shared_ptr[CxxSpecies] other) + cdef class _SolutionBase: cdef CxxThermoPhase* thermo cdef CxxKinetics* kinetics diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index 578cce70c..fea93948f 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -624,3 +624,36 @@ ideal_gas(name='spam', elements='O H', def test_checkReactionBalance(self): with self.assertRaises(Exception): ct.Solution('../data/h2o2_unbalancedReaction.xml') + + +class TestSpecies(utilities.CanteraTest): + def test_standalone(self): + s = ct.Species('CH4', {'C':1, 'H':4}) + + self.assertEqual(s.name, 'CH4') + c = s.composition + self.assertEqual(len(c), 2) + self.assertEqual(c['C'], 1) + self.assertEqual(c['H'], 4) + + def test_defaults(self): + s = ct.Species('H2') + self.assertEqual(s.size, 0.0) + 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 m,n in s.composition.items(): + self.assertEqual(n, 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) + self.assertEqual(s.name, name) diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index c88be3fd8..57df8bb0a 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -13,6 +13,47 @@ cdef stdmap[string,double] comp_map(dict X) except *: m[stringify(species)] = value return m +cdef comp_map_to_dict(stdmap[string,double] m): + return {pystr(species):value for species,value in m.items()} + + +cdef class Species: + def __cinit__(self, *args, init=True, **kwargs): + if init: + self._species.reset(new CxxSpecies()) + self.species = self._species.get() + + def __init__(self, name=None, composition=None, charge=None, size=None, + *args, init=True, **kwargs): + if not init: + return + + if name is not None: + self.species.name = stringify(name) + + if composition is not None: + self.species.composition = comp_map(composition) + + cdef _assign(self, shared_ptr[CxxSpecies] other): + self._species = other + self.species = self._species.get() + + property name: + def __get__(self): + return pystr(self.species.name) + + property composition: + def __get__(self): + return comp_map_to_dict(self.species.composition) + + property charge: + def __get__(self): + return self.species.charge + + property size: + def __get__(self): + return self.species.size + cdef class ThermoPhase(_SolutionBase): """ @@ -237,6 +278,16 @@ cdef class ThermoPhase(_SolutionBase): return index + def species(self, k): + s = Species(init=False) + if isinstance(k, (str, unicode)): + s._assign(self.thermo.species(stringify(k))) + elif isinstance(k, (int, float)): + s._assign(self.thermo.species(k)) + else: + raise TypeError("Argument must be a string or a number") + return s + def n_atoms(self, species, element): """ Number of atoms of element *element* in species *species*. The element diff --git a/src/thermo/Species.cpp b/src/thermo/Species.cpp index e01eb61f6..d9fe715dc 100644 --- a/src/thermo/Species.cpp +++ b/src/thermo/Species.cpp @@ -9,8 +9,8 @@ namespace Cantera { Species::Species() - : charge(std::numeric_limits::quiet_NaN()) - , size(std::numeric_limits::quiet_NaN()) + : charge(0.0) + , size(0.0) { }