From 5a722f9608508ddbdf3dc85d62495fc842bfc349 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Thu, 6 Sep 2012 19:58:50 +0000 Subject: [PATCH] Added docstrings to Cython ThermoPhase class --- doc/sphinx/conf.py | 2 +- doc/sphinx/cython/thermo.rst | 8 +- interfaces/cython/cantera/_cantera.pyx | 2 + interfaces/cython/cantera/thermo.pyx | 258 ++++++++++++++++++++++++- 4 files changed, 259 insertions(+), 11 deletions(-) diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py index 670b35ada..6584cce91 100644 --- a/doc/sphinx/conf.py +++ b/doc/sphinx/conf.py @@ -95,7 +95,7 @@ if sys.version_info.major == 3: # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +default_role = 'py:obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True diff --git a/doc/sphinx/cython/thermo.rst b/doc/sphinx/cython/thermo.rst index 77062e23a..d2d6f7030 100644 --- a/doc/sphinx/cython/thermo.rst +++ b/doc/sphinx/cython/thermo.rst @@ -1,8 +1,10 @@ +.. py:currentmodule:: cantera + Thermodynamic Properties ======================== These classes are used to describe the thermodynamic state of a system. -.. autoclass:: cantera.ThermoPhase -.. autoclass:: cantera.InterfacePhase -.. autoclass:: cantera.PureFluid +.. autoclass:: ThermoPhase +.. autoclass:: InterfacePhase +.. autoclass:: PureFluid diff --git a/interfaces/cython/cantera/_cantera.pyx b/interfaces/cython/cantera/_cantera.pyx index 4aad30501..5cce1a393 100644 --- a/interfaces/cython/cantera/_cantera.pyx +++ b/interfaces/cython/cantera/_cantera.pyx @@ -1,3 +1,5 @@ +#cython: embedsignature=True + import numpy as np cimport numpy as np diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 786b2ac56..f19f22ed6 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -5,24 +5,52 @@ cdef enum ThermoBasis: ctypedef void (*thermoMethod1d)(CxxThermoPhase*, double*) except + cdef class ThermoPhase(_SolutionBase): + """ + A phase with an equation of state. + + Class `ThermoPhase` may be used to represent the intensive thermodynamic + state of a phase of matter, which might be a gas, liquid, or solid. + + Class `ThermoPhase` is not usually instantiated directly. It is used + as a base class for classes `Solution` and `Interface`. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if 'source' not in kwargs: self.thermoBasis = massBasis def report(self, show_thermo=True): + """ + Generate a report describing the thermodynamic state of this phase. To + print the report to the terminal, simply call the phase object. The + following two statements are equivalent:: + + >>> phase() + >>> print(phase.report()) + """ return pystr(self.thermo.report(bool(show_thermo))) def __call__(self): print(self.report()) property name: + """ + The name assigned to this phase. The default is taken from the CTI/XML + input file. + """ def __get__(self): return pystr(self.thermo.name()) def __set__(self, name): self.thermo.setName(stringify(name)) property basis: + """ + Determines whether intensive thermodynamic properties are treated on a + `mass` (per kg) or `molar` (per kmol) basis. This affects the values + returned by the properties `h`, `u`, `s`, `g`, `v`, `density`, `cv`, + and `cp`, as well as the values used with the state-setting properties + such as `HPX` and `UV`. + """ def __get__(self): if self.thermoBasis == massBasis: return 'mass' @@ -51,19 +79,71 @@ cdef class ThermoPhase(_SolutionBase): else: return 1.0 - def equilibrate(self, XY, int solver=-1, double rtol=1e-9, + def equilibrate(self, XY, solver='auto', double rtol=1e-9, int maxsteps=1000, int maxiter=100, int loglevel=0): + """ + Set to a state of chemical equilibrium holding property pair + *XY* constant. + + :param XY: + A two-letter string, which must be one of the set:: + + ['TP','TV','HP','SP','SV','UV'] + + :param solver: + Specifies the equilibrium solver to use. May be one of the following: + + * ''element_potential'' - a fast solver using the element potential + method will be used. + * 'gibbs' - a slower but more robust Gibbs minimization solver will + be used. + * "auto" - The element potential solver will be tried first, then + if it fails the gibbs solver will be tried. + :param rtol: + the relative error tolerance. + :param maxsteps: + maximum number of steps in composition to take to find a converged + solution. + :param maxiter: + For the Gibbs minimization solver, this specifies the number of + 'outer' iterations on T or P when some property pair other + than TP is specified. + :param loglevel: + Set to a value > 0 to write diagnostic output to a file in HTML + format. Larger values generate more detailed information. The file + will be named ``equilibrate_log.html.`` Subsequent files will be + named ``equilibrate_log1.html``, etc., so that log files are + not overwritten. + """ + cdef int iSolver + if isinstance(solver, int): + iSolver = solver + elif solver == 'auto': + iSolver = -1 + elif solver == 'element_potential': + iSolver = 1 + elif solver == 'gibbs': + iSolver = 2 + else: + raise ValueError('Invalid equilibrium solver specified') + XY = XY.upper() equilibrate(deref(self.thermo), stringify(XY).c_str(), - solver, rtol, maxsteps, maxiter, loglevel) + iSolver, rtol, maxsteps, maxiter, loglevel) ####### Composition, species, and elements ######## property nElements: + """Number of elements.""" def __get__(self): return self.thermo.nElements() cpdef int elementIndex(self, element) except *: + """ + The index of element *element*, which may be specified as a string or + an integer. In the latter case, the index is checked for validity and + returned. If no such element is present, an exception is thrown. + """ if isinstance(element, str): index = self.thermo.elementIndex(stringify(element)) elif isinstance(element, (int, float)): @@ -77,20 +157,25 @@ cdef class ThermoPhase(_SolutionBase): return index def elementName(self, m): + """Name of the element with index *m*.""" return pystr(self.thermo.elementName(m)) property elementNames: + """A list of all the element names.""" def __get__(self): return [self.elementName(m) for m in range(self.nElements)] property nSpecies: + """Number of species.""" def __get__(self): return self.thermo.nSpecies() def speciesName(self, k): + """Name of the species with index *k*.""" return pystr(self.thermo.speciesName(k)) property speciesNames: + """A list of all the species names.""" def __get__(self): if self._selectedSpecies.size: indices = self._selectedSpecies @@ -99,6 +184,11 @@ cdef class ThermoPhase(_SolutionBase): return [self.speciesName(k) for k in indices] cpdef int speciesIndex(self, species) except *: + """ + The index of species *species*, which may be specified as a string or + an integer. In the latter case, the index is checked for validity and + returned. If no such species is present, an exception is thrown. + """ if isinstance(species, str): index = self.thermo.speciesIndex(stringify(species)) elif isinstance(species, (int, float)): @@ -112,6 +202,13 @@ cdef class ThermoPhase(_SolutionBase): return index def nAtoms(self, species, element): + """ + Number of atoms of element *element* in species *species*. The element + and species may be specified by name or by index. + + >>> phase.nAtoms('CH4','H') + 4 + """ return self.thermo.nAtoms(self.speciesIndex(species), self.elementIndex(element)) @@ -132,14 +229,25 @@ cdef class ThermoPhase(_SolutionBase): method(self.thermo, &data[0]) property molecularWeights: + """Array of species molecular weights (molar masses) [kg/kmol]""" def __get__(self): return self._getArray1(thermo_getMolecularWeights) property meanMolecularWeight: + """The mean molecular weight (molar mass) [kg/kmol]""" def __get__(self): return self.thermo.meanMolecularWeight() property Y: + """ + Get/Set the species mass fractions. Can be set as either an array or + as a string. Always returns an array:: + + >>> phase.Y = [0.1, 0, 0, 0.4, 0, 0, 0, 0, 0.5] + >>> phase.Y = 'H2:0.1, O2:0.4, AR:0.5' + >>> phase.Y + array([0.1, 0, 0, 0.4, 0, 0, 0, 0, 0.5]) + """ def __get__(self): return self._getArray1(thermo_getMassFractions) def __set__(self, Y): @@ -149,6 +257,16 @@ cdef class ThermoPhase(_SolutionBase): self._setArray1(thermo_setMassFractions, Y) property X: + """ + Get/Set the species mole fractions. Can be set as either an array or + as a string. Always returns an array:: + + >>> phase.Y = [0.1, 0, 0, 0.4, 0, 0, 0, 0, 0.5] + >>> phase.Y = 'H2:0.1, O2:0.4, AR:0.5' + >>> phase.Y + array([0.1, 0, 0, 0.4, 0, 0, 0, 0, 0.5]) + + """ def __get__(self): return self._getArray1(thermo_getMoleFractions) def __set__(self, X): @@ -158,6 +276,7 @@ cdef class ThermoPhase(_SolutionBase): self._setArray1(thermo_setMoleFractions, X) property concentrations: + """Get/Set the species concentrations [kmol/m^3].""" def __get__(self): return self._getArray1(thermo_getConcentrations) def __set__(self, C): @@ -166,103 +285,155 @@ cdef class ThermoPhase(_SolutionBase): ######## Read-only thermodynamic properties ######## property P: + """Pressure [Pa]""" def __get__(self): return self.thermo.pressure() property T: + """Temperature [K]""" def __get__(self): return self.thermo.temperature() property density: + """Density [kg/m^3 or kmol/m^3] depending on `basis`""" def __get__(self): return self.thermo.density() / self._massFactor() + property density_mass: + """(Mass) density [kg/m^3]""" def __get__(self): return self.thermo.density() + property density_mole: + """Molar density [kmol/m^3]""" def __get__(self): return self.thermo.molarDensity() property v: + """Specific volume [m^3/kg or m^3/kmol] depending on `basis`""" def __get__(self): return self._massFactor() / self.thermo.density() + property volume_mass: + """Specific volume [m^3/kg]""" def __get__(self): return 1.0 / self.thermo.density() + property volume_mole: + """Molar volume [m^3/kmol]""" def __get__(self): return self.thermo.molarVolume() property u: + """Internal energy in [J/kg or J/kmol]""" def __get__(self): return self.thermo.intEnergy_mole() * self._moleFactor() + property intEnergy_mole: + """Molar internal energy [J/kmol]""" def __get__(self): return self.thermo.intEnergy_mole() + property intEnergy_mass: + """Specific internal energy [J/kg]""" def __get__(self): return self.thermo.intEnergy_mass() property h: + """Enthalpy [J/kg or J/kmol] depending on `basis`""" def __get__(self): return self.thermo.enthalpy_mole() * self._moleFactor() + property enthalpy_mole: + """Molar enthalpy [J/kmol]""" def __get__(self): return self.thermo.enthalpy_mole() + property enthalpy_mass: + """Specific enthalpy [J/kg]""" def __get__(self): return self.thermo.enthalpy_mass() property s: + """Entropy [J/kg/K or J/kmol/K] depending on `basis`""" def __get__(self): return self.thermo.entropy_mole() * self._moleFactor() + property entropy_mole: + """Molar entropy [J/kmol/K]""" def __get__(self): return self.thermo.entropy_mole() + property entropy_mass: + """Specific entropy [J/kg]""" def __get__(self): return self.thermo.entropy_mass() property g: + """Gibbs free energy [J/kg or J/kmol] depending on `basis`""" def __get__(self): return self.thermo.gibbs_mole() * self._moleFactor() + property gibbs_mole: + """Molar Gibbs free energy [J/kmol]""" def __get__(self): return self.thermo.gibbs_mole() property gibbs_mass: + """Specific Gibbs free energy [J/kg]""" def __get__(self): return self.thermo.gibbs_mass() property cv: + """ + Heat capacity at constant volume [J/kg/K or J/kmol/K] depending on + `basis` + """ def __get__(self): return self.thermo.cv_mole() * self._moleFactor() + property cv_mole: + """Molar heat capacity at constant volume [J/kmol/K]""" def __get__(self): return self.thermo.cv_mole() + property cv_mass: + """Specific heat capacity at constant volume [J/kg/K]""" def __get__(self): return self.thermo.cv_mass() property cp: + """ + Heat capacity at constant pressure [J/kg/K or J/kmol/K] depending + on `basis` + """ def __get__(self): return self.thermo.cp_mole() * self._moleFactor() + property cp_mole: + """Molar heat capacity at constant pressure [J/kmol/K]""" def __get__(self): return self.thermo.cp_mole() + property cp_mass: + """Specific heat capacity at constant pressure [J/kg/K]""" def __get__(self): return self.thermo.cp_mass() ######## Methods to get/set the complete thermodynamic state ######## property TD: + """Get/Set temperature [K] and density [kg/m^3 or kmol/m^3].""" def __get__(self): return self.T, self.density def __set__(self, values): self.thermo.setState_TR(values[0], values[1] * self._massFactor()) property TDX: + """ + Get/Set temperature [K], density [kg/m^3 or kmol/m^3], and mole + fractions. + """ def __get__(self): return self.T, self.density, self.X def __set__(self, values): @@ -270,6 +441,10 @@ cdef class ThermoPhase(_SolutionBase): self.TD = values[:2] property TDY: + """ + Get/Set temperature [K] and density [kg/m^3 or kmol/m^3], and mass + fractions. + """ def __get__(self): return self.T, self.density, self.Y def __set__(self, values): @@ -277,12 +452,14 @@ cdef class ThermoPhase(_SolutionBase): self.TD = values[:2] property TP: + """Get/Set temperature [K] and pressure [Pa].""" def __get__(self): return self.T, self.P def __set__(self, values): self.thermo.setState_TP(values[0], values[1]) property TPX: + """Get/Set temperature [K], pressure [Pa], and mole fractions.""" def __get__(self): return self.T, self.P, self.X def __set__(self, values): @@ -290,6 +467,7 @@ cdef class ThermoPhase(_SolutionBase): self.TP = values[:2] property TPY: + """Get/Set temperature [K], pressure [Pa], and mass fractions.""" def __get__(self): return self.T, self.P, self.Y def __set__(self, values): @@ -297,6 +475,10 @@ cdef class ThermoPhase(_SolutionBase): self.TP = values[:2] property UV: + """ + Get/Set internal energy [J/kg or J/kmol] and specific volume + [m^3/kg or m^3/kmol]. + """ def __get__(self): return self.u, self.v def __set__(self, values): @@ -304,6 +486,10 @@ cdef class ThermoPhase(_SolutionBase): values[1] / self._massFactor()) property UVX: + """ + Get/Set internal energy [J/kg or J/kmol], specific volume + [m^3/kg or m^3/kmol], and mole fractions. + """ def __get__(self): return self.u, self.v, self.X def __set__(self, values): @@ -311,6 +497,10 @@ cdef class ThermoPhase(_SolutionBase): self.UV = values[:2] property UVY: + """ + Get/Set internal energy [J/kg or J/kmol], specific volume + [m^3/kg or m^3/kmol], and mass fractions. + """ def __get__(self): return self.u, self.v, self.Y def __set__(self, values): @@ -318,12 +508,14 @@ cdef class ThermoPhase(_SolutionBase): self.UV = values[:2] property HP: + """Get/Set enthalpy [J/kg or J/kmol] and pressure [Pa].""" def __get__(self): return self.h, self.P def __set__(self, values): self.thermo.setState_HP(values[0] / self._massFactor(), values[1]) property HPX: + """Get/Set enthalpy [J/kg or J/kmol], pressure [Pa] and mole fractions.""" def __get__(self): return self.h, self.P, self.X def __set__(self, values): @@ -331,6 +523,7 @@ cdef class ThermoPhase(_SolutionBase): self.HP = values[:2] property HPY: + """Get/Set enthalpy [J/kg or J/kmol], pressure [Pa] and mass fractions""" def __get__(self): return self.h, self.P, self.Y def __set__(self, values): @@ -338,12 +531,14 @@ cdef class ThermoPhase(_SolutionBase): self.HP = values[:2] property SP: + """Get/Set entropy [J/kg/K or J/kmol/K] and pressure [Pa]""" def __get__(self): return self.s, self.P def __set__(self, values): self.thermo.setState_SP(values[0] / self._massFactor(), values[1]) property SPX: + """Get/Set entropy [J/kg/K or J/kmol/K], pressure [Pa], and mole fractions""" def __get__(self): return self.s, self.P, self.X def __set__(self, values): @@ -351,6 +546,7 @@ cdef class ThermoPhase(_SolutionBase): self.SP = values[:2] property SPY: + """Get/Set entropy [J/kg/K or J/kmol/K], pressure [Pa], and mass fractions""" def __get__(self): return self.s, self.P, self.Y def __set__(self, values): @@ -359,85 +555,133 @@ cdef class ThermoPhase(_SolutionBase): # partial molar / non-dimensional properties property partial_molar_enthalpies: + """Array of species partial molar enthalpies [J/kmol]""" def __get__(self): return self._getArray1(thermo_getPartialMolarEnthalpies) property partial_molar_entropies: + """Array of species partial molar entropies [J/kmol/K]""" def __get__(self): return self._getArray1(thermo_getPartialMolarEntropies) property partial_molar_int_energies: + """Array of species partial molar internal energies [J/kmol]""" def __get__(self): return self._getArray1(thermo_getPartialMolarIntEnergies) property chem_potentials: + """Array of species chemical potentials [J/kmol]""" def __get__(self): return self._getArray1(thermo_getChemPotentials) property electrochem_potentials: + """Array of species electrochemical potentials [J/kmol]""" def __get__(self): return self._getArray1(thermo_getElectrochemPotentials) property partial_molar_cp: + """ + Array of species partial molar specific heat capacities at constant + pressure [J/kmol/K] + """ def __get__(self): return self._getArray1(thermo_getPartialMolarCp) property partial_molar_volumes: + """Array of species partial molar volumes [m^3/kmol]""" def __get__(self): return self._getArray1(thermo_getPartialMolarVolumes) property standard_enthalpies_RT: + """ + Array of nondimensional species standard-state enthalpies at the + current temperature and pressure. + """ def __get__(self): return self._getArray1(thermo_getEnthalpy_RT) property standard_entropies_R: + """ + Array of nondimensional species standard-state entropies at the + current temperature and pressure. + """ def __get__(self): return self._getArray1(thermo_getEntropy_R) property standard_intEnergies_RT: + """ + Array of nondimensional species standard-state internal energies at the + current temperature and pressure. + """ def __get__(self): return self._getArray1(thermo_getIntEnergy_RT) property standard_gibbs_RT: + """ + Array of nondimensional species standard-state Gibbs free energies at + the current temperature and pressure. + """ def __get__(self): return self._getArray1(thermo_getGibbs_RT) property standard_cp_R: + """ + Array of nondimensional species standard-state specific heat capacities + at constant pressure at the current temperature and pressure. + """ def __get__(self): return self._getArray1(thermo_getCp_R) ######## Miscellaneous properties ######## property isothermalCompressibility: + """Isothermal compressibility [1/Pa]""" def __get__(self): return self.thermo.isothermalCompressibility() property thermalExpansionCoeff: + """Thermal expansion coefficient [1/K]""" def __get__(self): return self.thermo.thermalExpansionCoeff() property minTemp: + """ + Minimum temperature for which the thermodynamic data for the phase are + valid. + """ def __get__(self): return self.thermo.minTemp() property maxTemp: + """ + Maximum temperature for which the thermodynamic data for the phase are + valid. + """ def __get__(self): return self.thermo.maxTemp() property refPressure: + """Reference state pressure [Pa]""" def __get__(self): return self.thermo.refPressure() property electricPotential: + """Get/Set the electric potential [V] for this phase""" def __get__(self): return self.thermo.electricPotential() def __set__(self, double value): self.thermo.setElectricPotential(value) - property elementPotentials: - def __get__(self): - cdef np.ndarray[np.double_t, ndim=1] data = np.zeros(self.nElements) - self.thermo.getElementPotentials(&data[0]) - return data + def elementPotentials(self): + """ + Get the array of element potentials. The element potentials are only + defined for equilibrium states. This method first sets the composition + to a state of equilibrium at constant T and P, then computes the + element potentials for this equilibrium state. + """ + self.equilibrate('TP') + cdef np.ndarray[np.double_t, ndim=1] data = np.zeros(self.nElements) + self.thermo.getElementPotentials(&data[0]) + return data cdef class InterfacePhase(ThermoPhase):