From bcbdd251010559944498c23d5be0d0433f414c41 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Thu, 6 Sep 2012 19:57:53 +0000 Subject: [PATCH] [Python] Added "slicing" support to Solution Slicing a Solution object (with the [] operator) creates a view where all species-specific properties are returned for the specified subset of the species. --- interfaces/cython/cantera/_cantera.pxd | 6 ++++ interfaces/cython/cantera/base.pyx | 49 +++++++++++++++++++++++--- interfaces/cython/cantera/thermo.pyx | 26 ++++++-------- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 72cb0f8de..e669bbea5 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -1,6 +1,10 @@ from libcpp.vector cimport vector from libcpp.string cimport string from libcpp cimport bool as cbool +from cpython cimport bool as pybool + +import numpy as np +cimport numpy as np cdef extern from "cantera/base/xml.h" namespace "Cantera": cdef cppclass XML_Node: @@ -271,6 +275,8 @@ cdef class _SolutionBase: cdef CxxKinetics* kinetics cdef CxxTransport* transport cdef int thermoBasis + cdef np.ndarray _selectedSpecies + cdef object parent cdef class Mixture: cdef CxxMultiPhase* mix diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index 019d90c05..dfc14c85e 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -1,5 +1,23 @@ cdef class _SolutionBase: - def __cinit__(self, infile, phaseid='', phases=()): + def __cinit__(self, infile='', phaseid='', phases=(), source=None): + # Shallow copy of an existing Solution (for slicing support) + cdef _SolutionBase other + if source is not None: + other = <_SolutionBase?>source + + # keep a reference to the parent to prevent the underlying + # C++ objects from being deleted + self.parent = other + + self.thermo = other.thermo + self.kinetics = other.kinetics + self.transport = other.transport + + self.thermoBasis = other.thermoBasis + self._selectedSpecies = other._selectedSpecies.copy() + return + + # Instantiate a set of new Cantera C++ objects rootNode = getCtmlTree(stringify(infile)) # Get XML data @@ -33,11 +51,34 @@ cdef class _SolutionBase: # Initialization of transport is deferred to Transport.__init__ self.transport = NULL + self._selectedSpecies = np.ndarray(0, dtype=np.integer) + def __init__(self, *args, **kwargs): if isinstance(self, Transport): assert self.transport is not NULL + def __getitem__(self, selection): + copy = self.__class__(source=self) + if isinstance(selection, slice): + selection = range(selection.start or 0, + selection.stop or self.nSpecies, + selection.step or 1) + copy.selectedSpecies = selection + return copy + + property selectedSpecies: + def __get__(self): + return list(self._selectedSpecies) + def __set__(self, species): + if isinstance(species, (str, int)): + species = (species,) + self._selectedSpecies.resize(len(species)) + for i,spec in enumerate(species): + self._selectedSpecies[i] = self.speciesIndex(spec) + def __dealloc__(self): - del self.thermo - del self.kinetics - del self.transport + # only delete the C++ objects if this is the parent object + if self.parent is None: + del self.thermo + del self.kinetics + del self.transport diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 8af6ff99b..2f62a1839 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -7,7 +7,8 @@ ctypedef void (*thermoMethod1d)(CxxThermoPhase*, double*) except + cdef class ThermoPhase(_SolutionBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.thermoBasis = massBasis + if 'source' not in kwargs: + self.thermoBasis = massBasis def report(self, show_thermo=True): return pystr(self.thermo.report(bool(show_thermo))) @@ -85,7 +86,11 @@ cdef class ThermoPhase(_SolutionBase): property speciesNames: def __get__(self): - return [self.speciesName(k) for k in range(self.nSpecies)] + if self._selectedSpecies.size: + indices = self._selectedSpecies + else: + indices = range(self.nSpecies) + return [self.speciesName(k) for k in indices] cpdef int speciesIndex(self, species) except *: if isinstance(species, str): @@ -107,7 +112,10 @@ cdef class ThermoPhase(_SolutionBase): cdef np.ndarray _getArray1(self, thermoMethod1d method): cdef np.ndarray[np.double_t, ndim=1] data = np.empty(self.nSpecies) method(self.thermo, &data[0]) - return data + if self._selectedSpecies.size: + return data[self._selectedSpecies] + else: + return data cdef void _setArray1(self, thermoMethod1d method, values) except *: if len(values) != self.nSpecies: @@ -121,9 +129,6 @@ cdef class ThermoPhase(_SolutionBase): def __get__(self): return self._getArray1(thermo_getMolecularWeights) - def molecularWeight(self, species): - return self.thermo.molecularWeight(self.speciesIndex(species)) - property meanMolecularWeight: def __get__(self): return self.thermo.meanMolecularWeight() @@ -137,9 +142,6 @@ cdef class ThermoPhase(_SolutionBase): else: self._setArray1(thermo_setMassFractions, Y) - def massFraction(self, species): - return self.thermo.massFraction(self.speciesIndex(species)) - property X: def __get__(self): return self._getArray1(thermo_getMoleFractions) @@ -149,18 +151,12 @@ cdef class ThermoPhase(_SolutionBase): else: self._setArray1(thermo_setMoleFractions, X) - def moleFraction(self, species): - return self.thermo.moleFraction(self.speciesIndex(species)) - property concentrations: def __get__(self): return self._getArray1(thermo_getConcentrations) def __set__(self, C): self._setArray1(thermo_setConcentrations, C) - def concentration(self, species): - return self.thermo.concentration(self.speciesIndex(species)) - ######## Read-only thermodynamic properties ######## property P: