From 68a312dc555645d1b741ed735472ee8901950976 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Fri, 4 Jan 2013 21:37:08 +0000 Subject: [PATCH] [Cython] Added array-valued properties to FlameBase --- interfaces/cython/cantera/onedim.pyx | 108 +++++++++++++----- interfaces/cython/cantera/test/test_onedim.py | 18 +-- 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/interfaces/cython/cantera/onedim.pyx b/interfaces/cython/cantera/onedim.pyx index bfe281b92..9fbcafb78 100644 --- a/interfaces/cython/cantera/onedim.pyx +++ b/interfaces/cython/cantera/onedim.pyx @@ -822,34 +822,6 @@ class FlameBase(Sim1D): """ return self.profile(self.flame, 'V') - @property - def Y(self): - """ - 2D array containing the species mass fractions at each point. Y[k,j] - is the mass fraction of species *k* at point *j*. - """ - cdef np.ndarray[np.double_t, ndim=2] Y = \ - np.empty((self.gas.nSpecies, self.flame.nPoints)) - - for j in range(self.flame.nPoints): - self.setGasState(j) - Y[:,j] = self.gas.Y - return Y - - @property - def X(self): - """ - 2D array containing the species mole fractions at each point. X[k,j] - is the mole fraction of species *k* at point *j*. - """ - cdef np.ndarray[np.double_t, ndim=2] X = \ - np.empty((self.gas.nSpecies, self.flame.nPoints)) - - for j in range(self.flame.nPoints): - self.setGasState(j) - X[:,j] = self.gas.X - return X - def solution(self, component, point=None): if point is None: return self.profile(self.flame, component) @@ -862,6 +834,86 @@ class FlameBase(Sim1D): for k in range(k0, k0 + self.gas.nSpecies)] self.gas.TPY = self.value(self.flame, 'T', point), self.P, Y +def _trim(docstring): + """Remove block indentation from a docstring.""" + if not docstring: + return '' + lines = docstring.splitlines() + # Determine minimum indentation (first line doesn't count): + indent = 999 + for line in lines[1:]: + stripped = line.lstrip() + if stripped: + indent = min(indent, len(line) - len(stripped)) + # Remove indentation (first line is special): + trimmed = [lines[0].strip()] + if indent < 999: + for line in lines[1:]: + trimmed.append(line[indent:].rstrip()) + # Strip off trailing and leading blank lines: + trimmed = [t for t in trimmed if t] + # Return a single string: + return '\n'.join(trimmed) + +def _array_property(attr, size=None): + """ + Generate a property that retrieves values at each point in the flame. The + 'size' argument is the attribute name of the gas object used to set the + leading dimension of the resulting array. + """ + def getter(self): + if size is None: + # 1D array for scalar property + vals = np.empty(self.flame.nPoints) + else: + # 2D array + vals = np.empty((getattr(self.gas, size), self.flame.nPoints)) + + for i in range(self.flame.nPoints): + self.setGasState(i) + vals[...,i] = getattr(self.gas, attr) + + return vals + + if size is None: + extradoc = "\nReturns an array of length `nPoints`." + else: + extradoc = "\nReturns an array of size `%s` x `nPoints`." % size + + doc = _trim(getattr(Solution, attr).__doc__) + extradoc + return property(getter, doc=doc) + +# Add scalar properties to FlameBase +for attr in ['density', 'density_mass', 'density_mole', 'volume_mass', + 'volume_mole', 'intEnergy_mole', 'intEnergy_mass', 'h', + 'enthalpy_mole', 'enthalpy_mass', 's', 'entropy_mole', + 'entropy_mass', 'g', 'gibbs_mole', 'gibbs_mass', 'cv', + 'cv_mole', 'cv_mass', 'cp', 'cp_mole', 'cp_mass', + 'isothermalCompressibility', 'thermalExpansionCoeff', + 'viscosity', 'thermalConductivity']: + setattr(FlameBase, attr, _array_property(attr)) +FlameBase.volume = _array_property('v') # avoid confusion with velocity gradient 'V' +FlameBase.intEnergy = _array_property('u') # avoid collision with velocity 'u' + +# Add properties with values for each species +for attr in ['X', 'Y', 'concentrations', 'partial_molar_enthalpies', + 'partial_molar_entropies', 'partial_molar_int_energies', + 'chem_potentials', 'electrochem_potentials', 'partial_molar_cp', + 'partial_molar_volumes', 'standard_enthalpies_RT', + 'standard_entropies_R', 'standard_intEnergies_RT', + 'standard_gibbs_RT', 'standard_cp_R', 'creationRates', + 'destructionRates', 'netProductionRates', 'mixDiffCoeffs', + 'mixDiffCoeffsMass', 'mixDiffCoeffsMole', 'thermalDiffCoeffs']: + setattr(FlameBase, attr, _array_property(attr, 'nSpecies')) + +# Add properties with values for each reaction +for attr in ['fwdRatesOfProgress', 'revRatesOfProgress', 'netRatesOfProgress', + 'equilibriumConstants', 'fwdRateConstants', 'revRateConstants', + 'deltaEnthalpy', 'deltaGibbs', 'deltaEntropy', + 'deltaStandardEnthalpy', 'deltaStandardGibbs', + 'deltaStandardEntropy']: + setattr(FlameBase, attr, _array_property(attr, 'nReactions')) + class FreeFlame(FlameBase): """A freely-propagating flat flame.""" diff --git a/interfaces/cython/cantera/test/test_onedim.py b/interfaces/cython/cantera/test/test_onedim.py index 06aa6f122..79918e11f 100644 --- a/interfaces/cython/cantera/test/test_onedim.py +++ b/interfaces/cython/cantera/test/test_onedim.py @@ -115,14 +115,11 @@ class TestFreeFlame(utilities.CanteraTest): self.gas.TPX = Tin, p, reactants self.gas.equilibrate('HP') - self.sim.setGasState(0) - rhou = self.gas.density * self.sim.u[0] + rhou = self.sim.density[0] * self.sim.u[0] - for j in range(self.sim.flame.nPoints): - self.sim.setGasState(j) - - # Check continuity - self.assertNear(self.gas.density * self.sim.u[j], rhou, 1e-4) + # Check continuity + for rhou_j in self.sim.density * self.sim.u: + self.assertNear(rhou_j, rhou, 1e-4) def test_multicomponent(self): reactants= 'H2:1.1, O2:1, AR:5.3' @@ -214,3 +211,10 @@ class TestFreeFlame(utilities.CanteraTest): self.assertArrayNear(Y1, Y3, 1e-3) self.assertArrayNear(u1, u3, 1e-3) self.assertArrayNear(V1, V3, 1e-3) + + def test_array_properties(self): + self.create_sim(ct.OneAtm, 300, 'H2:1.1, O2:1, AR:5') + + for attr in ct.FlameBase.__dict__: + if isinstance(ct.FlameBase.__dict__[attr], property): + getattr(self.sim, attr)