From 21a1be8e0a08d02de6fea1426051e6b830b9228c Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Wed, 30 Mar 2016 13:02:02 -0400 Subject: [PATCH] [Python] Add Solution.state to allow faster iteration over states --- interfaces/cython/cantera/_cantera.pxd | 2 ++ interfaces/cython/cantera/composite.py | 50 +++++++++++--------------- interfaces/cython/cantera/thermo.pyx | 16 +++++++++ 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index a6748e6d6e..f90607152f 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -109,6 +109,8 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": double refPressure() except + cbool getElementPotentials(double*) except + void equilibrate(string, string, double, int, int, int, int) except + + void saveState(size_t, double*) + void restoreState(size_t, double*) # initialization void addUndefinedElements() except + diff --git a/interfaces/cython/cantera/composite.py b/interfaces/cython/cantera/composite.py index a6cc203eef..fc3eb93183 100644 --- a/interfaces/cython/cantera/composite.py +++ b/interfaces/cython/cantera/composite.py @@ -188,7 +188,7 @@ def setter(self, value): return property(getter, setter, doc=getattr(Solution, attr).__doc__) for _attr in dir(Solution): - if _attr.startswith('_') or _attr in Quantity.__dict__: + if _attr.startswith('_') or _attr in Quantity.__dict__ or _attr == 'state': continue else: setattr(Quantity, _attr, _prop(_attr)) @@ -205,27 +205,11 @@ def __init__(self, phase, shape, states=None): else: self._shape = shape S = np.empty(shape + (2+self._phase.n_species,)) - S[...,0], S[...,1], S[...,2:] = self._phase.TDY + S[:] = self._phase.state self._states = S self._indices = list(np.ndindex(self._shape)) - def __iter__(self): - """ - Iterate over states, with the phase object set to the corresponding - state. - """ - for index in self._indices: - state = self._states[index] - self._phase.TDY = state[0], state[1], state[2:] - yield index - - def items(self): - for index in self._indices: - state = self._states[index] - self._phase.TDY = state[0], state[1], state[2:] - yield index, state - def __getitem__(self, index): states = self._states[index] shape = states.shape[:-1] @@ -233,9 +217,10 @@ def __getitem__(self, index): def equilibrate(self, *args, **kwargs): """ See `ThermoPhase.equilibrate` """ - for index, state in self.items(): + for index in self._indices: + self._phase.state = self._states[index] self._phase.equilibrate(*args, **kwargs) - state[0], state[1], state[2:] = self._phase.TDY + self._states[index][:] = self._phase.state def _make_functions(): @@ -270,16 +255,18 @@ def state2_prop(name): def getter(self): a = np.empty(self._shape) b = np.empty(self._shape) - for index in self: + for index in self._indices: + self._phase.state = self._states[index] a[index], b[index] = getattr(self._phase, name) return a, b def setter(self, AB): assert len(AB) == 2, "Expected 2 elements, got {}".format(len(AB)) A, B, _ = np.broadcast_arrays(AB[0], AB[1], self._states[...,0]) - for index, state in self.items(): + for index in self._indices: + self._phase.state = self._states[index] setattr(self._phase, name, (A[index], B[index])) - state[0], state[1], state[2:] = self._phase.TDY + self._states[index][:] = self._phase.state return property(getter, setter, doc=getattr(Solution, name).__doc__) @@ -293,7 +280,8 @@ def getter(self): a = np.empty(self._shape) b = np.empty(self._shape) c = np.empty(self._shape + (self._phase.n_species,)) - for index in self: + for index in self._indices: + self._phase.state = self._states[index] a[index], b[index], c[index] = getattr(self._phase, name) return a, b, c @@ -301,9 +289,10 @@ def setter(self, ABC): assert len(ABC) == 3, "Expected 3 elements, got {}".format(len(ABC)) A, B, C, _ = np.broadcast_arrays(ABC[0], ABC[1], ABC[2], self._states[...,0]) - for index, state in self.items(): + for index in self._indices: + self._phase.state = self._states[index] setattr(self._phase, name, (A[index], B[index], C[index])) - state[0], state[1], state[2:] = self._phase.TDY + self._states[index][:] = self._phase.state return property(getter, setter, doc=getattr(Solution, name).__doc__) @@ -313,7 +302,8 @@ def setter(self, ABC): def scalar_prop(name): def getter(self): v = np.empty(self._shape) - for index in self: + for index in self._indices: + self._phase.state = self._states[index] v[index] = getattr(self._phase, name) return v return property(getter, doc=getattr(Solution, name).__doc__) @@ -324,7 +314,8 @@ def getter(self): def species_prop(name): def getter(self): v = np.empty(self._shape + (self._phase.n_species,)) - for index in self: + for index in self._indices: + self._phase.state = self._states[index] v[index] = getattr(self._phase, name) return v return property(getter, doc=getattr(Solution, name).__doc__) @@ -336,7 +327,8 @@ def getter(self): def caller(name): def wrapper(self, *args, **kwargs): v = np.empty(self._shape) - for index in self: + for index in self._indices: + self._phase.state = self._states[index] v[index] = getattr(self._phase, name)(*args, **kwargs) return v return wrapper diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 291e68b4f6..019ad984c2 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -831,6 +831,22 @@ cdef class ThermoPhase(_SolutionBase): ######## Methods to get/set the complete thermodynamic state ######## + property state: + """ + Get/Set the full thermodynamic state as a single array, arranged as + [temperature, density, mass fractions] for most phases. Useful mainly + in cases where it is desired to store many states in a multidimensional + array. + """ + def __get__(self): + cdef np.ndarray[np.double_t, ndim=1] state = np.empty(self.n_species + 2) + self.thermo.saveState(len(state), &state[0]) + return state + + def __set__(self, state): + cdef np.ndarray[np.double_t, ndim=1] cstate = np.asarray(state) + self.thermo.restoreState(len(state), &cstate[0]) + property TD: """Get/Set temperature [K] and density [kg/m^3 or kmol/m^3].""" def __get__(self):