cantera/interfaces/cython/cantera/kinetics.pyx
Ray Speth 7af153c75f [Cython/Kinetics] stoich_coeff methods accept species by name
reactant_stoich_coeff and product_stoich_coeff now accept either the species
name or the "kinetics species index".
2014-07-10 22:36:28 +00:00

373 lines
14 KiB
Cython

# NOTE: These cdef functions cannot be members of Kinetics because they would
# cause "layout conflicts" when creating derived classes with multiple bases,
# e.g. class Solution. [Cython 0.16]
cdef np.ndarray get_species_array(Kinetics kin, kineticsMethod1d method):
cdef np.ndarray[np.double_t, ndim=1] data = np.empty(kin.n_total_species)
method(kin.kinetics, &data[0])
# @TODO: Fix _selected_species to work with interface kinetics
if kin._selected_species.size:
return data[kin._selected_species]
else:
return data
cdef np.ndarray get_reaction_array(Kinetics kin, kineticsMethod1d method):
cdef np.ndarray[np.double_t, ndim=1] data = np.empty(kin.n_reactions)
method(kin.kinetics, &data[0])
return data
cdef class Kinetics(_SolutionBase):
"""
Instances of class `Kinetics` are responsible for evaluating reaction rates
of progress, species production rates, and other quantities pertaining to
a reaction mechanism.
"""
property n_total_species:
"""
Total number of species in all phases participating in the kinetics
mechanism.
"""
def __get__(self):
return self.kinetics.nTotalSpecies()
property n_reactions:
"""Number of reactions in the reaction mechanism."""
def __get__(self):
return self.kinetics.nReactions()
property n_phases:
"""Number of phases in the reaction mechanism."""
def __get__(self):
return self.kinetics.nPhases()
property reaction_phase_index:
"""The index of the phase where the reactions occur."""
def __get__(self):
return self.kinetics.reactionPhaseIndex()
def _check_phase_index(self, n):
if not 0 <= n < self.n_phases:
raise ValueError("Phase index ({0}) out of range".format(n))
def _check_reaction_index(self, n):
if not 0 <= n < self.n_reactions:
raise ValueError("Reaction index ({0}) out of range".format(n))
def _check_kinetics_species_index(self, n):
if not 0 <= n < self.n_total_species:
raise ValueError("Kinetics Species index ({0}) out of range".format(n))
def kinetics_species_index(self, species, int phase=0):
"""
The index of species *species* of phase *phase* within arrays returned
by methods of class `Kinetics`. If *species* is a string, the *phase*
argument is unused.
"""
cdef int k
if isinstance(species, (str, unicode)):
return self.kinetics.kineticsSpeciesIndex(stringify(species))
else:
k = species
self._check_kinetics_species_index(k)
self._check_phase_index(k)
return self.kinetics.kineticsSpeciesIndex(k, phase)
def is_reversible(self, int i_reaction):
"""True if reaction `i_reaction` is reversible."""
self._check_reaction_index(i_reaction)
return self.kinetics.isReversible(i_reaction)
def multiplier(self, int i_reaction):
"""
A scaling factor applied to the rate coefficient for reaction
*i_reaction*. Can be used to carry out sensitivity analysis or to
selectively disable a particular reaction. See `set_multiplier`.
"""
self._check_reaction_index(i_reaction)
return self.kinetics.multiplier(i_reaction)
def set_multiplier(self, double value, int i_reaction=-1):
"""
Set the multiplier for for reaction *i_reaction* to *value*.
If *i_reaction* is not specified, then the multiplier for all reactions
is set to *value*. See `multiplier`.
"""
if i_reaction == -1:
for i_reaction in range(self.n_reactions):
self.kinetics.setMultiplier(i_reaction, value)
else:
self._check_reaction_index(i_reaction)
self.kinetics.setMultiplier(i_reaction, value)
def reaction_type(self, int i_reaction):
"""Type of reaction *i_reaction*."""
self._check_reaction_index(i_reaction)
return self.kinetics.reactionType(i_reaction)
def reaction_equation(self, int i_reaction):
"""The equation for the specified reaction. See also `reaction_equations`."""
self._check_reaction_index(i_reaction)
return pystr(self.kinetics.reactionString(i_reaction))
def reactants(self, int i_reaction):
"""The reactants portion of the reaction equation"""
self._check_reaction_index(i_reaction)
return pystr(self.kinetics.reactantString(i_reaction))
def products(self, int i_reaction):
"""The products portion of the reaction equation"""
self._check_reaction_index(i_reaction)
return pystr(self.kinetics.productString(i_reaction))
def reaction_equations(self, indices=None):
"""
Returns a list containing the reaction equation for all reactions in the
mechanism (if *indices* is unspecified) or the equations for each
reaction in the sequence *indices*. For example::
>>> gas.reaction_equations()
['2 O + M <=> O2 + M', 'O + H + M <=> OH + M', 'O + H2 <=> H + OH', ...]
>>> gas.reaction_equations([2,3])
['O + H + M <=> OH + M', 'O + H2 <=> H + OH']
See also `reaction_equation`.
"""
if indices is None:
return [self.reaction_equation(i) for i in range(self.n_reactions)]
else:
return [self.reaction_equation(i) for i in indices]
def reactant_stoich_coeff(self, k_spec, int i_reaction):
"""
The stoichiometric coefficient of species *k_spec* as a reactant in
reaction *i_reaction*.
"""
cdef int k
if isinstance(k_spec, (str, unicode)):
k = self.kinetics_species_index(k_spec)
else:
k = k_spec
self._check_kinetics_species_index(k_spec)
self._check_reaction_index(i_reaction)
return self.kinetics.reactantStoichCoeff(k, i_reaction)
def product_stoich_coeff(self, k_spec, int i_reaction):
"""
The stoichiometric coefficient of species *k_spec* as a product in
reaction *i_reaction*.
"""
cdef int k
if isinstance(k_spec, (str, unicode)):
k = self.kinetics_species_index(k_spec)
else:
k = k_spec
self._check_kinetics_species_index(k_spec)
self._check_reaction_index(i_reaction)
return self.kinetics.productStoichCoeff(k, i_reaction)
def reactant_stoich_coeffs(self):
"""
The array of reactant stoichiometric coefficients. Element *[k,i]* of
this array is the reactant stoichiometric coefficient of species *k* in
reaction *i*.
"""
cdef np.ndarray[np.double_t, ndim=2] data = np.empty((self.n_total_species,
self.n_reactions))
cdef int i,k
for i in range(self.n_reactions):
for k in range(self.n_total_species):
data[k,i] = self.kinetics.reactantStoichCoeff(k,i)
return data
def product_stoich_coeffs(self):
"""
The array of product stoichiometric coefficients. Element *[k,i]* of
this array is the product stoichiometric coefficient of species *k* in
reaction *i*.
"""
cdef np.ndarray[np.double_t, ndim=2] data = np.empty((self.n_total_species,
self.n_reactions))
cdef int i,k
for i in range(self.n_reactions):
for k in range(self.n_total_species):
data[k,i] = self.kinetics.productStoichCoeff(k,i)
return data
property forward_rates_of_progress:
"""
Forward rates of progress for the reactions. [kmol/m^3/s] for bulk
phases or [kmol/m^2/s] for surface phases.
"""
def __get__(self):
return get_reaction_array(self, kin_getFwdRatesOfProgress)
property reverse_rates_of_progress:
"""
Reverse rates of progress for the reactions. [kmol/m^3/s] for bulk
phases or [kmol/m^2/s] for surface phases.
"""
def __get__(self):
return get_reaction_array(self, kin_getRevRatesOfProgress)
property net_rates_of_progress:
"""
Net rates of progress for the reactions. [kmol/m^3/s] for bulk phases
or [kmol/m^2/s] for surface phases.
"""
def __get__(self):
return get_reaction_array(self, kin_getNetRatesOfProgress)
property equilibrium_constants:
"""Equilibrium constants in concentration units for all reactions."""
def __get__(self):
return get_reaction_array(self, kin_getEquilibriumConstants)
property forward_rate_constants:
"""
Forward rate constants for all reactions. Units are a combination of
kmol, m^3 and s, that depend on the rate expression for the reaction.
"""
def __get__(self):
return get_reaction_array(self, kin_getFwdRateConstants)
property reverse_rate_constants:
"""
Reverse rate constants for all reactions. Units are a combination of
kmol, m^3 and s, that depend on the rate expression for the reaction.
"""
def __get__(self):
return get_reaction_array(self, kin_getRevRateConstants)
property creation_rates:
"""
Creation rates for each species. [kmol/m^3/s] for bulk phases or
[kmol/m^2/s] for surface phases.
"""
def __get__(self):
return get_species_array(self, kin_getCreationRates)
property destruction_rates:
"""
Destruction rates for each species. [kmol/m^3/s] for bulk phases or
[kmol/m^2/s] for surface phases.
"""
def __get__(self):
return get_species_array(self, kin_getDestructionRates)
property net_production_rates:
"""
Net production rates for each species. [kmol/m^3/s] for bulk phases or
[kmol/m^2/s] for surface phases.
"""
def __get__(self):
return get_species_array(self, kin_getNetProductionRates)
property delta_enthalpy:
"""Change in enthalpy for each reaction [J/kmol]."""
def __get__(self):
return get_reaction_array(self, kin_getDeltaEnthalpy)
property delta_gibbs:
"""Change in Gibbs free energy for each reaction [J/kmol]."""
def __get__(self):
return get_reaction_array(self, kin_getDeltaGibbs)
property delta_entropy:
"""Change in entropy for each reaction [J/kmol/K]."""
def __get__(self):
return get_reaction_array(self, kin_getDeltaEntropy)
property delta_standard_enthalpy:
"""
Change in standard-state enthalpy (independent of composition) for
each reaction [J/kmol].
"""
def __get__(self):
return get_reaction_array(self, kin_getDeltaSSEnthalpy)
property delta_standard_gibbs:
"""
Change in standard-state Gibbs free energy (independent of composition)
for each reaction [J/kmol].
"""
def __get__(self):
return get_reaction_array(self, kin_getDeltaSSGibbs)
property delta_standard_entropy:
"""
Change in standard-state entropy (independent of composition) for
each reaction [J/kmol/K].
"""
def __get__(self):
return get_reaction_array(self, kin_getDeltaSSEntropy)
cdef class InterfaceKinetics(Kinetics):
"""
A kinetics manager for heterogeneous reaction mechanisms. The
reactions are assumed to occur at an interface between bulk phases.
"""
def __init__(self, infile, phaseid='', phases=(), *args, **kwargs):
super().__init__(infile, phaseid, phases, *args, **kwargs)
if self.kinetics.type() not in (kinetics_type_interface,
kinetics_type_edge):
raise TypeError("Underlying Kinetics class is not of the correct type.")
self._phase_indices = {}
for phase in [self] + list(phases):
i = self.kinetics.phaseIndex(stringify(phase.name))
self._phase_indices[phase] = i
self._phase_indices[phase.name] = i
self._phase_indices[i] = i
def advance_coverages(self, double dt):
"""
This method carries out a time-accurate advancement of the surface
coverages for a specified amount of time.
"""
(<CxxInterfaceKinetics*>self.kinetics).advanceCoverages(dt)
def phase_index(self, phase):
"""
Get the index of the phase *phase*, where *phase* may specified using
the phase object, the name, or the index itself.
"""
return self._phase_indices[phase]
def _phase_slice(self, phase):
p = self.phase_index(phase)
k1 = self.kinetics_species_index(0, p)
if p == self.n_phases - 1:
k2 = self.n_total_species
else:
k2 = self.kinetics_species_index(0, p+1)
return slice(k1,k2)
def get_creation_rates(self, phase):
"""
Creation rates for each species in phase *phase*. Use the
`creation_rates` property to get the creation rates for species in all
phases.
"""
return self.creation_rates[self._phase_slice(phase)]
def get_destruction_rates(self, phase):
"""
Destruction rates for each species in phase *phase*. Use the
`destruction_rates` property to get the destruction rates for species
in all phases.
"""
return self.destruction_rates[self._phase_slice(phase)]
def get_net_production_rates(self, phase):
"""
Net production rates for each species in phase *phase*. Use the
`net_production_rates` property to get the net_production rates for
species in all phases.
"""
return self.net_production_rates[self._phase_slice(phase)]