From 83b08d7a2623eb98d388719069598f023c99c542 Mon Sep 17 00:00:00 2001 From: Dave Goodwin Date: Fri, 10 Dec 2004 05:07:14 +0000 Subject: [PATCH] *** empty log message *** --- Cantera/clib/src/ctmultiphase.cpp | 26 +++ Cantera/clib/src/ctmultiphase.h | 4 + Cantera/cxx/include/equilibrium.h | 2 +- Cantera/cxx/include/transport.h | 2 + Cantera/python/Cantera/Mixture.py | 193 ++++++++++++++++++-- Cantera/python/Cantera/ThermoPhase.py | 3 + Cantera/python/Cantera/__init__.py | 2 + Cantera/python/src/ctmultiphase_methods.cpp | 65 ++++++- Cantera/python/src/methods.h | 4 + 9 files changed, 280 insertions(+), 21 deletions(-) diff --git a/Cantera/clib/src/ctmultiphase.cpp b/Cantera/clib/src/ctmultiphase.cpp index 327d8a847..b52c9db9f 100644 --- a/Cantera/clib/src/ctmultiphase.cpp +++ b/Cantera/clib/src/ctmultiphase.cpp @@ -96,10 +96,18 @@ extern "C" { return _mix(i)->nElements(); } + int DLL_EXPORT mix_elementIndex(int i, char* name) { + return _mix(i)->elementIndex(string(name)); + } + int DLL_EXPORT mix_nSpecies(int i) { return _mix(i)->nSpecies(); } + int DLL_EXPORT mix_speciesIndex(int i, int k, int p) { + return _mix(i)->speciesIndex(k, p); + } + doublereal DLL_EXPORT mix_nAtoms(int i, int k, int m) { bool ok = (checkSpecies(i,k) && checkElement(i,m)); if (ok) @@ -120,6 +128,24 @@ extern "C" { return 0; } + int DLL_EXPORT mix_setMoles(int i, int nlen, double* n) { + try { + if (nlen < _mix(i)->nSpecies()) + throw CanteraError("setMoles","array size too small."); + _mix(i)->setMoles(n); + return 0; + } + catch (CanteraError) { + return ERR; + } + } + + + int DLL_EXPORT mix_setMolesByName(int i, char* n) { + _mix(i)->setMolesByName(string(n)); + return 0; + } + int DLL_EXPORT mix_setTemperature(int i, double t) { if (t < 0.0) return -1; _mix(i)->setTemperature(t); diff --git a/Cantera/clib/src/ctmultiphase.h b/Cantera/clib/src/ctmultiphase.h index dce11e320..12ceafd5b 100644 --- a/Cantera/clib/src/ctmultiphase.h +++ b/Cantera/clib/src/ctmultiphase.h @@ -11,6 +11,8 @@ extern "C" { int DLL_IMPORT mix_assign(int i, int j); int DLL_IMPORT mix_addPhase(int i, int j, double moles); int DLL_IMPORT mix_nElements(int i); + int DLL_IMPORT mix_elementIndex(int i, char* name); + int DLL_IMPORT mix_speciesIndex(int i, int k, int p); int DLL_IMPORT mix_nSpecies(int i); int DLL_IMPORT mix_setTemperature(int i, double t); double DLL_IMPORT mix_temperature(int i); @@ -19,6 +21,8 @@ extern "C" { double DLL_IMPORT mix_nAtoms(int i, int k, int m); double DLL_IMPORT mix_phaseMoles(int i, int n); int DLL_IMPORT mix_setPhaseMoles(int i, int n, double v); + int DLL_IMPORT mix_setMoles(int i, int nlen, double* n); + int DLL_IMPORT mix_setMolesByName(int i, char* n); double DLL_IMPORT mix_speciesMoles(int i, int k); double DLL_IMPORT mix_elementMoles(int i, int m); double DLL_IMPORT mix_equilibrate(int i, char* XY, diff --git a/Cantera/cxx/include/equilibrium.h b/Cantera/cxx/include/equilibrium.h index cb77bda99..504b6f8e2 100755 --- a/Cantera/cxx/include/equilibrium.h +++ b/Cantera/cxx/include/equilibrium.h @@ -5,7 +5,7 @@ #define CT_EQUIL_INCL #include "kernel/ChemEquil.h" //#ifdef DEV_EQUIL -//#include "kernel/MultiPhaseEquil.h" +#include "kernel/MultiPhaseEquil.h" //#endif #endif diff --git a/Cantera/cxx/include/transport.h b/Cantera/cxx/include/transport.h index 793634846..246c8178b 100755 --- a/Cantera/cxx/include/transport.h +++ b/Cantera/cxx/include/transport.h @@ -3,4 +3,6 @@ #include "kernel/transport/TransportFactory.h" #include "kernel/transport/DustyGasTransport.h" +#include "kernel/transport/MultiTransport.h" +#include "kernel/transport/MixTransport.h" #endif diff --git a/Cantera/python/Cantera/Mixture.py b/Cantera/python/Cantera/Mixture.py index b441c85bd..ebf4e2043 100644 --- a/Cantera/python/Cantera/Mixture.py +++ b/Cantera/python/Cantera/Mixture.py @@ -1,9 +1,39 @@ import _cantera import types -from Numeric import zeros +from Numeric import zeros, array, asarray +from exceptions import CanteraError class Mixture: - """Class Mixture represents mixtures of one or more phases of matter.""" + """ + Multiphase mixtures. Class Mixture represents + mixtures of one or more phases of matter. To construct a mixture, + supply a list of phases to the constructor, each paired with the + number of moles for that phase: + + >>> gas = importPhase('gas.cti') + >>> gas.speciesNames() + ['H2', 'H', 'O2', 'O', 'OH'] + >>> graphite = importPhase('graphite.cti') + >>> graphite.speciesNames() + ['C(g)'] + >>> mix = Mixture([(gas, 1.0), (graphite, 0.1)]) + >>> mix.speciesNames() + ['H2', 'H', 'O2', 'O', 'OH', 'C(g)'] + + Note that the objects representing each phase compute only the + intensive state of the phase -- they do not store any information + on the amount of this phase. Mixture objects, on the other hand, represent + the full extensive state. + + Mixture objects are 'lightweight' in the sense that they do not + store parameters needed to compute thermodynamic or kinetic + properties of the phases. These are contained in the + ('heavyweight') phase objects. Multiple mixture objects may be + constructed using the same set of phase objects. Each one stores + its own state information locally, and synchronizes the phases + objects whenever it requires phase properties. + + """ def __init__(self, phases=[]): self.__mixid = _cantera.mix_new() @@ -17,67 +47,194 @@ class Mixture: except: ph = p moles = 0 - self.addPhase(ph, moles) + self._addPhase(ph, moles) self._phases.append(ph) - + self.setTemperature(self._phases[0].temperature()) + self.setPressure(self._phases[0].pressure()) def __del__(self): + """Delete the Mixture instance. The phase objects are not deleted.""" _cantera.mix_del(self.__mixid) def __repr__(self): s = '' for p in range(len(self._phases)): - s += '\n******************* Phase '+`p`+' ******************************\n' + s += '\n******************* Phase '+self._phases[p].name()+' ******************************\n' s += '\n Moles: '+`self.phaseMoles(p)`+'\n' s += self._phases[p].__repr__()+'\n\n' return s - - def addPhase(self, phase = None, moles = 0.0): + + def _addPhase(self, phase = None, moles = 0.0): + """Add a phase to the mixture.""" for k in range(phase.nSpecies()): - self._spnames.append(phase.speciesName(k)) + self._spnames.append(phase.speciesName(k)) _cantera.mix_addPhase(self.__mixid, phase.thermo_hndl(), moles) def nElements(self): """Total number of elements present in the mixture.""" return _cantera.mix_nElements(self.__mixid) + + def elementIndex(self, element): + """Index of element with name 'element'. + >>> mix.elementIndex('H') + 2 + >>> + """ + if type(element) == types.StringType: + return _cantera.mix_elementIndex(self.__mixid, element) + else: + return element + def nSpecies(self): """Total number of species present in the mixture. This is the sum of the numbers of species in each phase.""" return _cantera.mix_nSpecies(self.__mixid) + def speciesName(self, k): + """Name of the species with index k. Note that index numbers + are assigned in order as phases are added.""" return self._spnames[k] + + def speciesNames(self): + n = self.nSpecies() + s = [] + for k in range(n): + s.append(self.speciesName(k)) + return s + def speciesIndex(self, species): + """Index of species with name 'species'. If 'species' is not a string, + then it is simply returned.""" if type(species) == types.StringType: return self._spnames.index(species) else: return species + def nAtoms(self, k, m): - """Number of atoms of element m in species k.""" - return _cantera.mix_nAtoms(self.__mixid, k, m) + """Number of atoms of element m in species k. Both the species and + the element may be referenced either by name or by index number. + + >>> n = mix.nAtoms('CH4','H') + 4.0 + + """ + kk = self.speciesIndex(k) + mm = self.elementIndex(m) + return _cantera.mix_nAtoms(self.__mixid, kk, mm) + def setTemperature(self, t): + """Set the temperature [K]. The temperatures of all phases are + set to this value, holding the pressure fixed.""" return _cantera.mix_setTemperature(self.__mixid, t) def temperature(self): + """The temperature [K].""" return _cantera.mix_temperature(self.__mixid) def setPressure(self, p): + """Set the pressure [Pa]. The pressures of all phases are set + to the specified value, holding the temperature fixed.""" return _cantera.mix_setPressure(self.__mixid, p) def pressure(self): + """The pressure [Pa].""" return _cantera.mix_pressure(self.__mixid) def phaseMoles(self, n): """Moles of phase n.""" return _cantera.mix_phaseMoles(self.__mixid, n) def setPhaseMoles(self, n, moles): - """Set the moles of phase n.""" - return _cantera.mix_setPhaseMoles(self.__mixid, n, moles) - def speciesMoles(self, species): + """Set the number of moles of phase n.""" + _cantera.mix_setPhaseMoles(self.__mixid, n, moles) + def setMoles(self, moles): + """Set the moles of the species [kmol]. The moles may be + specified either as a string, or as an array. If an array is + used, it must be dimensioned at least as large as the total + number of species in the mixture. Note that the species may + belong to any phase, and unspecified species are set to zero. + + >>> mix.setMoles('C(s):1.0, CH4:2.0, O2:0.2') + + """ + if type(moles) == types.StringType: + _cantera.mix_setMolesByName(self.__mixid, moles) + else: + _cantera.mix_setMoles(self.__mixid, asarray(moles)) + + def speciesMoles(self, species = ""): """Moles of species k.""" - k = self.speciesIndex(species) - return _cantera.mix_speciesMoles(self.__mixid, k) + moles = array(self.nSpecies(),'d') + for k in range(self.nSpecies()): + moles[k] = _cantera.mix_speciesMoles(self.__mixid, k) + return self.selectSpecies(moles, species) + def elementMoles(self, m): - return _cantera.mix_elementMoles(self.__mixid, m) - def chemPotentials(self): + """Total number of moles of element m, summed over all species. + The element may be referenced either by index number or by name. + """ + mm = self.elementIndex(m) + return _cantera.mix_elementMoles(self.__mixid, mm) + + def chemPotentials(self, species=[]): + """The chemical potentials of all species [J/kmol].""" mu = zeros(self.nSpecies(),'d') _cantera.mix_getChemPotentials(self.__mixid, mu) - return mu + return self.selectSpecies(mu, species) + + def set(self, **p): + for o in p.keys(): + v = p[o] + if o == 'T' or o == 'Temperature': + self.setTemperature(v) + elif o == 'P' or o == 'Pressure': + self.setPressure(v) + elif o == 'Moles' or o == 'N': + self.setSpeciesMoles(v) + else: + raise CanteraError("unknown property: "+o) + def equilibrate(self, XY = "TP", err = 1.0e-9, maxiter = 1000): + """Set the mixture to a state of chemical equilibrium. + + This method uses the VCS algorithm to find the composition + that minimizes the total Gibbs free energy of the mixture, + subject to element conservation constraints. For a description + of the theory, see Smith and Missen, "Chemical Reaction + Equilibrium." The VCS algorithm is implemented in Cantera + kernel class MultiPhaseEquil. + + XY - Two-letter string specifying the two properties to hold fixed. + Currently, only TP (constant T and P) is implemented. Default: "TP". + + err - Error tolerance. Iteration will continue until (Delta + mu)/RT is less than this value for each reaction. Default: + 1.0e-9. + + maxiter - Maximum number of iterations to attempt. Default: 1000. + + >>> mix.equilibrate('TP') + >>> mix.equilibrate('TP', err = 1.0e-6, maxiter = 500) + + """ return _cantera.mix_equilibrate(self.__mixid, XY, err, maxiter) + def selectSpecies(self, f, species): + """Given an array 'f' of floating-point species properties, + return a Numeric array of those values corresponding to species + listed in 'species'. This method is used internally to implement + species selection in methods like moleFractions, massFractions, etc. + >>> f = mix.chemPotentials() + >>> muo2, muh2 = mix.selectSpecies(f, ['O2', 'H2']) + """ + sp = [] + if species: + if type(species) == types.StringType: + sp = [sp] + else: + sp = species + fs = [] + k = 0 + for s in s: + k = self.speciesIndex(s) + fs.append(f[k]) + return Numeric.asarray(fs) + else: + return f + + diff --git a/Cantera/python/Cantera/ThermoPhase.py b/Cantera/python/Cantera/ThermoPhase.py index b4e37df4e..8a9f8fe56 100644 --- a/Cantera/python/Cantera/ThermoPhase.py +++ b/Cantera/python/Cantera/ThermoPhase.py @@ -64,6 +64,9 @@ class ThermoPhase(Phase): if self._owner: _cantera.thermo_delete(self._phase_id) + def name(self): + return self.idtag + def refPressure(self): """Reference pressure [Pa]. All standard-state thermodynamic properties are for this pressure. diff --git a/Cantera/python/Cantera/__init__.py b/Cantera/python/Cantera/__init__.py index cce0f845a..c62fca32b 100755 --- a/Cantera/python/Cantera/__init__.py +++ b/Cantera/python/Cantera/__init__.py @@ -37,6 +37,8 @@ try: except: pass +from Mixture import Mixture + def writeCSV(f, list): """Write list items to file 'f' in comma-separated-value format.""" for item in list: diff --git a/Cantera/python/src/ctmultiphase_methods.cpp b/Cantera/python/src/ctmultiphase_methods.cpp index 9a42d7d3e..48507985a 100644 --- a/Cantera/python/src/ctmultiphase_methods.cpp +++ b/Cantera/python/src/ctmultiphase_methods.cpp @@ -51,6 +51,20 @@ py_mix_nElements(PyObject *self, PyObject *args) return Py_BuildValue("i",_val); } +static PyObject * +py_mix_elementIndex(PyObject *self, PyObject *args) +{ + int _val; + int i; + char* name; + if (!PyArg_ParseTuple(args, "is:mix_elementIndex", &i, &name)) + return NULL; + + _val = mix_elementIndex(i,name); + if (int(_val) < -900) return reportCanteraError(); + return Py_BuildValue("i",_val); +} + static PyObject * py_mix_nSpecies(PyObject *self, PyObject *args) { @@ -64,6 +78,18 @@ py_mix_nSpecies(PyObject *self, PyObject *args) return Py_BuildValue("i",_val); } +static PyObject * +py_mix_speciesIndex(PyObject *self, PyObject *args) +{ + int _val; + int i, k, p; + if (!PyArg_ParseTuple(args, "iii:mix_speciesIndex", &i, &k, &p)) + return NULL; + + _val = mix_speciesIndex(i,k,p); + if (int(_val) < -900) return reportCanteraError(); + return Py_BuildValue("i",_val); +} static PyObject * py_mix_nAtoms(PyObject *self, PyObject *args) @@ -192,11 +218,46 @@ py_mix_elementMoles(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "ii:mix_elementMoles", &i, &m)) return NULL; - _val = mix_elementMoles(i,m); - //if (int(_val) < -900) return reportCanteraError(); + _val = mix_elementMoles(i,m); + if (int(_val) < -900) return reportCanteraError(); return Py_BuildValue("d",_val); } + +static PyObject * +py_mix_setMoles(PyObject *self, PyObject *args) +{ + int _val; + int i; + PyObject* n; + if (!PyArg_ParseTuple(args, "iO:mix_setMoles", &i, &n)) + return NULL; + + + PyArrayObject* n_array = (PyArrayObject*)n; + double* n_data = (double*)n_array->data; + int n_len = n_array->dimensions[0]; + + _val = mix_setMoles(i,n_len,n_data); + if (int(_val) < -900) return reportCanteraError(); + return Py_BuildValue("i",_val); +} + + +static PyObject * +py_mix_setMolesByName(PyObject *self, PyObject *args) +{ + int _val; + int i; + char* n; + if (!PyArg_ParseTuple(args, "is:mix_setMolesByName", &i, &n)) + return NULL; + + _val = mix_setMolesByName(i,n); + if (int(_val) < -900) return reportCanteraError(); + return Py_BuildValue("i",_val); +} + static PyObject * py_mix_equilibrate(PyObject *self, PyObject *args) { diff --git a/Cantera/python/src/methods.h b/Cantera/python/src/methods.h index b0fc10da5..fb4635fc2 100644 --- a/Cantera/python/src/methods.h +++ b/Cantera/python/src/methods.h @@ -255,6 +255,8 @@ static PyMethodDef ct_methods[] = { {"mix_del", py_mix_del, METH_VARARGS}, {"mix_addPhase", py_mix_addPhase, METH_VARARGS}, {"mix_nElements", py_mix_nElements, METH_VARARGS}, + {"mix_elementIndex", py_mix_elementIndex, METH_VARARGS}, + {"mix_speciesIndex", py_mix_speciesIndex, METH_VARARGS}, {"mix_nSpecies", py_mix_nSpecies, METH_VARARGS}, {"mix_nAtoms", py_mix_nAtoms, METH_VARARGS}, {"mix_setTemperature", py_mix_setTemperature, METH_VARARGS}, @@ -263,6 +265,8 @@ static PyMethodDef ct_methods[] = { {"mix_pressure", py_mix_pressure, METH_VARARGS}, {"mix_phaseMoles", py_mix_phaseMoles, METH_VARARGS}, {"mix_setPhaseMoles", py_mix_setPhaseMoles, METH_VARARGS}, + {"mix_setMoles", py_mix_setMoles, METH_VARARGS}, + {"mix_setMolesByName", py_mix_setMolesByName, METH_VARARGS}, {"mix_speciesMoles", py_mix_speciesMoles, METH_VARARGS}, {"mix_elementMoles", py_mix_elementMoles, METH_VARARGS}, {"mix_equilibrate", py_mix_equilibrate, METH_VARARGS},