cantera/interfaces/cython/cantera/base.pyx
Ray Speth a1e4735743 [Python] Fix premature deallocation when slicing Solution objects
Testing the value of self.parent in _SolutionBase.__dealloc__ was ineffective
because this attribute can already be cleared when __dealloc__ is called,
causing the borrowed C++ objects to be deleted prematurely. A C data member
needs to be used to determine whether this object is responsible for deleting
the underlying C++ objects.
2015-04-29 14:38:45 -04:00

137 lines
4.7 KiB
Cython

cdef class _SolutionBase:
def __cinit__(self, infile='', phaseid='', phases=(), origin=None,
source=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:
other = <_SolutionBase?>origin
# keep a reference to the parent to prevent the underlying
# C++ objects from being deleted
self.parent = origin
self.is_slice = True
self.thermo = other.thermo
self.kinetics = other.kinetics
self.transport = other.transport
self.thermo_basis = other.thermo_basis
self._selected_species = other._selected_species.copy()
return
self.is_slice = False
if infile or source:
self._init_cti_xml(infile, phaseid, phases, source)
elif thermo and species:
self._init_parts(thermo, species, kinetics, phases, reactions)
else:
raise ValueError("Arguments are insufficient to define a phase")
# Initialization of transport is deferred to Transport.__init__
self.transport = NULL
self._selected_species = np.ndarray(0, dtype=np.integer)
def __init__(self, *args, **kwargs):
if isinstance(self, Transport):
assert self.transport is not NULL
def _init_cti_xml(self, infile, phaseid, phases, source):
"""
Instantiate a set of new Cantera C++ objects from a CTI or XML
phase definition
"""
if infile:
rootNode = CxxGetXmlFile(stringify(infile))
elif source:
rootNode = CxxGetXmlFromString(stringify(source))
# Get XML data
cdef XML_Node* phaseNode
if phaseid:
phaseNode = rootNode.findID(stringify(phaseid))
else:
phaseNode = rootNode.findByName(stringify('phase'))
if phaseNode is NULL:
raise ValueError("Couldn't read phase node from XML file")
# Thermo
if isinstance(self, ThermoPhase):
self.thermo = newPhase(deref(phaseNode))
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 = newKineticsMgr(deref(phaseNode), v)
else:
self.kinetics = NULL
def _init_parts(self, thermo, species, kinetics, phases, reactions):
"""
Instantiate a set of new Cantera C++ objects based on a string defining
the model type and a list of Species objects.
"""
self.thermo = newThermoPhase(stringify(thermo))
self.thermo.addUndefinedElements()
cdef Species S
for S in species:
self.thermo.addSpecies(S._species)
self.thermo.initThermo()
if not kinetics:
kinetics = "none"
cdef ThermoPhase phase
cdef Reaction reaction
if isinstance(self, Kinetics):
self.kinetics = CxxNewKinetics(stringify(kinetics))
self.kinetics.addPhase(deref(self.thermo))
for phase in phases:
self.kinetics.addPhase(deref(phase.thermo))
self.kinetics.init()
for reaction in reactions:
self.kinetics.addReaction(reaction._reaction)
self.kinetics.finalize()
def __getitem__(self, selection):
copy = self.__class__(origin=self)
if isinstance(selection, slice):
selection = range(selection.start or 0,
selection.stop or self.n_species,
selection.step or 1)
copy.selected_species = selection
return copy
property selected_species:
def __get__(self):
return list(self._selected_species)
def __set__(self, species):
if isinstance(species, (str, unicode, int)):
species = (species,)
self._selected_species.resize(len(species))
for i,spec in enumerate(species):
self._selected_species[i] = self.species_index(spec)
def __reduce__(self):
raise NotImplementedError('Solution object is not picklable')
def __copy__(self):
raise NotImplementedError('Solution object is not copyable')
def __dealloc__(self):
# only delete the C++ objects if this is the parent object
if not self.is_slice:
del self.thermo
del self.kinetics
del self.transport