diff --git a/Cantera/src/kinetics/EdgeKinetics.cpp b/Cantera/src/kinetics/EdgeKinetics.cpp new file mode 100644 index 000000000..c0726b972 --- /dev/null +++ b/Cantera/src/kinetics/EdgeKinetics.cpp @@ -0,0 +1,532 @@ +/** + * @file EdgeKinetics.cpp + * + */ + +// Copyright 2002 California Institute of Technology + + +// turn off warnings under Windows +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "EdgeKinetics.h" +#include "SurfPhase.h" + +#include "ReactionData.h" +//#include "StoichManager.h" +#include "RateCoeffMgr.h" + +#include +using namespace std; + + +namespace Cantera { + + ////////////////////////////////////////////////////////////////// + + /** + * Construct an empty EdgeKinetics reaction mechanism. + */ + EdgeKinetics:: + EdgeKinetics() : + Kinetics(), + m_kk(0), + m_redo_rates(false), + m_nirrev(0), + m_nrev(0), + m_finalized(false), + m_has_electrochem_rxns(false) + { + m_kdata = new EdgeKineticsData; + m_kdata->m_temp = 0.0; + } + + /** + * Destructor + */ + EdgeKinetics:: + ~EdgeKinetics(){ + delete m_kdata; + } + + + /** + * Update properties that depend on temperature + * + */ + void EdgeKinetics:: + _update_rates_T() { + _update_rates_phi(); + doublereal T = thermo(surfacePhaseIndex()).temperature(); + if (T != m_kdata->m_temp || m_redo_rates) { + m_kdata->m_logtemp = log(T); + m_rates.update(T, m_kdata->m_logtemp, DATA_PTR(m_kdata->m_rfn)); + if (m_has_electrochem_rxns) + applyButlerVolmerCorrection(DATA_PTR(m_kdata->m_rfn)); + m_kdata->m_temp = T; + updateKc(); + m_kdata->m_ROP_ok = false; + m_redo_rates = false; + } + } + + void EdgeKinetics:: + _update_rates_phi() { + int np = nPhases(); + for (int n = 0; n < np; n++) { + if (thermo(n).electricPotential() != m_phi[n]) { + m_phi[n] = thermo(n).electricPotential(); + m_redo_rates = true; + } + } + } + + + /** + * Update properties that depend on concentrations. This method + * fills out the array of generalized concentrations by calling + * method getActivityConcentrations for each phase, which classes + * representing phases should overload to return the appropriate + * quantities. + */ + void EdgeKinetics:: + _update_rates_C() { + int n; + + //m_rates.update(m_kdata->m_temp, + // m_kdata->m_logtemp, m_kdata->m_rfn.begin()); + + int np = nPhases(); + for (n = 0; n < np; n++) { + thermo(n).getActivityConcentrations(DATA_PTR(m_conc) + m_start[n]); + } + m_kdata->m_ROP_ok = false; + } + + + /** + * Update the equilibrium constants in molar units for all + * reversible reactions. Irreversible reactions have their + * equilibrium constant set to zero. + */ + void EdgeKinetics::updateKc() { + int i, irxn; + vector_fp& m_rkc = m_kdata->m_rkcn; + fill(m_rkc.begin(), m_rkc.end(), 0.0); + + if (m_nrev > 0) { + + int n, nsp, k, ik=0; + doublereal rt = GasConstant*thermo(0).temperature(); + doublereal rrt = 1.0/rt; + int np = nPhases(); + for (n = 0; n < np; n++) { + thermo(n).getStandardChemPotentials(DATA_PTR(m_mu0) + m_start[n]); + nsp = thermo(n).nSpecies(); + for (k = 0; k < nsp; k++) { + m_mu0[ik] -= rt*thermo(n).logStandardConc(k); + m_mu0[ik] += Faraday * m_phi[n] * thermo(n).charge(k); + ik++; + } + } + + // compute Delta mu^0 for all reversible reactions + m_reactantStoich.decrementReactions(DATA_PTR(m_mu0), + DATA_PTR(m_rkc)); + m_revProductStoich.incrementReactions(DATA_PTR(m_mu0), + DATA_PTR(m_rkc)); + + for (i = 0; i < m_nrev; i++) { + irxn = m_revindex[i]; + m_rkc[irxn] = exp(m_rkc[irxn]*rrt); + } + for (i = 0; i != m_nirrev; ++i) { + m_rkc[ m_irrev[i] ] = 0.0; + } + } + } + + + + void EdgeKinetics::checkPartialEquil() { + int i, irxn; + vector_fp dmu(nTotalSpecies(), 0.0); + vector_fp rmu(nReactions(), 0.0); + if (m_nrev > 0) { + + int n, nsp, k, ik=0; + doublereal rt = GasConstant*thermo(0).temperature(); + doublereal rrt = 1.0/rt; + int np = nPhases(); + for (n = 0; n < np; n++) { + thermo(n).getChemPotentials(DATA_PTR(dmu) + m_start[n]); + nsp = thermo(n).nSpecies(); + for (k = 0; k < nsp; k++) { + dmu[ik] += Faraday * m_phi[n] * thermo(n).charge(k); + cout << thermo(n).speciesName(k) << " " << dmu[ik] << endl; + ik++; + } + } + + // compute Delta mu^ for all reversible reactions + m_reactantStoich.decrementReactions(DATA_PTR(dmu), DATA_PTR(rmu)); + m_revProductStoich.incrementReactions(DATA_PTR(dmu), DATA_PTR(rmu)); + + for (i = 0; i < m_nrev; i++) { + irxn = m_revindex[i]; + cout << "Reaction " << irxn << " " << exp(rmu[irxn]*rrt) << endl; + } + } + } + + + /** + * Get the equilibrium constants of all reactions, whether + * reversible or not. + */ + void EdgeKinetics::getEquilibriumConstants(doublereal* kc) { + int i; + + int n, nsp, k, ik=0; + doublereal rt = GasConstant*thermo(0).temperature(); + doublereal rrt = 1.0/rt; + int np = nPhases(); + for (n = 0; n < np; n++) { + thermo(n).getStandardChemPotentials(DATA_PTR(m_mu0) + m_start[n]); + nsp = thermo(n).nSpecies(); + for (k = 0; k < nsp; k++) { + m_mu0[ik] -= rt*thermo(n).logStandardConc(k); + m_mu0[ik] += Faraday * m_phi[n] * thermo(n).charge(k); + ik++; + } + } + + fill(kc, kc + m_ii, 0.0); + + m_reactantStoich.decrementReactions(DATA_PTR(m_mu0), kc); + m_revProductStoich.incrementReactions(DATA_PTR(m_mu0), kc); + m_irrevProductStoich.incrementReactions(DATA_PTR(m_mu0), kc); + + for (i = 0; i < m_ii; i++) { + kc[i] = exp(-kc[i]*rrt); + } + } + + + /** + * For reactions that transfer charge across a potential difference, + * the activation energies are modified by the potential difference. + * (see, for example, Baird and Falkner, "Electrochemical Methods"). + * This method applies this correction. + */ + void EdgeKinetics::applyButlerVolmerCorrection(doublereal* kf) { + int i; + + int n, nsp, k, ik=0; + doublereal rt = GasConstant*thermo(0).temperature(); + doublereal rrt = 1.0/rt; + int np = nPhases(); + + // compute the electrical potential energy of each species + for (n = 0; n < np; n++) { + nsp = thermo(n).nSpecies(); + for (k = 0; k < nsp; k++) { + m_pot[ik] = Faraday*thermo(n).charge(k)*m_phi[n]; + ik++; + } + } + + // compute the change in electrical potential energy for each + // reaction. This will only be non-zero if a potential + // difference is present. + fill(DATA_PTR(m_rwork), DATA_PTR(m_rwork) + m_ii, 0.0); + m_reactantStoich.decrementReactions(DATA_PTR(m_pot), DATA_PTR(m_rwork)); + m_revProductStoich.incrementReactions(DATA_PTR(m_pot), DATA_PTR(m_rwork)); + m_irrevProductStoich.incrementReactions(DATA_PTR(m_pot), DATA_PTR(m_rwork)); + + // modify the reaction rates. Only modify those with a + // non-zero activation energy, and do not decrease the + // activation energy below zero. + doublereal ea, eamod; + + int nct = m_beta.size(); + int irxn; + for (i = 0; i < nct; i++) { + irxn = m_ctrxn[i]; + eamod = m_beta[i]*m_rwork[irxn]; + //cout << "i, beta = " << i << " " << m_beta[i] << endl; + if (eamod != 0.0 && m_E[i] != 0.0) { + ea = GasConstant * m_E[i]; + if (eamod + ea < 0.0) { + writelog("Warning: act energy mod too large"); + eamod = -ea; + } + kf[irxn] *= exp(-eamod*rrt); + } + } + } + + + /** + * Update the rates of progress of the reactions in the reaciton + * mechanism. This routine operates on internal data. + */ + void EdgeKinetics::updateROP() { + + _update_rates_T(); + _update_rates_C(); + + if (m_kdata->m_ROP_ok) return; + + const vector_fp& rf = m_kdata->m_rfn; + const vector_fp& m_rkc = m_kdata->m_rkcn; + array_fp& ropf = m_kdata->m_ropf; + array_fp& ropr = m_kdata->m_ropr; + array_fp& ropnet = m_kdata->m_ropnet; + + // copy rate coefficients into ropf + copy(rf.begin(), rf.end(), ropf.begin()); + + // multiply by perturbation factor + multiply_each(ropf.begin(), ropf.end(), m_perturb.begin()); + + // copy the forward rates to the reverse rates + copy(ropf.begin(), ropf.end(), ropr.begin()); + + // for reverse rates computed from thermochemistry, multiply + // the forward rates copied into m_ropr by the reciprocals of + // the equilibrium constants + multiply_each(ropr.begin(), ropr.end(), m_rkc.begin()); + + // multiply ropf by concentration products + m_reactantStoich.multiply(DATA_PTR(m_conc), DATA_PTR(ropf)); + + // for reversible reactions, multiply ropr by concentration + // products + m_revProductStoich.multiply(DATA_PTR(m_conc), DATA_PTR(ropr)); + + // do global reactions + //m_globalReactantStoich.power(DATA_PTR(m_conc), ropf.begin()); + + for (int j = 0; j != m_ii; ++j) { + ropnet[j] = ropf[j] - ropr[j]; + } + + m_kdata->m_ROP_ok = true; + } + + + /** + * Add a single reaction to the mechanism. This routine + * must be called after init() and before finalize(). + * This function branches on the types of reactions allowed + * by the interfaceKinetics manager in order to install + * the reaction correctly in the manager. + * The manager allows the following reaction types + * Elementary + * Surface + * Global + * There is no difference between elementary and surface + * reactions. + */ + void EdgeKinetics:: + addReaction(const ReactionData& r) { + + int nr = r.reactants.size(); + + // a global reaction is idnetified as one with + // a reactant stoichiometric coefficient not equal + // to the molecularity for some reactant + bool isglobal = false; + for (int n = 0; n < nr; n++) { + if (r.rstoich[n] != int(r.order[n])) { + isglobal = true; break; + } + } + if (isglobal) + addGlobalReaction(r); + else + addElementaryReaction(r); + + installReagents( r ); + installGroups(reactionNumber(), r.rgroups, r.pgroups); + incrementRxnCount(); + m_rxneqn.push_back(r.equation); + } + + + void EdgeKinetics:: + addElementaryReaction(const ReactionData& r) { + int iloc; + + // install rate coeff calculator + vector_fp rp = r.rateCoeffParameters; + + // coverage dependence + int ncov = r.cov.size(); + for (int m = 0; m < ncov; m++) rp.push_back(r.cov[m]); + + iloc = m_rates.install( reactionNumber(), r.rateCoeffType, rp.size(), + DATA_PTR(rp) ); + + // store activation energy + if (r.beta > 0.0) { + m_has_electrochem_rxns = true; + m_E.push_back(r.rateCoeffParameters[2]); + m_beta.push_back(r.beta); + m_ctrxn.push_back(reactionNumber()); + } + + // add constant term to rate coeff value vector + m_kdata->m_rfn.push_back(r.rateCoeffParameters[0]); + + registerReaction( reactionNumber(), ELEMENTARY_RXN, iloc); + } + + + void EdgeKinetics:: + addGlobalReaction(const ReactionData& r) { + + int iloc; + // install rate coeff calculator + vector_fp rp = r.rateCoeffParameters; + int ncov = r.cov.size(); + for (int m = 0; m < ncov; m++) rp.push_back(r.cov[m]); + iloc = m_rates.install( reactionNumber(), + r.rateCoeffType, rp.size(), + DATA_PTR(rp) ); + + // add constant term to rate coeff value vector + m_kdata->m_rfn.push_back(r.rateCoeffParameters[0]); + + int nr = r.order.size(); + vector_fp ordr(nr); + for (int n = 0; n < nr; n++) { + ordr[n] = r.order[n] - r.rstoich[n]; + } + m_globalReactantStoich.add( reactionNumber(), + r.reactants, ordr); + + registerReaction( reactionNumber(), GLOBAL_RXN, iloc); + } + + + void EdgeKinetics::installReagents(const ReactionData& r) { + + m_kdata->m_ropf.push_back(0.0); // extend by one for new rxn + m_kdata->m_ropr.push_back(0.0); + m_kdata->m_ropnet.push_back(0.0); + int n, ns, m; + doublereal nsFlt; + int rnum = reactionNumber(); + + vector_int rk; + int nr = r.reactants.size(); + for (n = 0; n < nr; n++) { + nsFlt = r.rstoich[n]; + ns = (int) nsFlt; + if ((doublereal) ns != nsFlt) { + if (ns < 1) ns = 1; + } + m_rrxn[r.reactants[n]][rnum] = ns; + for (m = 0; m < ns; m++) { + rk.push_back(r.reactants[n]); + } + } + m_reactants.push_back(rk); + + vector_int pk; + int np = r.products.size(); + for (n = 0; n < np; n++) { + nsFlt = r.pstoich[n]; + ns = (int) nsFlt; + if ((doublereal) ns != nsFlt) { + if (ns < 1) ns = 1; + } + m_prxn[r.products[n]][rnum] = ns; + for (m = 0; m < ns; m++) { + pk.push_back(r.products[n]); + } + } + m_products.push_back(pk); + + m_kdata->m_rkcn.push_back(0.0); + + m_reactantStoich.add( reactionNumber(), rk); + + if (r.reversible) { + m_revProductStoich.add(reactionNumber(), pk); + //m_dn.push_back(pk.size() - rk.size()); + m_revindex.push_back(reactionNumber()); + m_nrev++; + } + else { + m_irrevProductStoich.add(reactionNumber(), pk); + //m_dn.push_back(pk.size() - rk.size()); + m_irrev.push_back( reactionNumber() ); + m_nirrev++; + } + } + + + void EdgeKinetics::installGroups(int irxn, + const vector& r, const vector& p) { + if (!r.empty()) { + m_rgroups[reactionNumber()] = r; + m_pgroups[reactionNumber()] = p; + } + } + + /** + * Prepare the class for the addition of reactions. This function + * must be called after instantiation of the class, but before + * any reactions are actually added to the mechanism. + * This function calculates m_kk the number of species in all + * phases participating in the reaction mechanism. We don't know + * m_kk previously, before all phases have been added. + */ + void EdgeKinetics::init() { + int n; + m_kk = 0; + int np = nPhases(); + for (n = 0; n < np; n++) { + m_kk += thermo(n).nSpecies(); + } + m_rrxn.resize(m_kk); + m_prxn.resize(m_kk); + m_conc.resize(m_kk); + m_mu0.resize(m_kk); + m_pot.resize(m_kk, 0.0); + m_phi.resize(np, 0.0); + } + + /** + * Finish adding reactions and prepare for use. This function + * must be called after all reactions are entered into the mechanism + * and before the mechanism is used to calculate reaction rates. + * + * Here, we resize work arrays based on the number of reactions, + * since we don't know this number up to now. + */ + void EdgeKinetics::finalize() { + m_rwork.resize(nReactions()); + m_finalized = true; + } + + + bool EdgeKinetics::ready() const { + return (m_finalized); + } + +} + + + + + + + + diff --git a/Cantera/src/kinetics/EdgeKinetics.h b/Cantera/src/kinetics/EdgeKinetics.h new file mode 100644 index 000000000..c2f6e1b4d --- /dev/null +++ b/Cantera/src/kinetics/EdgeKinetics.h @@ -0,0 +1,392 @@ +/** + * @file EdgeKinetics.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_EDGEKINETICS_H +#define CT_EDGEKINETICS_H + +#include +#include +#include +#include + +#include "mix_defs.h" +#include "Kinetics.h" + +#include "utilities.h" +#include "RateCoeffMgr.h" +#include "StoichManager.h" + +namespace Cantera { + + // forward references + + class ReactionData; + class EdgeKineticsData; + class ThermoPhase; + class SurfPhase; + class ImplicitSurfChem; + + /** + * Holds mechanism-specific data. + */ + class EdgeKineticsData { + public: + EdgeKineticsData() : + m_ROP_ok(false), + m_temp(0.0), m_logtemp(0.0) + {} + virtual ~EdgeKineticsData(){} + + doublereal m_logp0, m_logc0; + array_fp m_ropf, m_ropr, m_ropnet; + //array_fp m_rfn_low, m_rfn_high; + bool m_ROP_ok; + + doublereal m_temp, m_logtemp; + vector_fp m_rfn; + vector_fp m_rkcn; + }; + + + class EdgeKinetics : public Kinetics { + + public: + + /** + * Constructor + * + */ + EdgeKinetics(); + + /// Destructor. + virtual ~EdgeKinetics(); + + /** + * Identifies the subclass of the Kinetics manager type. + * These are listed in mix_defs.h. + */ + virtual int ID() { return cEdgeKinetics; } + + /** + * Identifies the subclass of the Kinetics manager type. + * These are listed in mix_defs.h. + */ + virtual int type() { return cEdgeKinetics; } + + /** + * Set the electric potential in the nth phase + * + * @param n phase Index in this kinetics object. + * @param V Electric potential (volts) + */ + void setElectricPotential(int n, doublereal V) { + thermo(n).setElectricPotential(V); + m_redo_rates = true; + } + + /** + * @name Reaction Rates Of Progress + */ + //@{ + + /** + * Forward rates of progress. + * Return the forward rates of progress in array fwdROP, which + * must be dimensioned at least as large as the total number + * of reactions. + * Units are kmol/m2/s + */ + virtual void getFwdRatesOfProgress(doublereal* fwdROP) { + updateROP(); + std::copy(m_kdata->m_ropf.begin(), m_kdata->m_ropf.end(), fwdROP); + } + + /** + * Reverse rates of progress. + * Return the reverse rates of progress in array revROP, which + * must be dimensioned at least as large as the total number + * of reactions. + * Units are kmol/m2/s + */ + virtual void getRevRatesOfProgress(doublereal* revROP) { + updateROP(); + std::copy(m_kdata->m_ropr.begin(), m_kdata->m_ropr.end(), revROP); + } + + /** + * Net rates of progress. Return the net (forward - reverse) + * rates of progress in array netROP, which must be + * dimensioned at least as large as the total number of + * reactions. + * Units are kmol/m2/s + */ + virtual void getNetRatesOfProgress(doublereal* netROP) { + updateROP(); + std::copy(m_kdata->m_ropnet.begin(), m_kdata->m_ropnet.end(), netROP); + } + + /** + * Equilibrium constants. Return the equilibrium constants of + * the reactions in concentration units in array kc, which + * must be dimensioned at least as large as the total number + * of reactions. + */ + virtual void getEquilibriumConstants(doublereal* kc); + + + //@} + /** + * @name Species Production Rates + */ + //@{ + + /** + * Species creation rates [kmol/m^2/s]. Return the species + * creation rates in array cdot, which must be + * dimensioned at least as large as the total number of + * species in all phases of the kinetics + * model + * + */ + virtual void getCreationRates(doublereal* cdot) { + updateROP(); + std::fill(cdot, cdot + m_kk, 0.0); + m_revProductStoich.incrementSpecies( + &m_kdata->m_ropf[0], cdot); + m_irrevProductStoich.incrementSpecies( + &m_kdata->m_ropf[0], cdot); + m_reactantStoich.incrementSpecies( + &m_kdata->m_ropr[0], cdot); + } + + /** + * Species destruction rates [kmol/m^2/s]. Return the species + * destruction rates in array ddot, which must be + * dimensioned at least as large as the total number of + * species in all phases of the kinetics + * model + * + */ + virtual void getDestructionRates(doublereal* ddot) { + updateROP(); + std::fill(ddot, ddot + m_kk, 0.0); + m_revProductStoich.incrementSpecies( + &m_kdata->m_ropr[0], ddot); + m_reactantStoich.incrementSpecies( + &m_kdata->m_ropf[0], ddot); + } + + /** + * Species net production rates [kmol/m^2/s]. Return the species + * net production rates (creation - destruction) in array + * wdot, which must be dimensioned at least as large as the + * total number of species in all phases of the kinetics + * model + */ + virtual void getNetProductionRates(doublereal* net) { + updateROP(); + std::fill(net, net + m_kk, 0.0); + m_revProductStoich.incrementSpecies( + &m_kdata->m_ropnet[0], net); + m_irrevProductStoich.incrementSpecies( + &m_kdata->m_ropnet[0], net); + m_reactantStoich.decrementSpecies( + &m_kdata->m_ropnet[0], net); + } + + //@} + /** + * @name Reaction Mechanism Informational Query Routines + */ + //@{ + + /** + * Stoichiometric coefficient of species k as a reactant in + * reaction i. + */ + virtual doublereal reactantStoichCoeff(int k, int i) const { + return m_rrxn[k][i]; + } + + /** + * Stoichiometric coefficient of species k as a product in + * reaction i. + */ + virtual doublereal productStoichCoeff(int k, int i) const { + return m_prxn[k][i]; + } + + /** + * Flag specifying the type of reaction. The legal values and + * their meaning are specific to the particular kinetics + * manager. + */ + virtual int reactionType(int i) const { + return m_index[i].first; + } + + /** + * True if reaction i has been declared to be reversible. If + * isReversible(i) is false, then the reverse rate of progress + * for reaction i is always zero. + */ + virtual bool isReversible(int i) { + if (std::find(m_revindex.begin(), m_revindex.end(), i) + < m_revindex.end()) return true; + else return false; + } + + /** + * Return a string representing the reaction. + */ + virtual std::string reactionString(int i) const { + return m_rxneqn[i]; + } + + //@} + /** + * @name Reaction Mechanism Construction + */ + //@{ + + /** + * Prepare the class for the addition of reactions. This function + * must be called after instantiation of the class, but before + * any reactions are actually added to the mechanism. + * This function calculates m_kk the number of species in all + * phases participating in the reaction mechanism. We don't know + * m_kk previously, before all phases have been added. + */ + virtual void init(); + + /** + * Add a single reaction to the mechanism. + */ + virtual void addReaction(const ReactionData& r); + + /** + * Finish adding reactions and prepare for use. This function + * must be called after all reactions are entered into the mechanism + * and before the mechanism is used to calculate reaction rates. + */ + virtual void finalize(); + virtual bool ready() const; + + + void updateROP(); + + + const std::vector& reactantGroups(int i) + { return m_rgroups[i]; } + const std::vector& productGroups(int i) + { return m_pgroups[i]; } + + void _update_rates_T(); + void _update_rates_phi(); + void _update_rates_C(); + void checkPartialEquil(); + + protected: + /** + * m_kk here is the number of species in all of the phases + * that participate in the kinetics mechanism. + */ + int m_kk; + + Rate1 m_rates; + //Rate1 m_rates; + bool m_redo_rates; + + /** + * Vector of information about reactions in the + * mechanism. + * The key is the reaction index (0 < i < m_ii). + * The first pair is the reactionType of the reaction. + * The second pair is ... + */ + mutable std::map > m_index; + + std::vector m_irrev; + + StoichManagerN m_reactantStoich; + StoichManagerN m_revProductStoich; + StoichManagerN m_irrevProductStoich; + + StoichManagerN m_globalReactantStoich; + + int m_nirrev; + + /** + * Number of reversible reactions in the mechanism + */ + int m_nrev; + + std::map > m_rgroups; + std::map > m_pgroups; + + std::vector m_rxntype; + + mutable std::vector > m_rrxn; + mutable std::vector > m_prxn; + + vector_int m_revindex; + std::vector m_rxneqn; + + /** + * Temporary data storage used in calculating the rates of + * of reactions. + */ + EdgeKineticsData* m_kdata; + + /** + * An array of generalized concentrations + * \f$ C_k \f$ that are defined such that \f$ a_k = C_k / + * C^0_k, \f$ where \f$ C^0_k \f$ is a standard concentration/ + * These generalized concentrations are used + * by this kinetics manager class to compute the forward and + * reverse rates of elementary reactions. The "units" for the + * concentrations of each phase depend upon the implementation + * of kinetics within that phase. + * The order of the species within the vector is based on + * the order of listed ThermoPhase objects in the class, and the + * order of the species within each ThermoPhase class. + */ + vector_fp m_conc; + + vector_fp m_mu0; + vector_fp m_phi; + vector_fp m_pot; + vector_fp m_rwork; + vector_fp m_E; + vector_fp m_beta; + vector_int m_ctrxn; + + private: + + int reactionNumber(){ return m_ii;} + void addElementaryReaction(const ReactionData& r); + void addGlobalReaction(const ReactionData& r); + void installReagents(const ReactionData& r); + + void installGroups(int irxn, const std::vector& r, + const std::vector& p); + void updateKc(); + + void registerReaction(int rxnNumber, int type, int loc) { + m_index[rxnNumber] = std::pair(type, loc); + } + void applyButlerVolmerCorrection(doublereal* kf); + bool m_finalized; + bool m_has_electrochem_rxns; + }; +} + +#endif diff --git a/Cantera/src/kinetics/Enhanced3BConc.h b/Cantera/src/kinetics/Enhanced3BConc.h new file mode 100755 index 000000000..0aa892a64 --- /dev/null +++ b/Cantera/src/kinetics/Enhanced3BConc.h @@ -0,0 +1,81 @@ +/** + * @file Enhanced3BConc.h + */ + +/* $Author$ + * $Date$ + * $Revision$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_ENH_CONC_H +#define CT_ENH_CONC_H + +#include + +namespace Cantera { + + /** + * Computes enhanced third-body concentrations. + * @see GasKinetics + */ + class Enhanced3BConc { + + public: + + Enhanced3BConc() : m_n (0), m_deflt (1.0) {} + + Enhanced3BConc(int n, const std::map& enhanced, + doublereal deflt = 1.0) { + std::map::const_iterator iter; + for (iter = enhanced.begin(); iter != enhanced.end(); ++iter) { + m_index.push_back( iter->first ); + m_eff.push_back( iter->second - deflt); + } + m_deflt = deflt; + m_n = n; + } + + Enhanced3BConc(int n, const vector_int& e_index, + const vector_fp& efficiencies, doublereal deflt = 1.0) + : m_index (e_index), m_eff (efficiencies) { + int i; + m_n = n; + m_deflt = deflt; + for (i = 0; i < m_n; i++) { + m_eff[i] -= m_deflt; + } + } + + doublereal update(const vector_fp& c, doublereal ctot) const { + int i; + doublereal sum = 0.0; + for (i = 0; i < m_n; i++) { + sum += m_eff[i] * c[m_index[i]]; + } + return m_deflt * ctot + sum; + } + + void getEfficiencies(vector_fp& eff) const { + int i; + for (i = 0; i < m_n; i++) { + eff[m_index[i]] = m_eff[i] + m_deflt; + } + } + + private: + int m_n; + vector_int m_index; + vector_fp m_eff; + doublereal m_deflt; + }; + +} + +#endif + + + + diff --git a/Cantera/src/kinetics/FalloffFactory.cpp b/Cantera/src/kinetics/FalloffFactory.cpp new file mode 100755 index 000000000..f42f5b6ba --- /dev/null +++ b/Cantera/src/kinetics/FalloffFactory.cpp @@ -0,0 +1,288 @@ +/** + * @file FalloffFactory.cpp + */ + +/* $Author$ + * $Date$ + * $Revision$ + */ + +// Copyright 2001 California Institute of Technology + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include + +#include "FalloffFactory.h" + +namespace Cantera { + + FalloffFactory* FalloffFactory::s_factory = 0; + + /** + * The 3-parameter Troe falloff parameterization. + * This parameterization is + * defined by + * \f[ F = F_{cent}^{1/(1 + f_1^2)} \f] + * where + * \f[ F_{cent} = (1 - A)\exp(-T/T_3) + A \exp(-T/T_1) \f] + * \f[ f_1 = (\log_{10} P_r + C) / \left(N - 0.14 + * (\log_{10} P_r + C)\right) \f] + * \f[ C = -0.4 - 0.67 \log_{10} F_{cent} \f] + * \f[ N = 0.75 - 1.27 \log_{10} F_{cent} \f] + */ + class Troe3 : public Falloff { + public: + + /// Default constructor. + Troe3() : m_a (0.0), m_rt3 (0.0), m_rt1 (0.0) {} + + // Destructor. Does nothing. + virtual ~Troe3() {} + + /** + * Initialize. + * @param c Coefficient vector of length 3, + * with entries \f$ (A, T_3, T_1) \f$ + */ + virtual void init(const vector_fp& c) { + m_a = c[0]; + m_rt3 = 1.0/c[1]; + m_rt1 = 1.0/c[2]; + } + + virtual void updateTemp(doublereal T, workPtr work) const { + doublereal Fcent = (1.0 - m_a) * exp(- T * m_rt3 ) + + m_a * exp(- T * m_rt1 ); + *work = log10( fmaxx( Fcent, SmallNumber ) ); + } + + virtual doublereal F(doublereal pr, const_workPtr work) const { + doublereal lpr,f1,lgf, cc, nn; + lpr = log10( fmaxx(pr,SmallNumber) ); + cc = -0.4 - 0.67 * (*work); + nn = 0.75 - 1.27 * (*work); + f1 = ( lpr + cc )/ ( nn - 0.14 * ( lpr + cc ) ); + lgf = (*work) / ( 1.0 + f1 * f1 ); + return pow(10.0, lgf ); + } + + virtual size_t workSize() { return 1; } + + protected: + + doublereal m_a, m_rt3, m_rt1; + + private: + + }; + + + + /** + * The 4-parameter Troe falloff parameterization. This parameterization is + * defined by + * + * \f[ F = F_{cent}^{1/(1 + f_1^2)} \f] + * where + * \f[ F_{cent} = (1 - A)\exp(-T/T_3) + A \exp(-T/T_1) + \exp(-T_2/T) \f] + * \f[ f_1 = (\log_{10} P_r + C) / \left(N - 0.14 + * (\log_{10} P_r + C)\right) \f] + * \f[ C = -0.4 - 0.67 \log_{10} F_{cent} \f] + * \f[ N = 0.75 - 1.27 \log_{10} F_{cent} \f] + * + */ + + class Troe4 : public Falloff { + public: + + Troe4() : m_a (0.0), m_rt3 (0.0), m_rt1 (0.0), + m_t2 (0.0) {} + virtual ~Troe4() {} + + virtual void init(const vector_fp& c) { + m_a = c[0]; + m_rt3 = 1.0/c[1]; + m_rt1 = 1.0/c[2]; + m_t2 = c[3]; + } + + virtual void updateTemp(doublereal T, workPtr work) const { + doublereal Fcent = (1.0 - m_a) * exp(- T * m_rt3 ) + + m_a * exp(- T * m_rt1 ) + + exp(- m_t2 / T ); + *work = log10( fmaxx( Fcent, SmallNumber ) ); + } + + virtual doublereal F(doublereal pr, const_workPtr work) const { + doublereal lpr,f1,lgf, cc, nn; + lpr = log10( fmaxx(pr,SmallNumber) ); + cc = -0.4 - 0.67 * (*work); + nn = 0.75 - 1.27 * (*work); + f1 = ( lpr + cc )/ ( nn - 0.14 * ( lpr + cc ) ); + lgf = (*work) / ( 1.0 + f1 * f1 ); + return pow(10.0, lgf ); + } + + virtual size_t workSize() { return 1; } + + protected: + + doublereal m_a, m_rt3, m_rt1; + doublereal m_t2; + + private: + }; + + /** + * The 3-parameter SRI falloff function. + */ + class SRI3 : public Falloff { + + public: + + SRI3() {} + virtual ~SRI3() {} + + virtual void init(const vector_fp& c) { + m_a = c[0]; + m_b = c[1]; + m_c = c[2]; + } + + virtual void updateTemp(doublereal T, workPtr work) const { + *work = m_a * exp( - m_b / T); + if (m_c != 0.0) *work += exp( - T/m_c ); + } + + virtual doublereal F(doublereal pr, const_workPtr work) const { + doublereal lpr = log10( fmaxx(pr,SmallNumber) ); + doublereal xx = 1.0/(1.0 + lpr*lpr); + doublereal ff = pow( *work , xx); + return ff; + } + + virtual size_t workSize() { return 1; } + + protected: + doublereal m_a, m_b, m_c; + + private: + + }; + + + /** + * The 5-parameter SRI falloff function. + */ + class SRI5 : public Falloff { + + public: + SRI5() {} + virtual ~SRI5() {} + virtual void init(const vector_fp& c) { + m_a = c[0]; + m_b = c[1]; + m_c = c[2]; + m_d = c[3]; + m_e = c[4]; + } + + virtual void updateTemp(doublereal T, workPtr work) const { + *work = m_a * exp( - m_b / T); + if (m_c != 0.0) *work += exp( - T/m_c ); + work[1] = m_d * pow(T,m_e); + } + + virtual doublereal F(doublereal pr, const_workPtr work) const { + doublereal lpr = log10( fmaxx(pr,SmallNumber) ); + doublereal xx = 1.0/(1.0 + lpr*lpr); + return pow( *work, xx) * work[1]; + } + + virtual size_t workSize() { return 2; } + + protected: + + doublereal m_a, m_b, m_c; + doublereal m_d, m_e; + + private: + + }; + + + /** + * Wang-Frenklach falloff function. Reference: Wang, H., and + * Frenklach, M., Chem. Phys. Lett. vol. 205, 271 (1993). + */ + class WF93 : public Falloff { + + public: + WF93() {} + virtual ~WF93() {} + + virtual void init(const vector_fp& c) { + m_a = c[0]; + m_rt1 = 1.0/c[1]; + m_t2 = c[2]; + m_rt3 = 1.0/c[3]; + m_alpha0 = c[4]; + m_alpha1 = c[5]; + m_alpha2 = c[6]; + m_sigma0 = c[7]; + m_sigma1 = c[8]; + m_sigma2 = c[9]; + } + + virtual void updateTemp(doublereal T, workPtr work) const { + work[0] = m_alpha0 + (m_alpha1 + m_alpha2*T)*T; // alpha + work[1] = m_sigma0 + (m_sigma1 + m_sigma2*T)*T; // sigma + doublereal Fcent = (1.0 - m_a) * exp(- T * m_rt3 ) + + m_a * exp(- T * m_rt1 ) + exp(-m_t2/T); + work[2] = log10(Fcent); + } + + virtual doublereal F(doublereal pr, const_workPtr work) const { + doublereal lpr = log10( fmaxx(pr, SmallNumber) ); + doublereal x = (lpr - work[0])/work[1]; + doublereal flog = work[2]/exp(x*x); + return pow( 10.0, flog); + } + + virtual size_t workSize() { return 3; } + + protected: + + doublereal m_alpha0, m_alpha1, m_alpha2; + doublereal m_sigma0, m_sigma1, m_sigma2; + doublereal m_a, m_rt1, m_t2, m_rt3; + + private: + + }; + + + Falloff* FalloffFactory::newFalloff(int type, const vector_fp& c) { + Falloff* f; + switch(type) { + case TROE3_FALLOFF: + f = new Troe3(); break; + case TROE4_FALLOFF: + f = new Troe4(); break; + case SRI3_FALLOFF: + f = new SRI3(); break; + case SRI5_FALLOFF: + f = new SRI5(); break; + case WF_FALLOFF: + f = new WF93(); break; + default: return 0; + } + f->init(c); + return f; + } + +} diff --git a/Cantera/src/kinetics/FalloffFactory.h b/Cantera/src/kinetics/FalloffFactory.h new file mode 100755 index 000000000..f014df598 --- /dev/null +++ b/Cantera/src/kinetics/FalloffFactory.h @@ -0,0 +1,136 @@ +/** + * @file FalloffFactory.h + * + * Parameterizations for reaction falloff functions. Used by classes + * that implement gas-phase kinetics (GasKinetics, GRI_30_Kinetics). + */ + + +/* + * $Author$ + * $Date$ + * $Revision$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_NEWFALLOFF_H +#define CT_NEWFALLOFF_H + +#include "ct_defs.h" +#include "reaction_defs.h" + +namespace Cantera { + + /** + * Base class for falloff function calculators. Each instance of a + * subclass of Falloff computes one falloff function. + */ + class Falloff { + public: + + Falloff(){} + virtual ~Falloff(){} + + /** + * Initialize. Must be called before any other method is + * invoked. + * + * @param c Vector of coefficients of the parameterization. + * The number and meaning of these coefficients is + * subclass-dependent. + */ + virtual void init(const vector_fp& c) =0; + + /** + * Update the temperature-dependent portions of the falloff + * function, if any. This method evaluates temperature-dependent + * intermediate results and stores them in the 'work' array. + * If not overloaded, the default behavior is to do nothing. + * @param T Temperature [K]. + * @param work storage space for intermediate results. + */ + virtual void updateTemp (doublereal T, workPtr work) const {} + + /** + * The falloff function. This is defined so that the + * rate coefficient is + * \f[ k = F(Pr)\frac{Pr}{1 + Pr}. \f] + * Here \f$ Pr \f$ is the reduced pressure, defined by + * \f[ + * Pr = \frac{k_0 [M]}{k_\infty}. + * \f] + * @param pr reduced pressure (dimensionless). + * @param work array of size workSize() containing cached + * temperature-dependent intermediate results from a prior call + * to updateTemp. + */ + virtual doublereal F(doublereal pr, const_workPtr work) const =0; + + /** + * The size of the work array required. + */ + virtual size_t workSize() =0; + + protected: + private: + }; + + + + /** + * Factory class to construct falloff function calculators. + * The falloff factory is accessed through static method factory: + * @code + * Falloff* f = FalloffFactory::factory()->newFalloff(type, c) + * @endcode + * @ingroup falloffGroup + */ + class FalloffFactory { + public: + + /** + * Return a pointer to the factory. On the first call, a new + * instance is created. Since there is no need to instantiate + * more than one factory, on all subsequent calls, a pointer + * to the existing factory is returned. + */ + static FalloffFactory* factory() { + if (!s_factory) s_factory = new FalloffFactory; + return s_factory; + } + + static void deleteFalloffFactory() { + if (s_factory) { + delete s_factory; + s_factory = 0; + } + } + + /** + * Destructor doesn't do anything. We do not delete statically + * created single instance of this class here, because it would + * create an infinite loop if destructor is called for that + * single instance. Instead, to delete single instance, we + * call delete[] from FalloffMng's destructor. + */ + virtual ~FalloffFactory() { + } + + /** + * Return a pointer to a new falloff function calculator. + * @param type Integer flag specifying the type of falloff function. + * The standard types are defined in file reaction_defs.h. A factory + * class derived from FalloffFactory may define other types as well. + */ + virtual Falloff* newFalloff(int type, const vector_fp& c); + + private: + static FalloffFactory* s_factory; + FalloffFactory(){} + }; + +} +#endif + diff --git a/Cantera/src/kinetics/FalloffMgr.h b/Cantera/src/kinetics/FalloffMgr.h new file mode 100755 index 000000000..4b8c82026 --- /dev/null +++ b/Cantera/src/kinetics/FalloffMgr.h @@ -0,0 +1,123 @@ +/** + * @file FalloffMgr.h + * + * $Author$ + * $Date$ + * $Revision$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_FALLOFFMGR_H +#define CT_FALLOFFMGR_H + +#include "reaction_defs.h" +#include "FalloffFactory.h" + +namespace Cantera { + + /** + * A falloff manager that implements any set of falloff functions. + * @ingroup falloffGroup + */ + class FalloffMgr { + public: + + /** + * Constructor. + * @param f If supplied, this factory will be used to construct + * falloff function calculators. If omitted, the standard factory + * will be used. + */ + FalloffMgr(FalloffFactory* f = 0) : + m_n(0), m_n0(0), m_worksize(0) { + if (f == 0) m_factory = FalloffFactory::factory(); + else m_factory = f; + } + + /** + * Destructor. Deletes all installed falloff function + * calculators. + */ + virtual ~FalloffMgr(){ + int i; + for (i = 0; i < m_n; i++) delete m_falloff[i]; + if (m_factory) { + FalloffFactory::deleteFalloffFactory(); + m_factory = 0; + } + } + + /** + * Install a new falloff function calculator. @param rxn + * Index of the falloff reaction. This will be used to determine + * which array entry is modified in method pr_to_falloff. + * + * @param type of falloff function to install. + * @param c vector of coefficients for the falloff function. + */ + void install(int rxn, int type, + const vector_fp& c) { + if (type != SIMPLE_FALLOFF) { + m_rxn.push_back(rxn); + Falloff* f = m_factory->newFalloff(type,c); + m_offset.push_back(m_worksize); + m_worksize += f->workSize(); + m_falloff.push_back(f); + m_n++; + } + else { + m_rxn0.push_back(rxn); + m_n0++; + } + } + + /** + * Size of the work array required to store intermediate results. + */ + size_t workSize() { return m_worksize; } + + /** + * Update the cached temperature-dependent intermediate + * results for all installed falloff functions. + * @param t Temperature [K]. + * @param work Work array. Must be dimensioned at least workSize(). + */ + void updateTemp(doublereal t, workPtr work) { + int i; + for (i = 0; i < m_n; i++) { + m_falloff[i]->updateTemp(t, + work + m_offset[i]); + } + } + + /** + * Given a vector of reduced pressures for each falloff reaction, + * replace each entry by the value of the falloff function. + */ + void pr_to_falloff(doublereal* values, const_workPtr work) { + doublereal pr; + int i; + for (i = 0; i < m_n0; i++) { + values[m_rxn0[i]] /= (1.0 + values[m_rxn0[i]]); + } + for (i = 0; i < m_n; i++) { + pr = values[m_rxn[i]]; + values[m_rxn[i]] *= + m_falloff[i]->F(pr, work + m_offset[i]) /(1.0 + pr); + } + } + + protected: + vector_int m_rxn, m_rxn0; + std::vector m_falloff; + FalloffFactory* m_factory; + vector_int m_loc; + int m_n, m_n0; + std::vector m_offset; + size_t m_worksize; + }; +} + +#endif diff --git a/Cantera/src/kinetics/GRI_30_Kinetics.cpp b/Cantera/src/kinetics/GRI_30_Kinetics.cpp new file mode 100755 index 000000000..f71429275 --- /dev/null +++ b/Cantera/src/kinetics/GRI_30_Kinetics.cpp @@ -0,0 +1,999 @@ +/** + * @file GRI_30_Kinetics.cpp + * + */ + +// Copyright 2001 California Institute of Technology + + +// turn off warnings under Windows +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "GRI_30_Kinetics.h" + +#include "ReactionData.h" +//#include "StoichManager.h" +#include "Enhanced3BConc.h" +#include "ThirdBodyMgr.h" +#include "RateCoeffMgr.h" +#include "IdealGasPhase.h" + +#include +using namespace std; + + +namespace Cantera { + + /** + * Construct an empty reaction mechanism. + */ + GRI_30_Kinetics:: + GRI_30_Kinetics(thermo_t* th) : GasKinetics(th) {} + + void GRI_30_Kinetics:: + gri30_update_rates_T() { + doublereal T = thermo().temperature(); + if (fabs(T - m_kdata->m_temp) > m_dt_threshold) { + doublereal logT = log(T); + m_kdata->m_logc_ref = m_kdata->m_logp_ref - logT; + update_rates(T, logT, &m_kdata->m_rfn[0]); + m_falloff_low_rates.update(T, logT, &m_kdata->m_rfn_low[0]); + m_falloff_high_rates.update(T, logT, &m_kdata->m_rfn_high[0]); + m_falloffn.updateTemp(T, &m_kdata->falloff_work[0]); + m_kdata->m_temp = T; + gri30_updateKc(); + m_kdata->m_ROP_ok = false; + } + }; + + + /** + * Update the equilibrium constants in molar units. + * @todo This formulation assumes an ideal gas. + */ + void GRI_30_Kinetics::gri30_updateKc() { + doublereal* rkc = &m_kdata->m_rkcn[0]; + const doublereal* a = + &((IdealGasPhase*)m_thermo[0])->expGibbs_RT_ref()[0]; + doublereal exp_c_ref = exp(m_kdata->m_logc_ref); + update_kc(a, exp_c_ref, rkc); + } + + + void GRI_30_Kinetics::gri30_updateROP() { + + gri30_update_rates_T(); + _update_rates_C(); + + if (m_kdata->m_ROP_ok) return; + + const vector_fp& rf = m_kdata->m_rfn; + const vector_fp& rkc = m_kdata->m_rkcn; + array_fp& ropf = m_kdata->m_ropf; + array_fp& ropnet = m_kdata->m_ropnet; + + copy(rf.begin(), rf.end(), ropf.begin()); + m_3b_concm.multiply( &ropf[0], &m_kdata->concm_3b_values[0] ); + processFalloffReactions(); + multiply_each(ropf.begin(), ropf.end(), m_perturb.begin()); + eval_ropnet(&m_conc[0], &ropf[0], &rkc[0], &ropnet[0]); + m_kdata->m_ROP_ok = true; + } + + + void GRI_30_Kinetics::update_rates(double t, double tlog, double* rf) { + double rt = 1.0/t; + rf[0] = exp(25.5108 + -1 * tlog); + rf[1] = exp(26.9379 + -1 * tlog); + rf[2] = exp(3.65584 + 2.7 * tlog - 3150.48 * rt); + rf[4] = exp(9.17264 + 2 * tlog - 2013.09 * rt); + rf[10] = exp(13.8353 + 1.5 * tlog - 4328.13 * rt); + rf[14] = exp(24.3868 - 1781.58 * rt); + rf[17] = exp(5.96101 + 2.5 * tlog - 1560.14 * rt); + rf[18] = exp(4.86753 + 2.5 * tlog - 2516.36 * rt); + rf[20] = exp(9.51044 + 2 * tlog - 956.215 * rt); + rf[21] = exp(38.3674 + -1.41 * tlog - 14569.7 * rt); + rf[22] = exp(8.84506 + 2 * tlog - 956.215 * rt); + rf[24] = exp(9.43348 + 1.83 * tlog - 110.72 * rt); + rf[26] = exp(11.4053 + 1.92 * tlog - 2863.61 * rt); + rf[28] = exp(23.0259 - 4026.17 * rt); + rf[29] = exp(21.2829 - 679.416 * rt); + rf[30] = exp(21.6396 - 24056.4 * rt); + rf[31] = exp(25.3284 - 20130.9 * rt); + rf[32] = exp(28.6606 + -0.86 * tlog); + rf[33] = exp(30.666 + -1.24 * tlog); + rf[34] = exp(30.0523 + -0.76 * tlog); + rf[35] = exp(30.8891 + -1.24 * tlog); + rf[36] = exp(27.2743 + -0.8 * tlog); + rf[37] = exp(30.9082 + -0.6707 * tlog - 8576.25 * rt); + rf[38] = exp(27.631 + -1 * tlog); + rf[39] = exp(25.2231 + -0.6 * tlog); + rf[40] = exp(31.7254 + -1.25 * tlog); + rf[41] = exp(33.9409 + -2 * tlog); + rf[42] = exp(37.6298 + -2 * tlog); + rf[43] = exp(22.102 - 337.695 * rt); + rf[44] = exp(24.5255 - 537.494 * rt); + rf[45] = exp(25.1541 - 319.577 * rt); + rf[46] = exp(9.40096 + 2 * tlog - 2617.01 * rt); + rf[47] = exp(23.0259 - 1811.78 * rt); + rf[52] = exp(13.4 + 1.62 * tlog - 5455.46 * rt); + rf[57] = exp(10.9578 + 1.9 * tlog - 1379.97 * rt); + rf[60] = exp(18.9215 + 0.65 * tlog - -142.929 * rt); + rf[61] = exp(24.2137 + -0.09 * tlog - 306.995 * rt); + rf[63] = exp(10.6334 + 1.63 * tlog - 968.294 * rt); + rf[65] = exp(21.1287 + 0.5 * tlog - -55.3598 * rt); + rf[66] = exp(26.2916 + -0.23 * tlog - 538.5 * rt); + rf[67] = exp(9.74097 + 2.1 * tlog - 2450.93 * rt); + rf[68] = exp(8.34284 + 2.1 * tlog - 2450.93 * rt); + rf[74] = exp(7.18917 + 2.53 * tlog - 6160.04 * rt); + rf[77] = exp(11.6527 + 1.9 * tlog - 3789.63 * rt); + rf[79] = exp(24.6353 - 4026.17 * rt); + rf[80] = exp(23.1481 - 1725.21 * rt); + rf[83] = exp(12.283 + 1.51 * tlog - 1726.22 * rt); + rf[85] = exp(3.57515 + 2.4 * tlog - -1061.9 * rt); + rf[86] = exp(23.3974 - -251.636 * rt); + rf[87] = exp(21.4164 - 214.897 * rt); + rf[88] = exp(35.0694 - 14801.2 * rt); + rf[92] = exp(9.33256 + 2 * tlog - 1509.81 * rt); + rf[95] = exp(10.9331 + 1.6 * tlog - 2727.73 * rt); + rf[96] = exp(34.0987 + -1.34 * tlog - 713.135 * rt); + rf[97] = exp(11.5129 + 1.6 * tlog - 1570.21 * rt); + rf[98] = exp(10.7706 + 1.228 * tlog - 35.229 * rt); + rf[100] = exp(15.0481 + 1.18 * tlog - -224.962 * rt); + rf[103] = exp(7.2724 + 2 * tlog - -422.748 * rt); + rf[104] = exp(8.7483 + 2 * tlog - 754.907 * rt); + rf[106] = exp(-15.3388 + 4.5 * tlog - -503.271 * rt); + rf[107] = exp(6.22258 + 2.3 * tlog - 6794.16 * rt); + rf[108] = exp(10.4253 + 2 * tlog - 7045.8 * rt); + rf[109] = exp(-14.5432 + 4 * tlog - -1006.54 * rt); + rf[111] = exp(8.18869 + 2 * tlog - 1258.18 * rt); + rf[112] = exp(8.17188 + 2.12 * tlog - 437.846 * rt); + rf[113] = exp(22.7382 - 1006.54 * rt); + rf[114] = exp(18.683 - -820.332 * rt); + rf[115] = exp(26.7635 - 6039.26 * rt); + rf[119] = exp(25.7339 - 11877.2 * rt); + rf[120] = exp(8.63052 + 2 * tlog - 6039.26 * rt); + rf[121] = exp(24.7837 - 289.884 * rt); + rf[125] = exp(25.4054 - 1565.17 * rt); + rf[126] = exp(22.4655 - -379.97 * rt); + rf[131] = exp(25.9703 - 7947.66 * rt); + rf[132] = exp(25.2729 - -259.185 * rt); + rf[134] = exp(22.3327 - 754.907 * rt); + rf[135] = exp(6.21461 + 2 * tlog - 3638.65 * rt); + rf[136] = exp(28.101 - 6011.07 * rt); + rf[138] = exp(7.80792 + 2 * tlog - 4162.05 * rt); + rf[141] = exp(23.4313 - 301.963 * rt); + rf[142] = exp(22.9205 - 301.963 * rt); + rf[148] = exp(23.2082 - -286.865 * rt); + rf[149] = exp(23.4959 - -286.865 * rt); + rf[153] = exp(24.4121 - -276.799 * rt); + rf[154] = exp(24.2956 - 15339.7 * rt); + rf[155] = exp(21.5605 - 10224 * rt); + rf[156] = exp(3.19867 + 2.47 * tlog - 2606.95 * rt); + rf[158] = exp(22.6461 + 0.1 * tlog - 5334.68 * rt); + rf[160] = exp(1.19996 + 2.81 * tlog - 2949.17 * rt); + rf[161] = exp(10.309 + 1.5 * tlog - 5002.52 * rt); + rf[162] = exp(9.21034 + 1.5 * tlog - 5002.52 * rt); + rf[163] = exp(5.42495 + 2 * tlog - 4630.1 * rt); + rf[164] = exp(8.72258 + 1.74 * tlog - 5259.18 * rt); + rf[165] = exp(34.9442 + -1 * tlog - 8555.61 * rt); + rf[166] = exp(32.8621 + -1 * tlog - 8555.61 * rt); + rf[167] = exp(23.3222 - 201.309 * rt); + rf[168] = exp(23.6136 - 452.944 * rt); + rf[169] = exp(-35.3874 + 7.6 * tlog - -1776.55 * rt); + rf[170] = exp(23.0259 - -379.97 * rt); + rf[171] = exp(17.855 + 0.9 * tlog - 1003.02 * rt); + rf[172] = exp(31.4553 + -1.39 * tlog - 510.82 * rt); + rf[174] = exp(20.5489 - 1950.18 * rt); + rf[175] = exp(21.8864 - 429.794 * rt); + rf[177] = exp(24.0191 - 178.661 * rt); + rf[178] = exp(16.0127 + 1 * tlog - 3271.26 * rt); + rf[179] = exp(24.2378 - 193.759 * rt); + rf[180] = exp(21.0597 - 5440.36 * rt); + rf[181] = exp(24.0906 - 11650.7 * rt); + rf[182] = exp(26.6817 - 9501.76 * rt); + rf[183] = exp(21.4164 - 10598.9 * rt); + rf[185] = exp(21.47 - -241.57 * rt); + rf[186] = exp(32.2945 + -1.41 * tlog); + rf[187] = exp(22.0842 - -120.785 * rt); + rf[188] = exp(25.6061 - 181.178 * rt); + rf[190] = exp(24.189 - 166.08 * rt); + rf[192] = exp(14.5087 + 1.2 * tlog); + rf[193] = exp(6.1334 + 2 * tlog - 3271.26 * rt); + rf[194] = exp(7.15462 + 1.5 * tlog - 50.3271 * rt); + rf[196] = exp(23.719 - 6970.31 * rt); + rf[197] = exp(23.796 + -0.23 * tlog); + rf[198] = exp(26.6232 + -0.45 * tlog); + rf[201] = exp(24.4121 - 1836.94 * rt); + rf[202] = exp(11.4076 + 1.5 * tlog - -231.505 * rt); + rf[204] = exp(25.5908 + -0.11 * tlog - 2506.29 * rt); + rf[211] = exp(31.4332 + -1.32 * tlog - 372.421 * rt); + rf[213] = exp(20.6179 + 0.72 * tlog - 332.159 * rt); + rf[214] = exp(9.4727 + 1.9 * tlog - -478.108 * rt); + rf[215] = exp(23.0259 - 6542.53 * rt); + rf[218] = exp(22.8027 - 3754.4 * rt); + rf[219] = exp(22.5381 - -221.439 * rt); + rf[220] = exp(5.68698 + 2.45 * tlog - 1127.33 * rt); + rf[225] = exp(21.4164 - 10065.4 * rt); + rf[226] = exp(26.4598 - 27201.8 * rt); + rf[227] = exp(32.878 + -1.52 * tlog - 372.421 * rt); + rf[228] = exp(35.8738 + -2 * tlog - 402.617 * rt); + rf[229] = exp(59.9064 + -3.3 * tlog - 63714.1 * rt); + rf[230] = exp(3.01062 + 2.64 * tlog - 2506.29 * rt); + rf[231] = exp(1.62334 + 2.64 * tlog - 2506.29 * rt); + rf[232] = exp(15.179 + 1.58 * tlog - 13387 * rt); + rf[233] = exp(7.00307 + 2.03 * tlog - 6728.74 * rt); + rf[234] = exp(1.4816 + 2.26 * tlog - 3220.94 * rt); + rf[235] = exp(-1.83258 + 2.56 * tlog - 4529.44 * rt); + rf[237] = exp(24.8176 - 201.309 * rt); + rf[238] = exp(24.8664 - 23160.5 * rt); + rf[239] = exp(14.9533 + 0.88 * tlog - 10130.9 * rt); + rf[241] = exp(23.0259 - 37242.1 * rt); + rf[242] = exp(18.4207 - 32712.6 * rt); + rf[248] = exp(33.3676 + -1.38 * tlog - 639.155 * rt); + rf[249] = exp(26.3931 + -0.69 * tlog - 382.486 * rt); + rf[250] = exp(24.3609 + -0.36 * tlog - 291.897 * rt); + rf[251] = exp(33.3676 + -1.38 * tlog - 639.155 * rt); + rf[252] = exp(26.3931 + -0.69 * tlog - 382.486 * rt); + rf[253] = exp(24.3609 + -0.36 * tlog - 291.897 * rt); + rf[254] = exp(25.2876 - 14494.2 * rt); + rf[255] = exp(20.7233 - 10946.1 * rt); + rf[261] = exp(11.4927 + 1.41 * tlog - 4277.81 * rt); + rf[262] = exp(11.9184 + 1.57 * tlog - 22143.9 * rt); + rf[263] = exp(7.69621 + 2.11 * tlog - 5737.29 * rt); + rf[264] = exp(10.0213 + 1.7 * tlog - 1912.43 * rt); + rf[265] = exp(4.65396 + 2.5 * tlog - 6693.51 * rt); + rf[266] = exp(10.4043 + 1.5 * tlog - 1811.78 * rt); + rf[267] = exp(8.10168 + 1.5 * tlog - 1811.78 * rt); + rf[268] = exp(30.0991 - 42637.1 * rt); + rf[269] = exp(28.373 + -0.69 * tlog - 1434.32 * rt); + rf[270] = exp(19.4139 + 0.18 * tlog - 1066.94 * rt); + rf[271] = exp(25.8591 + -0.75 * tlog - 1454.45 * rt); + rf[272] = exp(9.90349 + 2 * tlog - 1006.54 * rt); + rf[274] = exp(27.1367 + -0.31 * tlog - 145.949 * rt); + rf[275] = exp(22.0316 + 0.15 * tlog - -45.2944 * rt); + rf[276] = exp(6.29157 + 2.4 * tlog - 4989.93 * rt); + rf[277] = exp(10.8198 + 1.6 * tlog - 480.624 * rt); + rf[278] = exp(9.14846 + 1.94 * tlog - 3251.13 * rt); + rf[279] = exp(23.0259 - 7221.94 * rt); + rf[280] = exp(29.4491 + -0.752 * tlog - 173.629 * rt); + rf[281] = exp(21.9019 - -354.806 * rt); + rf[282] = exp(21.8219 - 5686.97 * rt); + rf[284] = exp(8.80986 + 1.83 * tlog - 110.72 * rt); + rf[286] = exp(29.2405 - 8721.69 * rt); + rf[287] = exp(15.895 + 0.5 * tlog - -883.241 * rt); + rf[289] = exp(22.4811 - 754.907 * rt); + rf[290] = exp(21.5987 - 754.907 * rt); + rf[291] = exp(26.0216 - 5530.45 * rt); + rf[292] = exp(18.038 + 0.25 * tlog - -470.559 * rt); + rf[293] = exp(19.5292 + 0.29 * tlog - 5.53598 * rt); + rf[294] = exp(7.19818 + 1.61 * tlog - -193.256 * rt); + rf[295] = exp(22.488 - 909.914 * rt); + rf[296] = exp(22.488 - 909.914 * rt); + rf[297] = exp(24.1278 - 19703.1 * rt); + rf[298] = exp(14.5334 + 1.16 * tlog - 1210.37 * rt); + rf[299] = exp(14.5334 + 1.16 * tlog - 1210.37 * rt); + rf[300] = exp(16.9695 + 0.73 * tlog - -560.141 * rt); + rf[301] = exp(21.8252 - 6000.5 * rt); + rf[302] = exp(7.90839 + 1.77 * tlog - 2979.37 * rt); + rf[312] = exp(5.26269 + 2.68 * tlog - 1870.16 * rt); + rf[313] = exp(7.18539 + 2.54 * tlog - 3400.1 * rt); + rf[314] = exp(10.3609 + 1.8 * tlog - 470.055 * rt); + rf[315] = exp(-0.972861 + 2.72 * tlog - 754.907 * rt); + rf[316] = exp(-7.00979 + 3.65 * tlog - 3600.4 * rt); + rf[320] = exp(8.30894 + 2.19 * tlog - 447.911 * rt); + rf[322] = exp(17.0542 + 0.255 * tlog - -474.585 * rt); + rf[324] = exp(23.6818 + -0.32 * tlog); + } + + + void GRI_30_Kinetics::update_kc(const double* a, double exp_c0, double* rkc) { + rkc[0] = a[3]*exp_c0/(a[2]*a[2]); + rkc[1] = a[4]*exp_c0/(a[1]*a[2]); + rkc[2] = a[1]*a[4]/(a[0]*a[2]); + rkc[3] = a[3]*a[4]/(a[2]*a[6]); + rkc[4] = a[4]*a[6]/(a[2]*a[7]); + rkc[5] = a[1]*a[14]/(a[2]*a[9]); + rkc[6] = a[1]*a[16]/(a[2]*a[10]); + rkc[7] = a[0]*a[14]/(a[2]*a[11]); + rkc[8] = a[1]*a[16]/(a[2]*a[11]); + rkc[9] = a[1]*a[17]/(a[2]*a[12]); + rkc[10] = a[4]*a[12]/(a[2]*a[13]); + rkc[11] = a[15]*exp_c0/(a[2]*a[14]); + rkc[12] = a[4]*a[14]/(a[2]*a[16]); + rkc[13] = a[1]*a[15]/(a[2]*a[16]); + rkc[14] = a[4]*a[16]/(a[2]*a[17]); + rkc[15] = a[4]*a[17]/(a[2]*a[18]); + rkc[16] = a[4]*a[17]/(a[2]*a[19]); + rkc[17] = a[4]*a[18]/(a[2]*a[20]); + rkc[18] = a[4]*a[19]/(a[2]*a[20]); + rkc[19] = a[9]*a[14]/(a[2]*a[21]); + rkc[20] = a[1]*a[27]/(a[2]*a[22]); + rkc[21] = a[4]*a[21]/(a[2]*a[22]); + rkc[22] = a[10]*a[14]/(a[2]*a[22]); + rkc[23] = a[1]*a[28]/(a[2]*a[23]); + rkc[24] = a[12]*a[16]/(a[2]*a[24]); + rkc[25] = a[12]*a[17]/(a[2]*a[25]); + rkc[26] = a[4]*a[25]/(a[2]*a[26]); + rkc[27] = a[1]*a[14]*a[14]/(a[2]*a[27]*exp_c0); + rkc[28] = a[4]*a[27]/(a[2]*a[28]); + rkc[29] = a[10]*a[15]/(a[2]*a[28]); + rkc[30] = a[2]*a[15]/(a[3]*a[14]); + rkc[31] = a[6]*a[16]/(a[3]*a[17]); + rkc[32] = a[6]*exp_c0/(a[1]*a[3]); + rkc[33] = a[3]*a[6]*exp_c0/(a[1]*a[3]*a[3]); + rkc[34] = a[5]*a[6]*exp_c0/(a[1]*a[3]*a[5]); + rkc[35] = a[6]*a[47]*exp_c0/(a[1]*a[3]*a[47]); + rkc[36] = a[6]*a[48]*exp_c0/(a[1]*a[3]*a[48]); + rkc[37] = a[2]*a[4]/(a[1]*a[3]); + rkc[38] = a[0]*exp_c0/(a[1]*a[1]); + rkc[39] = a[0]*a[0]*exp_c0/(a[0]*a[1]*a[1]); + rkc[40] = a[0]*a[5]*exp_c0/(a[1]*a[1]*a[5]); + rkc[41] = a[0]*a[15]*exp_c0/(a[1]*a[1]*a[15]); + rkc[42] = a[5]*exp_c0/(a[1]*a[4]); + rkc[43] = a[2]*a[5]/(a[1]*a[6]); + rkc[44] = a[0]*a[3]/(a[1]*a[6]); + rkc[45] = a[4]*a[4]/(a[1]*a[6]); + rkc[46] = a[0]*a[6]/(a[1]*a[7]); + rkc[47] = a[4]*a[5]/(a[1]*a[7]); + rkc[48] = a[0]*a[8]/(a[1]*a[9]); + rkc[49] = a[12]*exp_c0/(a[1]*a[10]); + rkc[50] = a[0]*a[9]/(a[1]*a[11]); + rkc[51] = a[13]*exp_c0/(a[1]*a[12]); + rkc[52] = a[0]*a[12]/(a[1]*a[13]); + rkc[53] = a[17]*exp_c0/(a[1]*a[16]); + rkc[54] = a[0]*a[14]/(a[1]*a[16]); + rkc[55] = a[18]*exp_c0/(a[1]*a[17]); + rkc[56] = a[19]*exp_c0/(a[1]*a[17]); + rkc[57] = a[0]*a[16]/(a[1]*a[17]); + rkc[58] = a[20]*exp_c0/(a[1]*a[18]); + rkc[59] = a[0]*a[17]/(a[1]*a[18]); + rkc[60] = a[4]*a[12]/(a[1]*a[18]); + rkc[61] = a[5]*a[11]/(a[1]*a[18]); + rkc[62] = a[20]*exp_c0/(a[1]*a[19]); + rkc[63] = a[1]*a[18]/(a[1]*a[19]); + rkc[64] = a[0]*a[17]/(a[1]*a[19]); + rkc[65] = a[4]*a[12]/(a[1]*a[19]); + rkc[66] = a[5]*a[11]/(a[1]*a[19]); + rkc[67] = a[0]*a[18]/(a[1]*a[20]); + rkc[68] = a[0]*a[19]/(a[1]*a[20]); + rkc[69] = a[22]*exp_c0/(a[1]*a[21]); + rkc[70] = a[23]*exp_c0/(a[1]*a[22]); + rkc[71] = a[24]*exp_c0/(a[1]*a[23]); + rkc[72] = a[0]*a[22]/(a[1]*a[23]); + rkc[73] = a[25]*exp_c0/(a[1]*a[24]); + rkc[74] = a[0]*a[23]/(a[1]*a[24]); + rkc[75] = a[26]*exp_c0/(a[1]*a[25]); + rkc[76] = a[0]*a[24]/(a[1]*a[25]); + rkc[77] = a[0]*a[25]/(a[1]*a[26]); + rkc[78] = a[11]*a[14]/(a[1]*a[27]); + rkc[79] = a[0]*a[27]/(a[1]*a[28]); + rkc[80] = a[12]*a[14]/(a[1]*a[28]); + rkc[81] = a[1]*a[28]/(a[1]*a[29]); + rkc[82] = a[17]*exp_c0/(a[0]*a[14]); + rkc[83] = a[1]*a[5]/(a[0]*a[4]); + rkc[84] = a[7]*exp_c0/(a[4]*a[4]); + rkc[85] = a[2]*a[5]/(a[4]*a[4]); + rkc[86] = a[3]*a[5]/(a[4]*a[6]); + rkc[87] = a[5]*a[6]/(a[4]*a[7]); + rkc[88] = a[5]*a[6]/(a[4]*a[7]); + rkc[89] = a[1]*a[14]/(a[4]*a[8]); + rkc[90] = a[1]*a[16]/(a[4]*a[9]); + rkc[91] = a[1]*a[17]/(a[4]*a[10]); + rkc[92] = a[5]*a[9]/(a[4]*a[10]); + rkc[93] = a[1]*a[17]/(a[4]*a[11]); + rkc[94] = a[20]*exp_c0/(a[4]*a[12]); + rkc[95] = a[5]*a[10]/(a[4]*a[12]); + rkc[96] = a[5]*a[11]/(a[4]*a[12]); + rkc[97] = a[5]*a[12]/(a[4]*a[13]); + rkc[98] = a[1]*a[15]/(a[4]*a[14]); + rkc[99] = a[5]*a[14]/(a[4]*a[16]); + rkc[100] = a[5]*a[16]/(a[4]*a[17]); + rkc[101] = a[5]*a[17]/(a[4]*a[18]); + rkc[102] = a[5]*a[17]/(a[4]*a[19]); + rkc[103] = a[5]*a[18]/(a[4]*a[20]); + rkc[104] = a[5]*a[19]/(a[4]*a[20]); + rkc[105] = a[1]*a[27]/(a[4]*a[21]); + rkc[106] = a[1]*a[28]/(a[4]*a[22]); + rkc[107] = a[1]*a[29]/(a[4]*a[22]); + rkc[108] = a[5]*a[21]/(a[4]*a[22]); + rkc[109] = a[12]*a[14]/(a[4]*a[22]); + rkc[110] = a[5]*a[22]/(a[4]*a[23]); + rkc[111] = a[5]*a[23]/(a[4]*a[24]); + rkc[112] = a[5]*a[25]/(a[4]*a[26]); + rkc[113] = a[5]*a[27]/(a[4]*a[28]); + rkc[114] = a[3]*a[7]/(a[6]*a[6]); + rkc[115] = a[3]*a[7]/(a[6]*a[6]); + rkc[116] = a[4]*a[17]/(a[6]*a[10]); + rkc[117] = a[3]*a[13]/(a[6]*a[12]); + rkc[118] = a[4]*a[19]/(a[6]*a[12]); + rkc[119] = a[4]*a[15]/(a[6]*a[14]); + rkc[120] = a[7]*a[16]/(a[6]*a[17]); + rkc[121] = a[2]*a[14]/(a[3]*a[8]); + rkc[122] = a[1]*a[21]/(a[8]*a[10]); + rkc[123] = a[1]*a[22]/(a[8]*a[12]); + rkc[124] = a[2]*a[16]/(a[3]*a[9]); + rkc[125] = a[1]*a[10]/(a[0]*a[9]); + rkc[126] = a[1]*a[17]/(a[5]*a[9]); + rkc[127] = a[1]*a[22]/(a[9]*a[10]); + rkc[128] = a[1]*a[23]/(a[9]*a[12]); + rkc[129] = a[1]*a[24]/(a[9]*a[13]); + rkc[130] = a[27]*exp_c0/(a[9]*a[14]); + rkc[131] = a[14]*a[16]/(a[9]*a[15]); + rkc[132] = a[1]*a[28]/(a[9]*a[17]); + rkc[133] = a[14]*a[22]/(a[9]*a[27]); + rkc[135] = a[1]*a[12]/(a[0]*a[10]); + rkc[136] = a[0]*a[22]/(a[10]*a[10]); + rkc[137] = a[1]*a[24]/(a[10]*a[12]); + rkc[138] = a[12]*a[12]/(a[10]*a[13]); + rkc[139] = a[28]*exp_c0/(a[10]*a[14]); + rkc[140] = a[14]*a[23]/(a[10]*a[27]); + rkc[141] = a[10]*a[47]/(a[11]*a[47]); + rkc[142] = a[10]*a[48]/(a[11]*a[48]); + rkc[143] = a[1]*a[4]*a[14]/(a[3]*a[11]*exp_c0); + rkc[144] = a[5]*a[14]/(a[3]*a[11]); + rkc[145] = a[1]*a[12]/(a[0]*a[11]); + rkc[146] = a[20]*exp_c0/(a[5]*a[11]); + rkc[147] = a[5]*a[10]/(a[5]*a[11]); + rkc[148] = a[1]*a[24]/(a[11]*a[12]); + rkc[149] = a[12]*a[12]/(a[11]*a[13]); + rkc[150] = a[10]*a[14]/(a[11]*a[14]); + rkc[151] = a[10]*a[15]/(a[11]*a[15]); + rkc[152] = a[14]*a[17]/(a[11]*a[15]); + rkc[153] = a[12]*a[25]/(a[11]*a[26]); + rkc[154] = a[2]*a[19]/(a[3]*a[12]); + rkc[155] = a[4]*a[17]/(a[3]*a[12]); + rkc[156] = a[6]*a[13]/(a[7]*a[12]); + rkc[157] = a[26]*exp_c0/(a[12]*a[12]); + rkc[158] = a[1]*a[25]/(a[12]*a[12]); + rkc[159] = a[13]*a[14]/(a[12]*a[16]); + rkc[160] = a[13]*a[16]/(a[12]*a[17]); + rkc[161] = a[13]*a[18]/(a[12]*a[20]); + rkc[162] = a[13]*a[19]/(a[12]*a[20]); + rkc[163] = a[13]*a[23]/(a[12]*a[24]); + rkc[164] = a[13]*a[25]/(a[12]*a[26]); + rkc[165] = a[1]*a[5]*a[14]/(a[5]*a[16]*exp_c0); + rkc[166] = a[1]*a[14]/(a[16]*exp_c0); + rkc[167] = a[6]*a[14]/(a[3]*a[16]); + rkc[168] = a[6]*a[17]/(a[3]*a[18]); + rkc[169] = a[6]*a[17]/(a[3]*a[19]); + rkc[170] = a[14]*a[16]/(a[3]*a[21]); + rkc[171] = a[1]*a[22]/(a[0]*a[21]); + rkc[172] = a[16]*a[17]/(a[3]*a[23]); + rkc[173] = a[0]*a[22]/(a[24]*exp_c0); + rkc[174] = a[6]*a[24]/(a[3]*a[25]); + rkc[175] = a[4]*a[14]*a[14]/(a[3]*a[27]*exp_c0); + rkc[176] = a[14]*a[14]*a[22]/(a[27]*a[27]*exp_c0); + rkc[177] = a[2]*a[47]/(a[30]*a[35]); + rkc[178] = a[2]*a[35]/(a[3]*a[30]); + rkc[179] = a[1]*a[35]/(a[4]*a[30]); + rkc[180] = a[3]*a[47]/(a[2]*a[37]); + rkc[181] = a[35]*a[35]/(a[2]*a[37]); + rkc[182] = a[4]*a[47]/(a[1]*a[37]); + rkc[183] = a[6]*a[47]/(a[4]*a[37]); + rkc[184] = a[2]*a[47]/(a[37]*exp_c0); + rkc[185] = a[4]*a[36]/(a[6]*a[35]); + rkc[186] = a[36]*exp_c0/(a[2]*a[35]); + rkc[187] = a[3]*a[35]/(a[2]*a[36]); + rkc[188] = a[4]*a[35]/(a[1]*a[36]); + rkc[189] = a[1]*a[35]/(a[2]*a[31]); + rkc[190] = a[0]*a[30]/(a[1]*a[31]); + rkc[191] = a[1]*a[38]/(a[4]*a[31]); + rkc[192] = a[5]*a[30]/(a[4]*a[31]); + rkc[193] = a[2]*a[38]/(a[3]*a[31]); + rkc[194] = a[4]*a[35]/(a[3]*a[31]); + rkc[195] = a[1]*a[47]/(a[30]*a[31]); + rkc[196] = a[0]*a[38]/(a[5]*a[31]); + rkc[197] = a[4]*a[47]/(a[31]*a[35]); + rkc[198] = a[1]*a[37]/(a[31]*a[35]); + rkc[199] = a[4]*a[31]/(a[2]*a[32]); + rkc[200] = a[1]*a[38]/(a[2]*a[32]); + rkc[201] = a[0]*a[31]/(a[1]*a[32]); + rkc[202] = a[5]*a[31]/(a[4]*a[32]); + rkc[203] = a[1]*a[47]/(a[34]*exp_c0); + rkc[204] = a[1]*a[47]/(a[34]*exp_c0); + rkc[205] = a[6]*a[47]/(a[3]*a[34]); + rkc[206] = a[4]*a[47]/(a[2]*a[34]); + rkc[207] = a[31]*a[35]/(a[2]*a[34]); + rkc[208] = a[0]*a[47]/(a[1]*a[34]); + rkc[209] = a[5]*a[47]/(a[4]*a[34]); + rkc[210] = a[13]*a[47]/(a[12]*a[34]); + rkc[211] = a[38]*exp_c0/(a[1]*a[35]); + rkc[212] = a[4]*a[35]/(a[2]*a[38]); + rkc[213] = a[0]*a[35]/(a[1]*a[38]); + rkc[214] = a[5]*a[35]/(a[4]*a[38]); + rkc[215] = a[6]*a[35]/(a[3]*a[38]); + rkc[216] = a[14]*a[30]/(a[2]*a[39]); + rkc[217] = a[1]*a[46]/(a[4]*a[39]); + rkc[218] = a[4]*a[40]/(a[5]*a[39]); + rkc[219] = a[2]*a[46]/(a[3]*a[39]); + rkc[220] = a[1]*a[40]/(a[0]*a[39]); + rkc[221] = a[14]*a[35]/(a[2]*a[46]); + rkc[222] = a[14]*a[31]/(a[1]*a[46]); + rkc[223] = a[1]*a[14]*a[35]/(a[4]*a[46]*exp_c0); + rkc[224] = a[14]*a[47]/(a[30]*a[46]); + rkc[225] = a[15]*a[35]/(a[3]*a[46]); + rkc[226] = a[14]*a[30]/(a[46]*exp_c0); + rkc[227] = a[14]*a[37]/(a[35]*a[46]); + rkc[228] = a[15]*a[47]/(a[35]*a[46]); + rkc[229] = a[1]*a[39]/(a[40]*exp_c0); + rkc[230] = a[1]*a[46]/(a[2]*a[40]); + rkc[231] = a[14]*a[31]/(a[2]*a[40]); + rkc[232] = a[4]*a[39]/(a[2]*a[40]); + rkc[233] = a[1]*a[44]/(a[4]*a[40]); + rkc[234] = a[1]*a[45]/(a[4]*a[40]); + rkc[235] = a[14]*a[32]/(a[4]*a[40]); + rkc[236] = a[41]*exp_c0/(a[1]*a[40]); + rkc[237] = a[10]*a[47]/(a[30]*a[41]); + rkc[238] = a[30]*a[39]/(a[8]*a[47]); + rkc[239] = a[30]*a[40]/(a[9]*a[47]); + rkc[240] = a[42]*exp_c0/(a[9]*a[47]); + rkc[241] = a[31]*a[40]/(a[10]*a[47]); + rkc[242] = a[31]*a[40]/(a[11]*a[47]); + rkc[243] = a[2]*a[39]/(a[8]*a[35]); + rkc[244] = a[14]*a[30]/(a[8]*a[35]); + rkc[245] = a[2]*a[40]/(a[9]*a[35]); + rkc[246] = a[1]*a[46]/(a[9]*a[35]); + rkc[247] = a[16]*a[30]/(a[9]*a[35]); + rkc[248] = a[1]*a[45]/(a[10]*a[35]); + rkc[249] = a[4]*a[40]/(a[10]*a[35]); + rkc[250] = a[1]*a[43]/(a[10]*a[35]); + rkc[251] = a[1]*a[45]/(a[11]*a[35]); + rkc[252] = a[4]*a[40]/(a[11]*a[35]); + rkc[253] = a[1]*a[43]/(a[11]*a[35]); + rkc[254] = a[5]*a[40]/(a[12]*a[35]); + rkc[255] = a[4]*a[41]/(a[12]*a[35]); + rkc[256] = a[1]*a[14]*a[47]/(a[2]*a[42]*exp_c0); + rkc[257] = a[35]*a[40]/(a[2]*a[42]); + rkc[258] = a[2]*a[16]*a[47]/(a[3]*a[42]*exp_c0); + rkc[259] = a[1]*a[16]*a[47]/(a[4]*a[42]*exp_c0); + rkc[260] = a[10]*a[47]/(a[1]*a[42]); + rkc[261] = a[15]*a[31]/(a[2]*a[45]); + rkc[262] = a[14]*a[38]/(a[2]*a[45]); + rkc[263] = a[4]*a[46]/(a[2]*a[45]); + rkc[264] = a[14]*a[32]/(a[1]*a[45]); + rkc[265] = a[0]*a[46]/(a[1]*a[45]); + rkc[266] = a[5]*a[46]/(a[4]*a[45]); + rkc[267] = a[15]*a[32]/(a[4]*a[45]); + rkc[268] = a[14]*a[31]/(a[45]*exp_c0); + rkc[269] = a[1]*a[45]/(a[1]*a[43]); + rkc[270] = a[4]*a[40]/(a[1]*a[43]); + rkc[271] = a[14]*a[32]/(a[1]*a[43]); + rkc[272] = a[1]*a[45]/(a[1]*a[44]); + rkc[273] = a[14]*a[43]/(a[27]*a[35]); + rkc[274] = a[1]*a[41]/(a[12]*a[30]); + rkc[275] = a[0]*a[40]/(a[12]*a[30]); + rkc[276] = a[0]*a[32]/(a[1]*a[33]); + rkc[277] = a[5]*a[32]/(a[4]*a[33]); + rkc[278] = a[4]*a[32]/(a[2]*a[33]); + rkc[279] = a[14]*a[38]/(a[15]*a[31]); + rkc[280] = a[35]*a[46]/(a[36]*a[39]); + rkc[281] = a[15]*a[37]/(a[36]*a[46]); + rkc[282] = a[14]*a[35]/(a[15]*a[30]); + rkc[284] = a[1]*a[51]/(a[2]*a[24]); + rkc[285] = a[1]*a[52]/(a[2]*a[25]); + rkc[286] = a[3]*a[5]/(a[4]*a[6]); + rkc[288] = a[12]*exp_c0/(a[0]*a[9]); + rkc[290] = a[2]*a[17]/(a[3]*a[10]); + rkc[293] = a[2]*a[51]/(a[3]*a[23]); + rkc[294] = a[6]*a[22]/(a[3]*a[23]); + rkc[295] = a[4]*a[51]/(a[2]*a[52]); + rkc[298] = a[0]*a[51]/(a[1]*a[52]); + rkc[303] = a[51]*exp_c0/(a[1]*a[28]); + rkc[307] = a[12]*a[16]/(a[1]*a[51]); + rkc[308] = a[0]*a[28]/(a[1]*a[51]); + rkc[309] = a[5]*a[28]/(a[4]*a[51]); + rkc[310] = a[16]*a[18]/(a[4]*a[51]); + rkc[311] = a[50]*exp_c0/(a[12]*a[25]); + rkc[312] = a[4]*a[49]/(a[2]*a[50]); + rkc[313] = a[0]*a[49]/(a[1]*a[50]); + rkc[314] = a[5]*a[49]/(a[4]*a[50]); + rkc[315] = a[6]*a[50]/(a[7]*a[49]); + rkc[316] = a[13]*a[49]/(a[12]*a[50]); + rkc[317] = a[49]*exp_c0/(a[12]*a[24]); + rkc[318] = a[17]*a[25]/(a[2]*a[49]); + rkc[319] = a[50]*exp_c0/(a[1]*a[49]); + rkc[320] = a[12]*a[25]/(a[1]*a[49]); + rkc[321] = a[18]*a[25]/(a[4]*a[49]); + rkc[322] = a[3]*a[50]/(a[6]*a[49]); + rkc[324] = a[25]*a[25]/(a[12]*a[49]); + } + + + void GRI_30_Kinetics::get_wdot(const double* rop, double* wdot) { + wdot[0] = - rop[2] + rop[7] + rop[38] + rop[39] + rop[40] + rop[41] + rop[44] + rop[46] + rop[48] + rop[50] + rop[52] + rop[54] + rop[57] + rop[59] + rop[64] + rop[67] + rop[68] + rop[72] + rop[74] + rop[76] + rop[77] + rop[79] - rop[82] - rop[83] - rop[125] - rop[135] + rop[136] - rop[145] - rop[171] + rop[173] + rop[190] + rop[196] + rop[201] + rop[208] + rop[213] - rop[220] + rop[265] + rop[275] + rop[276] + rop[283] + rop[287] - rop[288] + rop[292] + rop[298] + rop[299] + rop[308] + rop[313]; + wdot[1] = - rop[1] + rop[2] + rop[5] + rop[6] + rop[8] + rop[9] + rop[13] + rop[20] + rop[23] + rop[27] - rop[32] - rop[33] - rop[34] - rop[35] - rop[36] - rop[37] - 2*rop[38] - 2*rop[39] - 2*rop[40] - 2*rop[41] - rop[42] - rop[43] - rop[44] - rop[45] - rop[46] - rop[47] - rop[48] - rop[49] - rop[50] - rop[51] - rop[52] - rop[53] - rop[54] - rop[55] - rop[56] - rop[57] - rop[58] - rop[59] - rop[60] - rop[61] - rop[62] - rop[64] - rop[65] - rop[66] - rop[67] - rop[68] - rop[69] - rop[70] - rop[71] - rop[72] - rop[73] - rop[74] - rop[75] - rop[76] - rop[77] - rop[78] - rop[79] - rop[80] + rop[83] + rop[89] + rop[90] + rop[91] + rop[93] + rop[98] + rop[105] + rop[106] + rop[107] + rop[122] + rop[123] + rop[125] + rop[126] + rop[127] + rop[128] + rop[129] + rop[132] + rop[134] + rop[135] + rop[137] + rop[143] + rop[145] + rop[148] + rop[158] + rop[165] + rop[166] + rop[171] + rop[179] - rop[182] - rop[188] + rop[189] - rop[190] + rop[191] + rop[195] + rop[198] + rop[200] - rop[201] + rop[203] + rop[204] - rop[208] - rop[211] - rop[213] + rop[217] + rop[220] - rop[222] + rop[223] + rop[229] + rop[230] + rop[233] + rop[234] - rop[236] + rop[246] + rop[248] + rop[250] + rop[251] + rop[253] + rop[256] + rop[259] - rop[260] - rop[264] - rop[265] - rop[270] - rop[271] + rop[274] - rop[276] + rop[283] + rop[284] + rop[285] + 2*rop[289] + 2*rop[291] - rop[298] - rop[299] - rop[303] + rop[304] - rop[307] - rop[308] - rop[313] - rop[319] - rop[320]; + wdot[2] = - 2*rop[0] - rop[1] - rop[2] - rop[3] - rop[4] - rop[5] - rop[6] - rop[7] - rop[8] - rop[9] - rop[10] - rop[11] - rop[12] - rop[13] - rop[14] - rop[15] - rop[16] - rop[17] - rop[18] - rop[19] - rop[20] - rop[21] - rop[22] - rop[23] - rop[24] - rop[25] - rop[26] - rop[27] - rop[28] - rop[29] + rop[30] + rop[37] + rop[43] + rop[85] + rop[121] + rop[124] + rop[154] + rop[177] + rop[178] - rop[180] - rop[181] + rop[184] - rop[186] - rop[187] - rop[189] + rop[193] - rop[199] - rop[200] - rop[206] - rop[207] - rop[212] - rop[216] + rop[219] - rop[221] - rop[230] - rop[231] - rop[232] + rop[243] + rop[245] - rop[256] - rop[257] + rop[258] - rop[261] - rop[262] - rop[263] - rop[278] - rop[283] - rop[284] - rop[285] + rop[290] + rop[293] - rop[295] - rop[296] - rop[304] - rop[312] - rop[318]; + wdot[3] = + rop[0] + rop[3] - rop[30] - rop[31] - rop[32] - rop[33] - rop[34] - rop[35] - rop[36] - rop[37] + rop[44] + rop[86] + rop[114] + rop[115] + rop[117] - rop[121] - rop[124] - rop[134] - rop[143] - rop[144] - rop[154] - rop[155] - rop[167] - rop[168] - rop[169] - rop[170] - rop[172] - rop[174] - rop[175] - rop[178] + rop[180] + rop[187] - rop[193] - rop[194] - rop[205] - rop[215] - rop[219] - rop[225] - rop[258] + rop[286] - rop[289] - rop[290] - rop[293] - rop[294] - rop[297] - rop[305] - rop[306] + rop[322]; + wdot[4] = + rop[1] + rop[2] + rop[3] + rop[4] + rop[10] + rop[12] + rop[14] + rop[15] + rop[16] + rop[17] + rop[18] + rop[21] + rop[26] + rop[28] + rop[37] - rop[42] + 2*rop[45] + rop[47] + rop[60] + rop[65] - rop[83] - 2*rop[84] - 2*rop[85] - rop[86] - rop[87] - rop[88] - rop[89] - rop[90] - rop[91] - rop[92] - rop[93] - rop[94] - rop[95] - rop[96] - rop[97] - rop[98] - rop[99] - rop[100] - rop[101] - rop[102] - rop[103] - rop[104] - rop[105] - rop[106] - rop[107] - rop[108] - rop[109] - rop[110] - rop[111] - rop[112] - rop[113] + rop[116] + rop[118] + rop[119] + rop[134] + rop[143] + rop[155] + rop[175] - rop[179] + rop[182] - rop[183] + rop[185] + rop[188] - rop[191] - rop[192] + rop[194] + rop[197] + rop[199] - rop[202] + rop[206] - rop[209] + rop[212] - rop[214] - rop[217] + rop[218] - rop[223] + rop[232] - rop[233] - rop[234] - rop[235] + rop[249] + rop[252] + rop[255] - rop[259] + rop[263] - rop[266] - rop[267] + rop[270] - rop[277] + rop[278] - rop[286] - rop[287] + rop[295] + rop[296] - rop[300] + rop[305] + rop[306] - rop[309] - rop[310] + rop[312] - rop[314] - rop[321] + rop[323]; + wdot[5] = + rop[42] + rop[43] + rop[47] + rop[61] + rop[66] + rop[83] + rop[85] + rop[86] + rop[87] + rop[88] + rop[92] + rop[95] + rop[96] + rop[97] + rop[99] + rop[100] + rop[101] + rop[102] + rop[103] + rop[104] + rop[108] + rop[110] + rop[111] + rop[112] + rop[113] - rop[126] + rop[144] - rop[146] + rop[192] - rop[196] + rop[202] + rop[209] + rop[214] - rop[218] + rop[254] + rop[266] + rop[277] + rop[286] - rop[292] + rop[300] + rop[309] + rop[314]; + wdot[6] = - rop[3] + rop[4] + rop[31] + rop[32] + rop[33] + rop[34] + rop[35] + rop[36] - rop[43] - rop[44] - rop[45] + rop[46] - rop[86] + rop[87] + rop[88] - 2*rop[114] - 2*rop[115] - rop[116] - rop[117] - rop[118] - rop[119] - rop[120] + rop[156] + rop[167] + rop[168] + rop[169] + rop[174] + rop[183] - rop[185] + rop[205] + rop[215] - rop[286] + rop[294] + rop[297] - rop[301] + rop[315] - rop[322] - rop[323]; + wdot[7] = - rop[4] - rop[46] - rop[47] + rop[84] - rop[87] - rop[88] + rop[114] + rop[115] + rop[120] - rop[156] + rop[301] - rop[315]; + wdot[8] = + rop[48] - rop[89] - rop[121] - rop[122] - rop[123] - rop[238] - rop[243] - rop[244]; + wdot[9] = - rop[5] + rop[19] - rop[48] + rop[50] - rop[90] + rop[92] - rop[124] - rop[125] - rop[126] - rop[127] - rop[128] - rop[129] - rop[130] - rop[131] - rop[132] - rop[133] - rop[239] - rop[240] - rop[245] - rop[246] - rop[247] - rop[288]; + wdot[10] = - rop[6] + rop[22] + rop[29] - rop[49] - rop[91] - rop[92] + rop[95] - rop[116] - rop[122] + rop[125] - rop[127] - rop[134] - rop[135] - 2*rop[136] - rop[137] - rop[138] - rop[139] - rop[140] + rop[141] + rop[142] + rop[147] + rop[150] + rop[151] + rop[237] - rop[241] - rop[248] - rop[249] - rop[250] + rop[260] - rop[289] - rop[290] - 2*rop[291] + rop[304]; + wdot[11] = - rop[7] - rop[8] - rop[50] + rop[61] + rop[66] + rop[78] - rop[93] + rop[96] - rop[141] - rop[142] - rop[143] - rop[144] - rop[145] - rop[146] - rop[147] - rop[148] - rop[149] - rop[150] - rop[151] - rop[152] - rop[153] - rop[242] - rop[251] - rop[252] - rop[253] - rop[292]; + wdot[12] = - rop[9] + rop[10] + rop[24] + rop[25] + rop[49] - rop[51] + rop[52] + rop[60] + rop[65] + rop[80] - rop[94] - rop[95] - rop[96] + rop[97] + rop[109] - rop[117] - rop[118] - rop[123] - rop[128] + rop[135] - rop[137] + 2*rop[138] + rop[145] - rop[148] + 2*rop[149] + rop[153] - rop[154] - rop[155] - rop[156] - 2*rop[157] - 2*rop[158] - rop[159] - rop[160] - rop[161] - rop[162] - rop[163] - rop[164] - rop[210] - rop[254] - rop[255] - rop[274] - rop[275] - rop[283] - rop[287] + rop[288] + rop[296] + rop[297] + rop[299] + rop[300] + rop[301] + rop[307] - rop[311] - rop[316] - rop[317] + rop[320] - rop[324]; + wdot[13] = - rop[10] + rop[51] - rop[52] - rop[97] + rop[117] - rop[129] - rop[138] - rop[149] + rop[156] + rop[159] + rop[160] + rop[161] + rop[162] + rop[163] + rop[164] + rop[210] + rop[302] + rop[316]; + wdot[14] = + rop[5] + rop[7] - rop[11] + rop[12] + rop[19] + rop[22] + 2*rop[27] - rop[30] + rop[54] + rop[78] + rop[80] - rop[82] + rop[89] - rop[98] + rop[99] + rop[109] - rop[119] + rop[121] - rop[130] + rop[131] + rop[133] + rop[134] - rop[139] + rop[140] + rop[143] + rop[144] + rop[152] + rop[159] + rop[165] + rop[166] + rop[167] + rop[170] + 2*rop[175] + 2*rop[176] + rop[216] + rop[221] + rop[222] + rop[223] + rop[224] + rop[226] + rop[227] + rop[231] + rop[235] + rop[244] + rop[256] + rop[262] + rop[264] + rop[268] + rop[271] + rop[273] + rop[279] + rop[282] + rop[283] + rop[296] + rop[297] + rop[299] + rop[300] + rop[301] + rop[302] + rop[305]; + wdot[15] = + rop[11] + rop[13] + rop[29] + rop[30] + rop[98] + rop[119] - rop[131] - rop[152] + rop[225] + rop[228] + rop[261] + rop[267] - rop[279] + rop[281] - rop[282] + rop[289] + rop[304]; + wdot[16] = + rop[6] + rop[8] - rop[12] - rop[13] + rop[14] + rop[24] + rop[31] - rop[53] - rop[54] + rop[57] + rop[90] - rop[99] + rop[100] + rop[120] + rop[124] + rop[131] - rop[159] + rop[160] - rop[165] - rop[166] - rop[167] + rop[170] + rop[172] + rop[247] + rop[258] + rop[259] + 2*rop[306] + rop[307] + rop[310]; + wdot[17] = + rop[9] - rop[14] + rop[15] + rop[16] + rop[25] - rop[31] + rop[53] - rop[55] - rop[56] - rop[57] + rop[59] + rop[64] + rop[82] + rop[91] + rop[93] - rop[100] + rop[101] + rop[102] + rop[116] - rop[120] + rop[126] - rop[132] + rop[152] + rop[155] - rop[160] + rop[168] + rop[169] + rop[172] + rop[287] + rop[290] + rop[292] + rop[305] + rop[318] + rop[323]; + wdot[18] = - rop[15] + rop[17] + rop[55] - rop[58] - rop[59] - rop[60] - rop[61] + rop[63] + rop[67] - rop[101] + rop[103] + rop[161] - rop[168] + rop[310] + rop[321]; + wdot[19] = - rop[16] + rop[18] + rop[56] - rop[62] - rop[63] - rop[64] - rop[65] - rop[66] + rop[68] - rop[102] + rop[104] + rop[118] + rop[154] + rop[162] - rop[169]; + wdot[20] = - rop[17] - rop[18] + rop[58] + rop[62] - rop[67] - rop[68] + rop[94] - rop[103] - rop[104] + rop[146] - rop[161] - rop[162]; + wdot[21] = - rop[19] + rop[21] - rop[69] - rop[105] + rop[108] + rop[122] - rop[170] - rop[171]; + wdot[22] = - rop[20] - rop[21] - rop[22] + rop[69] - rop[70] + rop[72] - rop[106] - rop[107] - rop[108] - rop[109] + rop[110] + rop[123] + rop[127] + rop[133] + rop[136] + rop[171] + rop[173] + rop[176] + rop[291] + rop[294]; + wdot[23] = - rop[23] + rop[70] - rop[71] - rop[72] + rop[74] - rop[110] + rop[111] + rop[128] + rop[140] + rop[163] - rop[172] - rop[293] - rop[294]; + wdot[24] = - rop[24] + rop[71] - rop[73] - rop[74] + rop[76] - rop[111] + rop[129] + rop[137] + rop[148] - rop[163] - rop[173] + rop[174] - rop[284] - rop[317]; + wdot[25] = - rop[25] + rop[26] + rop[73] - rop[75] - rop[76] + rop[77] + rop[112] + rop[153] + rop[158] + rop[164] - rop[174] - rop[285] - rop[311] + rop[318] + rop[320] + rop[321] + rop[323] + 2*rop[324]; + wdot[26] = - rop[26] + rop[75] - rop[77] - rop[112] - rop[153] + rop[157] - rop[164]; + wdot[27] = + rop[20] - rop[27] + rop[28] - rop[78] + rop[79] + rop[105] + rop[113] + rop[130] - rop[133] - rop[140] - rop[175] - 2*rop[176] - rop[273]; + wdot[28] = + rop[23] - rop[28] - rop[29] - rop[79] - rop[80] + rop[81] + rop[106] - rop[113] + rop[132] + rop[139] - rop[303] + rop[308] + rop[309]; + wdot[29] = - rop[81] + rop[107]; + wdot[30] = - rop[177] - rop[178] - rop[179] + rop[190] + rop[192] - rop[195] + rop[216] - rop[224] + rop[226] - rop[237] + rop[238] + rop[239] + rop[244] + rop[247] - rop[274] - rop[275] - rop[282]; + wdot[31] = - rop[189] - rop[190] - rop[191] - rop[192] - rop[193] - rop[194] - rop[195] - rop[196] - rop[197] - rop[198] + rop[199] + rop[201] + rop[202] + rop[207] + rop[222] + rop[231] + rop[241] + rop[242] + rop[261] + rop[268] - rop[279]; + wdot[32] = - rop[199] - rop[200] - rop[201] - rop[202] + rop[235] + rop[264] + rop[267] + rop[271] + rop[276] + rop[277] + rop[278]; + wdot[33] = - rop[276] - rop[277] - rop[278]; + wdot[34] = - rop[203] - rop[204] - rop[205] - rop[206] - rop[207] - rop[208] - rop[209] - rop[210]; + wdot[35] = - rop[177] + rop[178] + rop[179] + 2*rop[181] - rop[185] - rop[186] + rop[187] + rop[188] + rop[189] + rop[194] - rop[197] - rop[198] + rop[207] - rop[211] + rop[212] + rop[213] + rop[214] + rop[215] + rop[221] + rop[223] + rop[225] - rop[227] - rop[228] - rop[243] - rop[244] - rop[245] - rop[246] - rop[247] - rop[248] - rop[249] - rop[250] - rop[251] - rop[252] - rop[253] - rop[254] - rop[255] + rop[257] - rop[273] + rop[280] + rop[282]; + wdot[36] = + rop[185] + rop[186] - rop[187] - rop[188] - rop[280] - rop[281]; + wdot[37] = - rop[180] - rop[181] - rop[182] - rop[183] - rop[184] + rop[198] + rop[227] + rop[281]; + wdot[38] = + rop[191] + rop[193] + rop[196] + rop[200] + rop[211] - rop[212] - rop[213] - rop[214] - rop[215] + rop[262] + rop[279]; + wdot[39] = - rop[216] - rop[217] - rop[218] - rop[219] - rop[220] + rop[229] + rop[232] + rop[238] + rop[243] - rop[280]; + wdot[40] = + rop[218] + rop[220] - rop[229] - rop[230] - rop[231] - rop[232] - rop[233] - rop[234] - rop[235] - rop[236] + rop[239] + rop[241] + rop[242] + rop[245] + rop[249] + rop[252] + rop[254] + rop[257] + rop[270] + rop[275]; + wdot[41] = + rop[236] - rop[237] + rop[255] + rop[274]; + wdot[42] = + rop[240] - rop[256] - rop[257] - rop[258] - rop[259] - rop[260]; + wdot[43] = + rop[250] + rop[253] - rop[269] - rop[270] - rop[271] + rop[273]; + wdot[44] = + rop[233] - rop[272]; + wdot[45] = + rop[234] + rop[248] + rop[251] - rop[261] - rop[262] - rop[263] - rop[264] - rop[265] - rop[266] - rop[267] - rop[268] + rop[269] + rop[272]; + wdot[46] = + rop[217] + rop[219] - rop[221] - rop[222] - rop[223] - rop[224] - rop[225] - rop[226] - rop[227] - rop[228] + rop[230] + rop[246] + rop[263] + rop[265] + rop[266] + rop[280] - rop[281]; + wdot[47] = + rop[177] + rop[180] + rop[182] + rop[183] + rop[184] + rop[195] + rop[197] + rop[203] + rop[204] + rop[205] + rop[206] + rop[208] + rop[209] + rop[210] + rop[224] + rop[228] + rop[237] - rop[238] - rop[239] - rop[240] - rop[241] - rop[242] + rop[256] + rop[258] + rop[259] + rop[260]; + wdot[48] = 0.0; + wdot[49] = + rop[312] + rop[313] + rop[314] - rop[315] + rop[316] + rop[317] - rop[318] - rop[319] - rop[320] - rop[321] - rop[322] - rop[323] - rop[324]; + wdot[50] = + rop[311] - rop[312] - rop[313] - rop[314] + rop[315] - rop[316] + rop[319] + rop[322]; + wdot[51] = + rop[284] + rop[293] + rop[295] + rop[298] + rop[303] - rop[304] - rop[305] - rop[306] - rop[307] - rop[308] - rop[309] - rop[310]; + wdot[52] = + rop[285] - rop[295] - rop[296] - rop[297] - rop[298] - rop[299] - rop[300] - rop[301] - rop[302]; + } + + + void GRI_30_Kinetics::eval_ropnet(const double* c, const double* rf, const double* rkc, double* r) { + r[0] = rf[0] * (c[2] * c[2] - rkc[0] * c[3]); + r[1] = rf[1] * (c[2] * c[1] - rkc[1] * c[4]); + r[2] = rf[2] * (c[2] * c[0] - rkc[2] * c[1] * c[4]); + r[3] = rf[3] * (c[2] * c[6] - rkc[3] * c[4] * c[3]); + r[4] = rf[4] * (c[2] * c[7] - rkc[4] * c[4] * c[6]); + r[5] = rf[5] * (c[2] * c[9] - rkc[5] * c[1] * c[14]); + r[6] = rf[6] * (c[2] * c[10] - rkc[6] * c[1] * c[16]); + r[7] = rf[7] * (c[2] * c[11] - rkc[7] * c[0] * c[14]); + r[8] = rf[8] * (c[2] * c[11] - rkc[8] * c[1] * c[16]); + r[9] = rf[9] * (c[2] * c[12] - rkc[9] * c[1] * c[17]); + r[10] = rf[10] * (c[2] * c[13] - rkc[10] * c[4] * c[12]); + r[11] = rf[11] * (c[2] * c[14] - rkc[11] * c[15]); + r[12] = rf[12] * (c[2] * c[16] - rkc[12] * c[4] * c[14]); + r[13] = rf[13] * (c[2] * c[16] - rkc[13] * c[1] * c[15]); + r[14] = rf[14] * (c[2] * c[17] - rkc[14] * c[4] * c[16]); + r[15] = rf[15] * (c[2] * c[18] - rkc[15] * c[4] * c[17]); + r[16] = rf[16] * (c[2] * c[19] - rkc[16] * c[4] * c[17]); + r[17] = rf[17] * (c[2] * c[20] - rkc[17] * c[4] * c[18]); + r[18] = rf[18] * (c[2] * c[20] - rkc[18] * c[4] * c[19]); + r[19] = rf[19] * (c[2] * c[21] - rkc[19] * c[9] * c[14]); + r[20] = rf[20] * (c[2] * c[22] - rkc[20] * c[1] * c[27]); + r[21] = rf[21] * (c[2] * c[22] - rkc[21] * c[4] * c[21]); + r[22] = rf[22] * (c[2] * c[22] - rkc[22] * c[14] * c[10]); + r[23] = rf[23] * (c[2] * c[23] - rkc[23] * c[1] * c[28]); + r[24] = rf[24] * (c[2] * c[24] - rkc[24] * c[12] * c[16]); + r[25] = rf[25] * (c[2] * c[25] - rkc[25] * c[12] * c[17]); + r[26] = rf[26] * (c[2] * c[26] - rkc[26] * c[4] * c[25]); + r[27] = rf[27] * (c[2] * c[27] - rkc[27] * c[1] * c[14] * c[14]); + r[28] = rf[28] * (c[2] * c[28] - rkc[28] * c[4] * c[27]); + r[29] = rf[29] * (c[2] * c[28] - rkc[29] * c[10] * c[15]); + r[30] = rf[30] * (c[3] * c[14] - rkc[30] * c[2] * c[15]); + r[31] = rf[31] * (c[3] * c[17] - rkc[31] * c[6] * c[16]); + r[32] = rf[32] * (c[1] * c[3] - rkc[32] * c[6]); + r[33] = rf[33] * (c[1] * c[3] * c[3] - rkc[33] * c[6] * c[3]); + r[34] = rf[34] * (c[1] * c[3] * c[5] - rkc[34] * c[6] * c[5]); + r[35] = rf[35] * (c[1] * c[3] * c[47] - rkc[35] * c[6] * c[47]); + r[36] = rf[36] * (c[1] * c[3] * c[48] - rkc[36] * c[6] * c[48]); + r[37] = rf[37] * (c[1] * c[3] - rkc[37] * c[2] * c[4]); + r[38] = rf[38] * (c[1] * c[1] - rkc[38] * c[0]); + r[39] = rf[39] * (c[1] * c[1] * c[0] - rkc[39] * c[0] * c[0]); + r[40] = rf[40] * (c[1] * c[1] * c[5] - rkc[40] * c[0] * c[5]); + r[41] = rf[41] * (c[1] * c[1] * c[15] - rkc[41] * c[0] * c[15]); + r[42] = rf[42] * (c[1] * c[4] - rkc[42] * c[5]); + r[43] = rf[43] * (c[1] * c[6] - rkc[43] * c[2] * c[5]); + r[44] = rf[44] * (c[1] * c[6] - rkc[44] * c[3] * c[0]); + r[45] = rf[45] * (c[1] * c[6] - rkc[45] * c[4] * c[4]); + r[46] = rf[46] * (c[1] * c[7] - rkc[46] * c[6] * c[0]); + r[47] = rf[47] * (c[1] * c[7] - rkc[47] * c[4] * c[5]); + r[48] = rf[48] * (c[1] * c[9] - rkc[48] * c[8] * c[0]); + r[49] = rf[49] * (c[1] * c[10] - rkc[49] * c[12]); + r[50] = rf[50] * (c[1] * c[11] - rkc[50] * c[9] * c[0]); + r[51] = rf[51] * (c[1] * c[12] - rkc[51] * c[13]); + r[52] = rf[52] * (c[1] * c[13] - rkc[52] * c[12] * c[0]); + r[53] = rf[53] * (c[1] * c[16] - rkc[53] * c[17]); + r[54] = rf[54] * (c[1] * c[16] - rkc[54] * c[0] * c[14]); + r[55] = rf[55] * (c[1] * c[17] - rkc[55] * c[18]); + r[56] = rf[56] * (c[1] * c[17] - rkc[56] * c[19]); + r[57] = rf[57] * (c[1] * c[17] - rkc[57] * c[16] * c[0]); + r[58] = rf[58] * (c[1] * c[18] - rkc[58] * c[20]); + r[59] = rf[59] * (c[1] * c[18] - rkc[59] * c[0] * c[17]); + r[60] = rf[60] * (c[1] * c[18] - rkc[60] * c[4] * c[12]); + r[61] = rf[61] * (c[1] * c[18] - rkc[61] * c[11] * c[5]); + r[62] = rf[62] * (c[1] * c[19] - rkc[62] * c[20]); + r[63] = rf[63] * (c[1] * c[19] - rkc[63] * c[1] * c[18]); + r[64] = rf[64] * (c[1] * c[19] - rkc[64] * c[0] * c[17]); + r[65] = rf[65] * (c[1] * c[19] - rkc[65] * c[4] * c[12]); + r[66] = rf[66] * (c[1] * c[19] - rkc[66] * c[11] * c[5]); + r[67] = rf[67] * (c[1] * c[20] - rkc[67] * c[18] * c[0]); + r[68] = rf[68] * (c[1] * c[20] - rkc[68] * c[19] * c[0]); + r[69] = rf[69] * (c[1] * c[21] - rkc[69] * c[22]); + r[70] = rf[70] * (c[1] * c[22] - rkc[70] * c[23]); + r[71] = rf[71] * (c[1] * c[23] - rkc[71] * c[24]); + r[72] = rf[72] * (c[1] * c[23] - rkc[72] * c[0] * c[22]); + r[73] = rf[73] * (c[1] * c[24] - rkc[73] * c[25]); + r[74] = rf[74] * (c[1] * c[24] - rkc[74] * c[23] * c[0]); + r[75] = rf[75] * (c[1] * c[25] - rkc[75] * c[26]); + r[76] = rf[76] * (c[1] * c[25] - rkc[76] * c[0] * c[24]); + r[77] = rf[77] * (c[1] * c[26] - rkc[77] * c[25] * c[0]); + r[78] = rf[78] * (c[1] * c[27] - rkc[78] * c[11] * c[14]); + r[79] = rf[79] * (c[1] * c[28] - rkc[79] * c[27] * c[0]); + r[80] = rf[80] * (c[1] * c[28] - rkc[80] * c[12] * c[14]); + r[81] = rf[81] * (c[1] * c[29] - rkc[81] * c[1] * c[28]); + r[82] = rf[82] * (c[0] * c[14] - rkc[82] * c[17]); + r[83] = rf[83] * (c[4] * c[0] - rkc[83] * c[1] * c[5]); + r[84] = rf[84] * (c[4] * c[4] - rkc[84] * c[7]); + r[85] = rf[85] * (c[4] * c[4] - rkc[85] * c[2] * c[5]); + r[86] = rf[86] * (c[4] * c[6] - rkc[86] * c[3] * c[5]); + r[87] = rf[87] * (c[4] * c[7] - rkc[87] * c[6] * c[5]); + r[88] = rf[88] * (c[4] * c[7] - rkc[88] * c[6] * c[5]); + r[89] = rf[89] * (c[4] * c[8] - rkc[89] * c[1] * c[14]); + r[90] = rf[90] * (c[4] * c[9] - rkc[90] * c[1] * c[16]); + r[91] = rf[91] * (c[4] * c[10] - rkc[91] * c[1] * c[17]); + r[92] = rf[92] * (c[4] * c[10] - rkc[92] * c[9] * c[5]); + r[93] = rf[93] * (c[4] * c[11] - rkc[93] * c[1] * c[17]); + r[94] = rf[94] * (c[4] * c[12] - rkc[94] * c[20]); + r[95] = rf[95] * (c[4] * c[12] - rkc[95] * c[10] * c[5]); + r[96] = rf[96] * (c[4] * c[12] - rkc[96] * c[11] * c[5]); + r[97] = rf[97] * (c[4] * c[13] - rkc[97] * c[12] * c[5]); + r[98] = rf[98] * (c[4] * c[14] - rkc[98] * c[1] * c[15]); + r[99] = rf[99] * (c[4] * c[16] - rkc[99] * c[5] * c[14]); + r[100] = rf[100] * (c[4] * c[17] - rkc[100] * c[16] * c[5]); + r[101] = rf[101] * (c[4] * c[18] - rkc[101] * c[5] * c[17]); + r[102] = rf[102] * (c[4] * c[19] - rkc[102] * c[5] * c[17]); + r[103] = rf[103] * (c[4] * c[20] - rkc[103] * c[18] * c[5]); + r[104] = rf[104] * (c[4] * c[20] - rkc[104] * c[19] * c[5]); + r[105] = rf[105] * (c[4] * c[21] - rkc[105] * c[1] * c[27]); + r[106] = rf[106] * (c[4] * c[22] - rkc[106] * c[1] * c[28]); + r[107] = rf[107] * (c[4] * c[22] - rkc[107] * c[1] * c[29]); + r[108] = rf[108] * (c[4] * c[22] - rkc[108] * c[21] * c[5]); + r[109] = rf[109] * (c[4] * c[22] - rkc[109] * c[12] * c[14]); + r[110] = rf[110] * (c[4] * c[23] - rkc[110] * c[5] * c[22]); + r[111] = rf[111] * (c[4] * c[24] - rkc[111] * c[23] * c[5]); + r[112] = rf[112] * (c[4] * c[26] - rkc[112] * c[25] * c[5]); + r[113] = rf[113] * (c[4] * c[28] - rkc[113] * c[27] * c[5]); + r[114] = rf[114] * (c[6] * c[6] - rkc[114] * c[3] * c[7]); + r[115] = rf[115] * (c[6] * c[6] - rkc[115] * c[3] * c[7]); + r[116] = rf[116] * (c[6] * c[10] - rkc[116] * c[4] * c[17]); + r[117] = rf[117] * (c[6] * c[12] - rkc[117] * c[3] * c[13]); + r[118] = rf[118] * (c[6] * c[12] - rkc[118] * c[4] * c[19]); + r[119] = rf[119] * (c[6] * c[14] - rkc[119] * c[4] * c[15]); + r[120] = rf[120] * (c[6] * c[17] - rkc[120] * c[16] * c[7]); + r[121] = rf[121] * (c[8] * c[3] - rkc[121] * c[2] * c[14]); + r[122] = rf[122] * (c[8] * c[10] - rkc[122] * c[1] * c[21]); + r[123] = rf[123] * (c[8] * c[12] - rkc[123] * c[1] * c[22]); + r[124] = rf[124] * (c[9] * c[3] - rkc[124] * c[2] * c[16]); + r[125] = rf[125] * (c[9] * c[0] - rkc[125] * c[1] * c[10]); + r[126] = rf[126] * (c[9] * c[5] - rkc[126] * c[1] * c[17]); + r[127] = rf[127] * (c[9] * c[10] - rkc[127] * c[1] * c[22]); + r[128] = rf[128] * (c[9] * c[12] - rkc[128] * c[1] * c[23]); + r[129] = rf[129] * (c[9] * c[13] - rkc[129] * c[1] * c[24]); + r[130] = rf[130] * (c[9] * c[14] - rkc[130] * c[27]); + r[131] = rf[131] * (c[9] * c[15] - rkc[131] * c[16] * c[14]); + r[132] = rf[132] * (c[9] * c[17] - rkc[132] * c[1] * c[28]); + r[133] = rf[133] * (c[9] * c[27] - rkc[133] * c[14] * c[22]); + r[134] = rf[134] * (c[10] * c[3]); + r[135] = rf[135] * (c[10] * c[0] - rkc[135] * c[1] * c[12]); + r[136] = rf[136] * (c[10] * c[10] - rkc[136] * c[0] * c[22]); + r[137] = rf[137] * (c[10] * c[12] - rkc[137] * c[1] * c[24]); + r[138] = rf[138] * (c[10] * c[13] - rkc[138] * c[12] * c[12]); + r[139] = rf[139] * (c[10] * c[14] - rkc[139] * c[28]); + r[140] = rf[140] * (c[10] * c[27] - rkc[140] * c[23] * c[14]); + r[141] = rf[141] * (c[11] * c[47] - rkc[141] * c[10] * c[47]); + r[142] = rf[142] * (c[11] * c[48] - rkc[142] * c[10] * c[48]); + r[143] = rf[143] * (c[11] * c[3] - rkc[143] * c[1] * c[4] * c[14]); + r[144] = rf[144] * (c[11] * c[3] - rkc[144] * c[14] * c[5]); + r[145] = rf[145] * (c[11] * c[0] - rkc[145] * c[12] * c[1]); + r[146] = rf[146] * (c[11] * c[5] - rkc[146] * c[20]); + r[147] = rf[147] * (c[11] * c[5] - rkc[147] * c[10] * c[5]); + r[148] = rf[148] * (c[11] * c[12] - rkc[148] * c[1] * c[24]); + r[149] = rf[149] * (c[11] * c[13] - rkc[149] * c[12] * c[12]); + r[150] = rf[150] * (c[11] * c[14] - rkc[150] * c[10] * c[14]); + r[151] = rf[151] * (c[11] * c[15] - rkc[151] * c[10] * c[15]); + r[152] = rf[152] * (c[11] * c[15] - rkc[152] * c[14] * c[17]); + r[153] = rf[153] * (c[11] * c[26] - rkc[153] * c[12] * c[25]); + r[154] = rf[154] * (c[12] * c[3] - rkc[154] * c[2] * c[19]); + r[155] = rf[155] * (c[12] * c[3] - rkc[155] * c[4] * c[17]); + r[156] = rf[156] * (c[12] * c[7] - rkc[156] * c[6] * c[13]); + r[157] = rf[157] * (c[12] * c[12] - rkc[157] * c[26]); + r[158] = rf[158] * (c[12] * c[12] - rkc[158] * c[1] * c[25]); + r[159] = rf[159] * (c[12] * c[16] - rkc[159] * c[13] * c[14]); + r[160] = rf[160] * (c[12] * c[17] - rkc[160] * c[16] * c[13]); + r[161] = rf[161] * (c[12] * c[20] - rkc[161] * c[18] * c[13]); + r[162] = rf[162] * (c[12] * c[20] - rkc[162] * c[19] * c[13]); + r[163] = rf[163] * (c[12] * c[24] - rkc[163] * c[23] * c[13]); + r[164] = rf[164] * (c[12] * c[26] - rkc[164] * c[25] * c[13]); + r[165] = rf[165] * (c[16] * c[5] - rkc[165] * c[1] * c[14] * c[5]); + r[166] = rf[166] * (c[16] - rkc[166] * c[1] * c[14]); + r[167] = rf[167] * (c[16] * c[3] - rkc[167] * c[6] * c[14]); + r[168] = rf[168] * (c[18] * c[3] - rkc[168] * c[6] * c[17]); + r[169] = rf[169] * (c[19] * c[3] - rkc[169] * c[6] * c[17]); + r[170] = rf[170] * (c[21] * c[3] - rkc[170] * c[16] * c[14]); + r[171] = rf[171] * (c[21] * c[0] - rkc[171] * c[1] * c[22]); + r[172] = rf[172] * (c[23] * c[3] - rkc[172] * c[16] * c[17]); + r[173] = rf[173] * (c[24] - rkc[173] * c[0] * c[22]); + r[174] = rf[174] * (c[25] * c[3] - rkc[174] * c[6] * c[24]); + r[175] = rf[175] * (c[27] * c[3] - rkc[175] * c[4] * c[14] * c[14]); + r[176] = rf[176] * (c[27] * c[27] - rkc[176] * c[14] * c[14] * c[22]); + r[177] = rf[177] * (c[30] * c[35] - rkc[177] * c[47] * c[2]); + r[178] = rf[178] * (c[30] * c[3] - rkc[178] * c[35] * c[2]); + r[179] = rf[179] * (c[30] * c[4] - rkc[179] * c[35] * c[1]); + r[180] = rf[180] * (c[37] * c[2] - rkc[180] * c[47] * c[3]); + r[181] = rf[181] * (c[37] * c[2] - rkc[181] * c[35] * c[35]); + r[182] = rf[182] * (c[37] * c[1] - rkc[182] * c[47] * c[4]); + r[183] = rf[183] * (c[37] * c[4] - rkc[183] * c[47] * c[6]); + r[184] = rf[184] * (c[37] - rkc[184] * c[47] * c[2]); + r[185] = rf[185] * (c[6] * c[35] - rkc[185] * c[36] * c[4]); + r[186] = rf[186] * (c[35] * c[2] - rkc[186] * c[36]); + r[187] = rf[187] * (c[36] * c[2] - rkc[187] * c[35] * c[3]); + r[188] = rf[188] * (c[36] * c[1] - rkc[188] * c[35] * c[4]); + r[189] = rf[189] * (c[31] * c[2] - rkc[189] * c[35] * c[1]); + r[190] = rf[190] * (c[31] * c[1] - rkc[190] * c[30] * c[0]); + r[191] = rf[191] * (c[31] * c[4] - rkc[191] * c[38] * c[1]); + r[192] = rf[192] * (c[31] * c[4] - rkc[192] * c[30] * c[5]); + r[193] = rf[193] * (c[31] * c[3] - rkc[193] * c[38] * c[2]); + r[194] = rf[194] * (c[31] * c[3] - rkc[194] * c[35] * c[4]); + r[195] = rf[195] * (c[31] * c[30] - rkc[195] * c[47] * c[1]); + r[196] = rf[196] * (c[31] * c[5] - rkc[196] * c[38] * c[0]); + r[197] = rf[197] * (c[31] * c[35] - rkc[197] * c[47] * c[4]); + r[198] = rf[198] * (c[31] * c[35] - rkc[198] * c[37] * c[1]); + r[199] = rf[199] * (c[32] * c[2] - rkc[199] * c[4] * c[31]); + r[200] = rf[200] * (c[32] * c[2] - rkc[200] * c[1] * c[38]); + r[201] = rf[201] * (c[32] * c[1] - rkc[201] * c[31] * c[0]); + r[202] = rf[202] * (c[32] * c[4] - rkc[202] * c[31] * c[5]); + r[203] = rf[203] * (c[34] - rkc[203] * c[47] * c[1]); + r[204] = rf[204] * (c[34] - rkc[204] * c[47] * c[1]); + r[205] = rf[205] * (c[34] * c[3] - rkc[205] * c[6] * c[47]); + r[206] = rf[206] * (c[34] * c[2] - rkc[206] * c[4] * c[47]); + r[207] = rf[207] * (c[34] * c[2] - rkc[207] * c[31] * c[35]); + r[208] = rf[208] * (c[34] * c[1] - rkc[208] * c[0] * c[47]); + r[209] = rf[209] * (c[34] * c[4] - rkc[209] * c[5] * c[47]); + r[210] = rf[210] * (c[34] * c[12] - rkc[210] * c[13] * c[47]); + r[211] = rf[211] * (c[1] * c[35] - rkc[211] * c[38]); + r[212] = rf[212] * (c[38] * c[2] - rkc[212] * c[35] * c[4]); + r[213] = rf[213] * (c[38] * c[1] - rkc[213] * c[0] * c[35]); + r[214] = rf[214] * (c[38] * c[4] - rkc[214] * c[35] * c[5]); + r[215] = rf[215] * (c[38] * c[3] - rkc[215] * c[6] * c[35]); + r[216] = rf[216] * (c[39] * c[2] - rkc[216] * c[14] * c[30]); + r[217] = rf[217] * (c[39] * c[4] - rkc[217] * c[46] * c[1]); + r[218] = rf[218] * (c[39] * c[5] - rkc[218] * c[40] * c[4]); + r[219] = rf[219] * (c[39] * c[3] - rkc[219] * c[46] * c[2]); + r[220] = rf[220] * (c[39] * c[0] - rkc[220] * c[40] * c[1]); + r[221] = rf[221] * (c[46] * c[2] - rkc[221] * c[35] * c[14]); + r[222] = rf[222] * (c[46] * c[1] - rkc[222] * c[31] * c[14]); + r[223] = rf[223] * (c[46] * c[4] - rkc[223] * c[35] * c[1] * c[14]); + r[224] = rf[224] * (c[46] * c[30] - rkc[224] * c[47] * c[14]); + r[225] = rf[225] * (c[46] * c[3] - rkc[225] * c[35] * c[15]); + r[226] = rf[226] * (c[46] - rkc[226] * c[30] * c[14]); + r[227] = rf[227] * (c[46] * c[35] - rkc[227] * c[37] * c[14]); + r[228] = rf[228] * (c[46] * c[35] - rkc[228] * c[47] * c[15]); + r[229] = rf[229] * (c[40] - rkc[229] * c[1] * c[39]); + r[230] = rf[230] * (c[40] * c[2] - rkc[230] * c[46] * c[1]); + r[231] = rf[231] * (c[40] * c[2] - rkc[231] * c[31] * c[14]); + r[232] = rf[232] * (c[40] * c[2] - rkc[232] * c[39] * c[4]); + r[233] = rf[233] * (c[40] * c[4] - rkc[233] * c[44] * c[1]); + r[234] = rf[234] * (c[40] * c[4] - rkc[234] * c[45] * c[1]); + r[235] = rf[235] * (c[40] * c[4] - rkc[235] * c[32] * c[14]); + r[236] = rf[236] * (c[1] * c[40] - rkc[236] * c[41]); + r[237] = rf[237] * (c[41] * c[30] - rkc[237] * c[47] * c[10]); + r[238] = rf[238] * (c[8] * c[47] - rkc[238] * c[39] * c[30]); + r[239] = rf[239] * (c[9] * c[47] - rkc[239] * c[40] * c[30]); + r[240] = rf[240] * (c[9] * c[47] - rkc[240] * c[42]); + r[241] = rf[241] * (c[10] * c[47] - rkc[241] * c[40] * c[31]); + r[242] = rf[242] * (c[11] * c[47] - rkc[242] * c[31] * c[40]); + r[243] = rf[243] * (c[8] * c[35] - rkc[243] * c[39] * c[2]); + r[244] = rf[244] * (c[8] * c[35] - rkc[244] * c[14] * c[30]); + r[245] = rf[245] * (c[9] * c[35] - rkc[245] * c[40] * c[2]); + r[246] = rf[246] * (c[9] * c[35] - rkc[246] * c[1] * c[46]); + r[247] = rf[247] * (c[9] * c[35] - rkc[247] * c[30] * c[16]); + r[248] = rf[248] * (c[10] * c[35] - rkc[248] * c[1] * c[45]); + r[249] = rf[249] * (c[10] * c[35] - rkc[249] * c[4] * c[40]); + r[250] = rf[250] * (c[10] * c[35] - rkc[250] * c[1] * c[43]); + r[251] = rf[251] * (c[11] * c[35] - rkc[251] * c[1] * c[45]); + r[252] = rf[252] * (c[11] * c[35] - rkc[252] * c[4] * c[40]); + r[253] = rf[253] * (c[11] * c[35] - rkc[253] * c[1] * c[43]); + r[254] = rf[254] * (c[12] * c[35] - rkc[254] * c[40] * c[5]); + r[255] = rf[255] * (c[12] * c[35] - rkc[255] * c[41] * c[4]); + r[256] = rf[256] * (c[42] * c[2] - rkc[256] * c[14] * c[1] * c[47]); + r[257] = rf[257] * (c[42] * c[2] - rkc[257] * c[40] * c[35]); + r[258] = rf[258] * (c[42] * c[3] - rkc[258] * c[2] * c[16] * c[47]); + r[259] = rf[259] * (c[42] * c[4] - rkc[259] * c[1] * c[16] * c[47]); + r[260] = rf[260] * (c[42] * c[1] - rkc[260] * c[10] * c[47]); + r[261] = rf[261] * (c[45] * c[2] - rkc[261] * c[31] * c[15]); + r[262] = rf[262] * (c[45] * c[2] - rkc[262] * c[38] * c[14]); + r[263] = rf[263] * (c[45] * c[2] - rkc[263] * c[46] * c[4]); + r[264] = rf[264] * (c[45] * c[1] - rkc[264] * c[32] * c[14]); + r[265] = rf[265] * (c[45] * c[1] - rkc[265] * c[0] * c[46]); + r[266] = rf[266] * (c[45] * c[4] - rkc[266] * c[46] * c[5]); + r[267] = rf[267] * (c[45] * c[4] - rkc[267] * c[32] * c[15]); + r[268] = rf[268] * (c[45] - rkc[268] * c[31] * c[14]); + r[269] = rf[269] * (c[43] * c[1] - rkc[269] * c[1] * c[45]); + r[270] = rf[270] * (c[43] * c[1] - rkc[270] * c[4] * c[40]); + r[271] = rf[271] * (c[43] * c[1] - rkc[271] * c[32] * c[14]); + r[272] = rf[272] * (c[44] * c[1] - rkc[272] * c[1] * c[45]); + r[273] = rf[273] * (c[27] * c[35] - rkc[273] * c[43] * c[14]); + r[274] = rf[274] * (c[12] * c[30] - rkc[274] * c[41] * c[1]); + r[275] = rf[275] * (c[12] * c[30] - rkc[275] * c[40] * c[0]); + r[276] = rf[276] * (c[33] * c[1] - rkc[276] * c[32] * c[0]); + r[277] = rf[277] * (c[33] * c[4] - rkc[277] * c[32] * c[5]); + r[278] = rf[278] * (c[33] * c[2] - rkc[278] * c[32] * c[4]); + r[279] = rf[279] * (c[31] * c[15] - rkc[279] * c[38] * c[14]); + r[280] = rf[280] * (c[39] * c[36] - rkc[280] * c[46] * c[35]); + r[281] = rf[281] * (c[46] * c[36] - rkc[281] * c[37] * c[15]); + r[282] = rf[282] * (c[30] * c[15] - rkc[282] * c[35] * c[14]); + r[283] = rf[283] * (c[2] * c[12]); + r[284] = rf[284] * (c[2] * c[24] - rkc[284] * c[1] * c[51]); + r[285] = rf[285] * (c[2] * c[25] - rkc[285] * c[1] * c[52]); + r[286] = rf[286] * (c[4] * c[6] - rkc[286] * c[3] * c[5]); + r[287] = rf[287] * (c[4] * c[12]); + r[288] = rf[288] * (c[9] * c[0] - rkc[288] * c[12]); + r[289] = rf[289] * (c[10] * c[3]); + r[290] = rf[290] * (c[10] * c[3] - rkc[290] * c[2] * c[17]); + r[291] = rf[291] * (c[10] * c[10]); + r[292] = rf[292] * (c[11] * c[5]); + r[293] = rf[293] * (c[23] * c[3] - rkc[293] * c[2] * c[51]); + r[294] = rf[294] * (c[23] * c[3] - rkc[294] * c[6] * c[22]); + r[295] = rf[295] * (c[2] * c[52] - rkc[295] * c[4] * c[51]); + r[296] = rf[296] * (c[2] * c[52]); + r[297] = rf[297] * (c[3] * c[52]); + r[298] = rf[298] * (c[1] * c[52] - rkc[298] * c[51] * c[0]); + r[299] = rf[299] * (c[1] * c[52]); + r[300] = rf[300] * (c[4] * c[52]); + r[301] = rf[301] * (c[6] * c[52]); + r[302] = rf[302] * (c[12] * c[52]); + r[303] = rf[303] * (c[1] * c[28] - rkc[303] * c[51]); + r[304] = rf[304] * (c[2] * c[51]); + r[305] = rf[305] * (c[3] * c[51]); + r[306] = rf[306] * (c[3] * c[51]); + r[307] = rf[307] * (c[1] * c[51] - rkc[307] * c[12] * c[16]); + r[308] = rf[308] * (c[1] * c[51] - rkc[308] * c[28] * c[0]); + r[309] = rf[309] * (c[4] * c[51] - rkc[309] * c[5] * c[28]); + r[310] = rf[310] * (c[4] * c[51] - rkc[310] * c[16] * c[18]); + r[311] = rf[311] * (c[12] * c[25] - rkc[311] * c[50]); + r[312] = rf[312] * (c[2] * c[50] - rkc[312] * c[4] * c[49]); + r[313] = rf[313] * (c[1] * c[50] - rkc[313] * c[49] * c[0]); + r[314] = rf[314] * (c[4] * c[50] - rkc[314] * c[49] * c[5]); + r[315] = rf[315] * (c[49] * c[7] - rkc[315] * c[6] * c[50]); + r[316] = rf[316] * (c[12] * c[50] - rkc[316] * c[49] * c[13]); + r[317] = rf[317] * (c[12] * c[24] - rkc[317] * c[49]); + r[318] = rf[318] * (c[2] * c[49] - rkc[318] * c[25] * c[17]); + r[319] = rf[319] * (c[1] * c[49] - rkc[319] * c[50]); + r[320] = rf[320] * (c[1] * c[49] - rkc[320] * c[12] * c[25]); + r[321] = rf[321] * (c[4] * c[49] - rkc[321] * c[25] * c[18]); + r[322] = rf[322] * (c[6] * c[49] - rkc[322] * c[3] * c[50]); + r[323] = rf[323] * (c[6] * c[49]); + r[324] = rf[324] * (c[12] * c[49] - rkc[324] * c[25] * c[25]); + } + +} + + + + + + + + diff --git a/Cantera/src/kinetics/GRI_30_Kinetics.h b/Cantera/src/kinetics/GRI_30_Kinetics.h new file mode 100755 index 000000000..c9dd601d8 --- /dev/null +++ b/Cantera/src/kinetics/GRI_30_Kinetics.h @@ -0,0 +1,50 @@ +/** + * + * @file GRI_30_Kinetics.h + * + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_GRI30_KINETICS_H +#define CT_GRI30_KINETICS_H + +#include "GasKinetics.h" + +namespace Cantera { + + const int cGRI_30_Kinetics = cGasKinetics + 1; + + /** + * Kinetics manager implementing reaction mechanism GRI-Mech 3.0 + */ + class GRI_30_Kinetics : public GasKinetics { + + public: + + /// Default constructor. + GRI_30_Kinetics(thermo_t* th=0); + + /// Destructor. + virtual ~GRI_30_Kinetics(){} + + virtual int ID() { return cGRI_30_Kinetics; } + + virtual void getNetProductionRates(doublereal* net) { + gri30_updateROP(); + get_wdot(&m_kdata->m_ropnet[0], net); + } + + private: + void gri30_update_rates_T(); + void gri30_updateROP(); + void gri30_updateKc(); + void get_wdot(const doublereal* rop, doublereal* wdot); + void update_kc(const double* grt, double c0, double* rkc); + void update_rates(double t, double tlog, double* rf); + void eval_ropnet(const double* c, const double* rf, const double* rkc, double* r); + }; +} + +#endif diff --git a/Cantera/src/kinetics/GasKinetics.cpp b/Cantera/src/kinetics/GasKinetics.cpp new file mode 100755 index 000000000..caa07a288 --- /dev/null +++ b/Cantera/src/kinetics/GasKinetics.cpp @@ -0,0 +1,666 @@ +/** + * @file GasKinetics.cpp + * + * Homogeneous kinetics in ideal gases + * + */ + +// Copyright 2001 California Institute of Technology + + +// turn off warnings under Windows +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "GasKinetics.h" + +#include "ReactionData.h" +#include "Enhanced3BConc.h" +#include "ThirdBodyMgr.h" +#include "RateCoeffMgr.h" + +//#include "../user/grirxnstoich.h" + +#include +using namespace std; + + +namespace Cantera { + + + /** + * Construct an empty reaction mechanism. + */ + GasKinetics:: + GasKinetics(thermo_t* thermo) : + Kinetics(), + m_kk(0), + m_nfall(0), + m_dt_threshold(0.0), // 1.e-6), + m_nirrev(0), + m_nrev(0), + m_finalized(false) + { + if (thermo != 0) addPhase(*thermo); + m_kdata = new GasKineticsData; + m_kdata->m_temp = 0.0; + m_rxnstoich = new ReactionStoichMgr; + } + + GasKinetics:: + ~GasKinetics() {delete m_kdata; delete m_rxnstoich;} + + /** + * Update temperature-dependent portions of reaction rates and + * falloff functions. + */ + void GasKinetics:: + update_T() {} + + void GasKinetics:: + update_C() {} + + void GasKinetics:: + _update_rates_T() { + doublereal T = thermo().temperature(); + m_kdata->m_logStandConc = log(thermo().standardConcentration()); + if (fabs(T - m_kdata->m_temp) > 0.0) { // m_dt_threshold) { + doublereal logT = log(T); + //m_kdata->m_logp0 - logT; + m_rates.update(T, logT, &m_kdata->m_rfn[0]); + m_falloff_low_rates.update(T, logT, &m_kdata->m_rfn_low[0]); + m_falloff_high_rates.update(T, logT, &m_kdata->m_rfn_high[0]); + m_falloffn.updateTemp(T, &m_kdata->falloff_work[0]); + m_kdata->m_temp = T; + updateKc(); + m_kdata->m_ROP_ok = false; + } + }; + + + /** + * Update properties that depend on concentrations. Currently only + * the enhanced collision partner concentrations are updated here. + */ + void GasKinetics:: + _update_rates_C() { + thermo().getActivityConcentrations(&m_conc[0]); + doublereal ctot = thermo().molarDensity(); + m_3b_concm.update(m_conc, ctot, &m_kdata->concm_3b_values[0]); + m_falloff_concm.update(m_conc, ctot, + &m_kdata->concm_falloff_values[0]); + m_kdata->m_ROP_ok = false; + } + + /** + * Update the equilibrium constants in molar units. + */ + void GasKinetics::updateKc() { + int i, irxn; + vector_fp& m_rkc = m_kdata->m_rkcn; + + thermo().getStandardChemPotentials(&m_grt[0]); + fill(m_rkc.begin(), m_rkc.end(), 0.0); + + // compute Delta G^0 for all reversible reactions + m_rxnstoich->getRevReactionDelta(m_ii, &m_grt[0], &m_rkc[0]); + + doublereal logStandConc = m_kdata->m_logStandConc; + doublereal rrt = 1.0/(GasConstant * thermo().temperature()); + for (i = 0; i < m_nrev; i++) { + irxn = m_revindex[i]; + m_rkc[irxn] = exp(m_rkc[irxn]*rrt - m_dn[irxn]*logStandConc); + } + + for(i = 0; i != m_nirrev; ++i) { + m_rkc[ m_irrev[i] ] = 0.0; + } + } + + /** + * Get the equilibrium constants of all reactions, whether + * reversible or not. + */ + void GasKinetics::getEquilibriumConstants(doublereal* kc) { + int i; + _update_rates_T(); + vector_fp& rkc = m_kdata->m_rkcn; + //thermo().getGibbs_RT(m_grt.begin()); + thermo().getStandardChemPotentials(&m_grt[0]); + fill(rkc.begin(), rkc.end(), 0.0); + + // compute Delta G^0 for all reactions + m_rxnstoich->getReactionDelta(m_ii, &m_grt[0], &rkc[0]); + + doublereal logStandConc = m_kdata->m_logStandConc; + doublereal rrt = 1.0/(GasConstant * thermo().temperature()); + for (i = 0; i < m_ii; i++) { + kc[i] = exp(-rkc[i]*rrt + m_dn[i]*logStandConc); + } + + // force an update of T-dependent properties, so that m_rkcn will + // be updated before it is used next. + m_kdata->m_temp = 0.0; + } + + /** + * + * getDeltaGibbs(): + * + * Return the vector of values for the reaction gibbs free energy + * change + * These values depend upon the concentration + * of the ideal gas. + * + * units = J kmol-1 + */ + void GasKinetics::getDeltaGibbs(doublereal* deltaG) { + /* + * Get the chemical potentials of the species in the + * ideal gas solution. + */ + thermo().getChemPotentials(&m_grt[0]); + /* + * Use the stoichiometric manager to find deltaG for each + * reaction. + */ + m_rxnstoich->getReactionDelta(m_ii, &m_grt[0], deltaG); + } + + /** + * + * getDeltaEnthalpy(): + * + * Return the vector of values for the reactions change in + * enthalpy. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + void GasKinetics::getDeltaEnthalpy(doublereal* deltaH) { + /* + * Get the partial molar enthalpy of all species in the + * ideal gas. + */ + thermo().getPartialMolarEnthalpies(&m_grt[0]); + /* + * Use the stoichiometric manager to find deltaG for each + * reaction. + */ + m_rxnstoich->getReactionDelta(m_ii, &m_grt[0], deltaH); + } + + /************************************************************************ + * + * getDeltaEntropy(): + * + * Return the vector of values for the reactions change in + * entropy. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 Kelvin-1 + */ + void GasKinetics::getDeltaEntropy( doublereal* deltaS) { + /* + * Get the partial molar entropy of all species in the + * solid solution. + */ + thermo().getPartialMolarEntropies(&m_grt[0]); + /* + * Use the stoichiometric manager to find deltaS for each + * reaction. + */ + m_rxnstoich->getReactionDelta(m_ii, &m_grt[0], deltaS); + } + + /** + * + * getDeltaSSGibbs(): + * + * Return the vector of values for the reaction + * standard state gibbs free energy change. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + void GasKinetics::getDeltaSSGibbs(doublereal* deltaG) { + /* + * Get the standard state chemical potentials of the species. + * This is the array of chemical potentials at unit activity + * We define these here as the chemical potentials of the pure + * species at the temperature and pressure of the solution. + */ + thermo().getStandardChemPotentials(&m_grt[0]); + /* + * Use the stoichiometric manager to find deltaG for each + * reaction. + */ + m_rxnstoich->getReactionDelta(m_ii, &m_grt[0], deltaG); + } + + /** + * + * getDeltaSSEnthalpy(): + * + * Return the vector of values for the change in the + * standard state enthalpies of reaction. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + void GasKinetics::getDeltaSSEnthalpy(doublereal* deltaH) { + /* + * Get the standard state enthalpies of the species. + * This is the array of chemical potentials at unit activity + * We define these here as the enthalpies of the pure + * species at the temperature and pressure of the solution. + */ + thermo().getEnthalpy_RT(&m_grt[0]); + doublereal RT = thermo().temperature() * GasConstant; + for (int k = 0; k < m_kk; k++) { + m_grt[k] *= RT; + } + /* + * Use the stoichiometric manager to find deltaG for each + * reaction. + */ + m_rxnstoich->getReactionDelta(m_ii, &m_grt[0], deltaH); + } + + /********************************************************************* + * + * getDeltaSSEntropy(): + * + * Return the vector of values for the change in the + * standard state entropies for each reaction. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 Kelvin-1 + */ + void GasKinetics::getDeltaSSEntropy(doublereal* deltaS) { + /* + * Get the standard state entropy of the species. + * We define these here as the entropies of the pure + * species at the temperature and pressure of the solution. + */ + thermo().getEntropy_R(&m_grt[0]); + doublereal R = GasConstant; + for (int k = 0; k < m_kk; k++) { + m_grt[k] *= R; + } + /* + * Use the stoichiometric manager to find deltaS for each + * reaction. + */ + m_rxnstoich->getReactionDelta(m_ii, &m_grt[0], deltaS); + } + + void GasKinetics::processFalloffReactions() { + + int i; + const vector_fp& fc = m_kdata->concm_falloff_values; + const array_fp& m_rf_low = m_kdata->m_rfn_low; + const array_fp& m_rf_high = m_kdata->m_rfn_high; + + // use m_ropr for temporary storage of reduced pressure + array_fp& pr = m_kdata->m_ropr; + + array_fp& ropf = m_kdata->m_ropf; + + for (i = 0; i < m_nfall; i++) { + pr[i] = fc[i] * m_rf_low[i] / m_rf_high[i]; + } + + m_falloffn.pr_to_falloff( &pr[0], &m_kdata->falloff_work[0] ); + + for (i = 0; i < m_nfall; i++) { + pr[i] *= m_rf_high[i]; + } + + scatter_copy(pr.begin(), pr.begin() + m_nfall, + ropf.begin(), m_fallindx.begin()); + } + + + void GasKinetics::updateROP() { + + _update_rates_T(); + _update_rates_C(); + + if (m_kdata->m_ROP_ok) return; + + const vector_fp& rf = m_kdata->m_rfn; + const vector_fp& m_rkc = m_kdata->m_rkcn; + array_fp& ropf = m_kdata->m_ropf; + array_fp& ropr = m_kdata->m_ropr; + array_fp& ropnet = m_kdata->m_ropnet; + + // copy rate coefficients into ropf + copy(rf.begin(), rf.end(), ropf.begin()); + + // multiply ropf by enhanced 3b conc for all 3b rxns + m_3b_concm.multiply( &ropf[0], &m_kdata->concm_3b_values[0] ); + + processFalloffReactions(); + + // multiply by perturbation factor + multiply_each(ropf.begin(), ropf.end(), m_perturb.begin()); + + // copy the forward rates to the reverse rates + copy(ropf.begin(), ropf.end(), ropr.begin()); + + // for reverse rates computed from thermochemistry, multiply + // the forward rates copied into m_ropr by the reciprocals of + // the equilibrium constants + multiply_each(ropr.begin(), ropr.end(), m_rkc.begin()); + + // multiply ropf by concentration products + m_rxnstoich->multiplyReactants(&m_conc[0], &ropf[0]); + //m_reactantStoich.multiply(m_conc.begin(), ropf.begin()); + + // for reversible reactions, multiply ropr by concentration + // products + m_rxnstoich->multiplyRevProducts(&m_conc[0], &ropr[0]); + //m_revProductStoich.multiply(m_conc.begin(), ropr.begin()); + + for (int j = 0; j != m_ii; ++j) { + ropnet[j] = ropf[j] - ropr[j]; + } + + m_kdata->m_ROP_ok = true; + } + + /** + * + * getFwdRateConstants(): + * + * Update the rate of progress for the reactions. + * This key routine makes sure that the rate of progress vectors + * located in the solid kinetics data class are up to date. + */ + void GasKinetics:: + getFwdRateConstants(doublereal *kfwd) { + _update_rates_T(); + _update_rates_C(); + + // copy rate coefficients into ropf + const vector_fp& rf = m_kdata->m_rfn; + array_fp& ropf = m_kdata->m_ropf; + copy(rf.begin(), rf.end(), ropf.begin()); + + // multiply ropf by enhanced 3b conc for all 3b rxns + m_3b_concm.multiply(&ropf[0], &m_kdata->concm_3b_values[0] ); + + /* + * This routine is hardcoded to replace some of the values + * of the ropf vector. + */ + processFalloffReactions(); + + // multiply by perturbation factor + multiply_each(ropf.begin(), ropf.end(), m_perturb.begin()); + + for (int i = 0; i < m_ii; i++) { + kfwd[i] = ropf[i]; + } + } + + /** + * + * getRevRateConstants(): + * + * Return a vector of the reverse reaction rate constants + * + * Length is the number of reactions. units depends + * on many issues. Note, this routine will return rate constants + * for irreversible reactions if the default for + * doIrreversible is overridden. + */ + void GasKinetics:: + getRevRateConstants(doublereal *krev, bool doIrreversible) { + /* + * go get the forward rate constants. -> note, we don't + * really care about speed or redundancy in these + * informational routines. + */ + getFwdRateConstants(krev); + + if (doIrreversible) { + doublereal *tmpKc = &m_kdata->m_ropnet[0]; + getEquilibriumConstants(tmpKc); + for (int i = 0; i < m_ii; i++) { + krev[i] /= tmpKc[i]; + } + } else { + /* + * m_rkc[] is zero for irreversibly reactions + */ + const vector_fp& m_rkc = m_kdata->m_rkcn; + for (int i = 0; i < m_ii; i++) { + krev[i] *= m_rkc[i]; + } + } + } + + void GasKinetics:: + addReaction(const ReactionData& r) { + + if (r.reactionType == ELEMENTARY_RXN) addElementaryReaction(r); + else if (r.reactionType == THREE_BODY_RXN) addThreeBodyReaction(r); + else if (r.reactionType == FALLOFF_RXN) addFalloffReaction(r); + + // operations common to all reaction types + installReagents( r ); + installGroups(reactionNumber(), r.rgroups, r.pgroups); + incrementRxnCount(); + m_rxneqn.push_back(r.equation); + } + + + void GasKinetics:: + addFalloffReaction(const ReactionData& r) { + + // install high and low rate coeff calculators + + int iloc = m_falloff_high_rates.install(m_nfall, + r.rateCoeffType, + r.rateCoeffParameters.size(), + &r.rateCoeffParameters[0] ); + + m_falloff_low_rates.install( m_nfall, + r.rateCoeffType, r.auxRateCoeffParameters.size(), + DATA_PTR(r.auxRateCoeffParameters) ); + + // add constant terms to high and low rate + // coeff value vectors + m_kdata->m_rfn_high.push_back(r.rateCoeffParameters[0]); + m_kdata->m_rfn_low.push_back(r.auxRateCoeffParameters[0]); + + // add a dummy entry in m_rf, where computed falloff + // rate coeff will be put + m_kdata->m_rfn.push_back(0.0); + + // add this reaction number to the list of + // falloff reactions + m_fallindx.push_back( reactionNumber() ); + + // install the enhanced third-body concentration + // calculator for this reaction + m_falloff_concm.install( m_nfall, r.thirdBodyEfficiencies, + r.default_3b_eff); + + // install the falloff function calculator for + // this reaction + m_falloffn.install( m_nfall, r.falloffType, r.falloffParameters ); + + // forward rxn order equals number of reactants, since rate + // coeff is defined in terms of the high-pressure limit + m_fwdOrder.push_back(r.reactants.size()); + + // increment the falloff reaction counter + ++m_nfall; + registerReaction( reactionNumber(), FALLOFF_RXN, iloc); + } + + + void GasKinetics:: + addElementaryReaction(const ReactionData& r) { + int iloc; + + // install rate coeff calculator + iloc = m_rates.install( reactionNumber(), + r.rateCoeffType, r.rateCoeffParameters.size(), + DATA_PTR(r.rateCoeffParameters) ); + + // add constant term to rate coeff value vector + m_kdata->m_rfn.push_back(r.rateCoeffParameters[0]); + + // forward rxn order equals number of reactants + m_fwdOrder.push_back(r.reactants.size()); + registerReaction( reactionNumber(), ELEMENTARY_RXN, iloc); + } + + + void GasKinetics:: + addThreeBodyReaction(const ReactionData& r) { + + int iloc; + // install rate coeff calculator + iloc = m_rates.install( reactionNumber(), + r.rateCoeffType, r.rateCoeffParameters.size(), + DATA_PTR(r.rateCoeffParameters) ); + + // add constant term to rate coeff value vector + m_kdata->m_rfn.push_back(r.rateCoeffParameters[0]); + + // forward rxn order equals number of reactants + 1 + m_fwdOrder.push_back(r.reactants.size() + 1); + + m_3b_concm.install( reactionNumber(), r.thirdBodyEfficiencies, + r.default_3b_eff ); + registerReaction( reactionNumber(), THREE_BODY_RXN, iloc); + } + + + void GasKinetics::installReagents(const ReactionData& r) { + + m_kdata->m_ropf.push_back(0.0); // extend by one for new rxn + m_kdata->m_ropr.push_back(0.0); + m_kdata->m_ropnet.push_back(0.0); + int n, ns, m; + doublereal nsFlt; + doublereal reactantGlobalOrder = 0.0; + doublereal productGlobalOrder = 0.0; + int rnum = reactionNumber(); + + vector_int rk; + int nr = r.reactants.size(); + for (n = 0; n < nr; n++) { + nsFlt = r.rstoich[n]; + reactantGlobalOrder += nsFlt; + ns = (int) nsFlt; + if ((doublereal) ns != nsFlt) { + if (ns < 1) { + ns = 1; + } + } + if (r.rstoich[n] != 0.0) + m_rrxn[r.reactants[n]][rnum] += r.rstoich[n]; + for (m = 0; m < ns; m++) { + rk.push_back(r.reactants[n]); + } + } + m_reactants.push_back(rk); + + vector_int pk; + int np = r.products.size(); + for (n = 0; n < np; n++) { + nsFlt = r.pstoich[n]; + productGlobalOrder += nsFlt; + ns = (int) nsFlt; + if ((double) ns != nsFlt) { + if (ns < 1) { + ns = 1; + } + } + if (r.pstoich[n] != 0.0) + m_prxn[r.products[n]][rnum] += r.pstoich[n]; + for (m = 0; m < ns; m++) { + pk.push_back(r.products[n]); + } + } + m_products.push_back(pk); + + m_kdata->m_rkcn.push_back(0.0); + + m_rxnstoich->add(reactionNumber(), r); + + if (r.reversible) { + m_dn.push_back(productGlobalOrder - reactantGlobalOrder); + m_revindex.push_back(reactionNumber()); + m_nrev++; + } + else { + m_dn.push_back(productGlobalOrder - reactantGlobalOrder); + m_irrev.push_back( reactionNumber() ); + m_nirrev++; + } + } + + + void GasKinetics::installGroups(int irxn, + const vector& r, const vector& p) { + if (!r.empty()) { + writelog("installing groups for reaction "+int2str(reactionNumber())); + m_rgroups[reactionNumber()] = r; + m_pgroups[reactionNumber()] = p; + } + } + + + void GasKinetics::init() { + m_kk = thermo().nSpecies(); + m_rrxn.resize(m_kk); + m_prxn.resize(m_kk); + m_conc.resize(m_kk); + m_grt.resize(m_kk); + m_kdata->m_logp_ref = log(thermo().refPressure()) - log(GasConstant); + } + + void GasKinetics::finalize() { + if (!m_finalized) { + // int i, j, nr, np; + m_kdata->falloff_work.resize( + static_cast(m_falloffn.workSize())); + m_kdata->concm_3b_values.resize( + static_cast(m_3b_concm.workSize())); + m_kdata->concm_falloff_values.resize( + static_cast(m_falloff_concm.workSize())); + +// for (i = 0; i < m_ii; i++) { +// nr = m_reactants[i].size(); +// for (j = 0; j < nr; j++) { +// m_rstoich[i][m_reactants[i][j]]++; +// } +// np = m_products[i].size(); +// for (j = 0; j < np; j++) { +// m_pstoich[i][m_products[i][j]]++; +// } +// } + //m_rxnstoich->write("c.cpp"); + m_finalized = true; + } + } + + bool GasKinetics::ready() const { + return (m_finalized); + } + +} diff --git a/Cantera/src/kinetics/GasKinetics.h b/Cantera/src/kinetics/GasKinetics.h new file mode 100755 index 000000000..142be865e --- /dev/null +++ b/Cantera/src/kinetics/GasKinetics.h @@ -0,0 +1,420 @@ +/** + * @file GasKinetics.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_GASKINETICS_H +#define CT_GASKINETICS_H + +#include +#include +#include +#include + +#include "mix_defs.h" +#include "Kinetics.h" + +#include "utilities.h" + +#include "ReactionStoichMgr.h" +#include "ThirdBodyMgr.h" +#include "FalloffMgr.h" +#include "RateCoeffMgr.h" + +void get_wdot(const doublereal* rop, doublereal* wdot); + +namespace Cantera { + + // forward references + + class Enhanced3BConc; + class ReactionData; + class GasKineticsData; + class Thermo; + + /** + * Holds mechanism-specific data. + */ + class GasKineticsData { + public: + GasKineticsData() : + m_logp_ref(0.0), + m_logc_ref(0.0), + m_logStandConc(0.0), + m_ROP_ok(false), + m_temp(0.0) + {} + virtual ~GasKineticsData(){} + + doublereal m_logp_ref, m_logc_ref, m_logStandConc; + array_fp m_ropf, m_ropr, m_ropnet; + array_fp m_rfn_low, m_rfn_high; + bool m_ROP_ok; + + doublereal m_temp; + vector_fp m_rfn; + vector_fp falloff_work; + vector_fp concm_3b_values; + vector_fp concm_falloff_values; + vector_fp m_rkcn; + }; + + + /** + * Kinetics manager for elementary gas-phase chemistry. This + * kinetics manager implements standard mass-action reaction rate + * expressions for low-density gases. + * @ingroup kinetics + */ + + class GasKinetics : public Kinetics { + + public: + /** + * @name Constructors and General Information about Mechanism + */ + //@{ + /// Constructor. + GasKinetics(thermo_t* thermo = 0); + + /// Destructor. + virtual ~GasKinetics(); + + virtual int ID() { return cGasKinetics; } + + virtual doublereal reactantStoichCoeff(int k, int i) const { + return m_rrxn[k][i]; + } + + virtual doublereal productStoichCoeff(int k, int i) const { + return m_prxn[k][i]; + } + + //@} + /** + * @name Reaction Rates Of Progress + */ + //@{ + /** + * Forward rates of progress. + * Return the forward rates of progress in array fwdROP, which + * must be dimensioned at least as large as the total number + * of reactions. + */ + virtual void getFwdRatesOfProgress(doublereal* fwdROP) { + updateROP(); + std::copy(m_kdata->m_ropf.begin(), m_kdata->m_ropf.end(), fwdROP); + } + /** + * Reverse rates of progress. + * Return the reverse rates of progress in array revROP, which + * must be dimensioned at least as large as the total number + * of reactions. + */ + virtual void getRevRatesOfProgress(doublereal* revROP) { + updateROP(); + std::copy(m_kdata->m_ropr.begin(), m_kdata->m_ropr.end(), revROP); + } + /** + * Net rates of progress. Return the net (forward - reverse) + * rates of progress in array netROP, which must be + * dimensioned at least as large as the total number of + * reactions. + */ + virtual void getNetRatesOfProgress(doublereal* netROP) { + updateROP(); + std::copy(m_kdata->m_ropnet.begin(), m_kdata->m_ropnet.end(), netROP); + } + + + /** + * Equilibrium constants. Return the equilibrium constants of + * the reactions in concentration units in array kc, which + * must be dimensioned at least as large as the total number + * of reactions. + */ + virtual void getEquilibriumConstants(doublereal* kc); + + /** + * Return the vector of values for the reaction gibbs free energy + * change. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + virtual void getDeltaGibbs( doublereal* deltaG); + + /** + * Return the vector of values for the reactions change in + * enthalpy. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + virtual void getDeltaEnthalpy( doublereal* deltaH); + + /** + * Return the vector of values for the reactions change in + * entropy. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 Kelvin-1 + */ + virtual void getDeltaEntropy(doublereal* deltaS); + + /** + * Return the vector of values for the reaction + * standard state gibbs free energy change. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + virtual void getDeltaSSGibbs(doublereal* deltaG); + + /** + * Return the vector of values for the change in the + * standard state enthalpies of reaction. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + virtual void getDeltaSSEnthalpy(doublereal* deltaH); + + /** + * Return the vector of values for the change in the + * standard state entropies for each reaction. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 Kelvin-1 + */ + virtual void getDeltaSSEntropy(doublereal* deltaS); + + //@} + /** + * @name Species Production Rates + */ + //@{ + + /** + * Species net production rates [kmol/m^3]. Return the species + * net production rates (creation - destruction) in array + * wdot, which must be dimensioned at least as large as the + * total number of species. + */ + virtual void getNetProductionRates(doublereal* net) { + updateROP(); +#ifdef HWMECH + get_wdot(&m_kdata->m_ropnet[0], net); +#else + m_rxnstoich->getNetProductionRates(m_kk, &m_kdata->m_ropnet[0], net); +#endif + } + + /** + * Species creation rates [kmol/m^3]. Return the species + * creation rates in array cdot, which must be + * dimensioned at least as large as the total number of + * species. + * + */ + virtual void getCreationRates(doublereal* cdot) { + updateROP(); + m_rxnstoich->getCreationRates(m_kk, &m_kdata->m_ropf[0], + &m_kdata->m_ropr[0], cdot); + } + + /** + * Species destruction rates [kmol/m^3]. Return the species + * destruction rates in array ddot, which must be + * dimensioned at least as large as the total number of + * species. + * + */ + virtual void getDestructionRates(doublereal* ddot) { + updateROP(); + m_rxnstoich->getDestructionRates(m_kk, &m_kdata->m_ropf[0], + &m_kdata->m_ropr[0], ddot); + // fill(ddot, ddot + m_kk, 0.0); + //m_revProductStoich.incrementSpecies( + // m_kdata->m_ropr.begin(), ddot); + //m_reactantStoich.incrementSpecies( + // m_kdata->m_ropf.begin(), ddot); + } + + //@} + /** + * @name Reaction Mechanism Informational Query Routines + */ + //@{ + + /** + * Flag specifying the type of reaction. The legal values and + * their meaning are specific to the particular kinetics + * manager. + */ + virtual int reactionType(int i) const { + return m_index[i].first; + } + + virtual std::string reactionString(int i) const { + return m_rxneqn[i]; + } + + /** + * True if reaction i has been declared to be reversible. If + * isReversible(i) is false, then the reverse rate of progress + * for reaction i is always zero. + */ + virtual bool isReversible(int i) { + if (std::find(m_revindex.begin(), m_revindex.end(), i) + < m_revindex.end()) return true; + else return false; + } + + /** + * Return the forward rate constants + * + * length is the number of reactions. units depends + * on many issues. + */ + virtual void getFwdRateConstants(doublereal *kfwd); + + /** + * Return the reverse rate constants. + * + * length is the number of reactions. units depends + * on many issues. Note, this routine will return rate constants + * for irreversible reactions if the default for + * doIrreversible is overridden. + */ + virtual void getRevRateConstants(doublereal *krev, + bool doIrreversible = false); + + //@} + /** + * @name Reaction Mechanism Setup Routines + */ + //@{ + + + /** + * Set delta T threshold for updating temperature-dependent + * rates. + */ + void setRateUpdateThreshold(doublereal dt) { + m_dt_threshold = dt; + } + + virtual void init(); + + /// Add a reaction to the mechanism. + void addReaction(const ReactionData& r); + + virtual void finalize(); + virtual bool ready() const; + + virtual void update_T(); + virtual void update_C(); + + void updateROP(); + + + const std::vector& reactantGroups(int i) + { return m_rgroups[i]; } + const std::vector& productGroups(int i) + { return m_pgroups[i]; } + + + void _update_rates_T(); + void _update_rates_C(); + + //@} + + protected: + + int m_kk, m_nfall; + + vector_int m_fallindx; + doublereal m_dt_threshold; + + Rate1 m_falloff_low_rates; + Rate1 m_falloff_high_rates; + Rate1 m_rates; + + mutable std::map > m_index; + + FalloffMgr m_falloffn; + + ThirdBodyMgr m_3b_concm; + ThirdBodyMgr m_falloff_concm; + + std::vector m_irrev; + + ReactionStoichMgr* m_rxnstoich; + + std::vector m_fwdOrder; + + int m_nirrev; + int m_nrev; + + std::map > m_rgroups; + std::map > m_pgroups; + + std::vector m_rxntype; + + mutable std::vector > m_rrxn; + mutable std::vector > m_prxn; + + /** + * Difference between the input global reactants order + * and the input global products order. Changed to a double + * to account for the fact that we can have real-valued + * stoichiometries. + */ + vector_fp m_dn; + vector_int m_revindex; + + std::vector m_rxneqn; + + GasKineticsData* m_kdata; + + vector_fp m_conc; + void processFalloffReactions(); + vector_fp m_grt; + + + private: + + int reactionNumber(){ return m_ii;} + std::vector > m_stoich; + + void addElementaryReaction(const ReactionData& r); + void addThreeBodyReaction(const ReactionData& r); + void addFalloffReaction(const ReactionData& r); + + void installReagents(const ReactionData& r); + + void installGroups(int irxn, const std::vector& r, + const std::vector& p); + void updateKc(); + + void registerReaction(int rxnNumber, int type, int loc) { + m_index[rxnNumber] = std::pair(type, loc); + } + bool m_finalized; + }; +} + +#endif diff --git a/Cantera/src/kinetics/GasKineticsWriter.cpp b/Cantera/src/kinetics/GasKineticsWriter.cpp new file mode 100755 index 000000000..113849d75 --- /dev/null +++ b/Cantera/src/kinetics/GasKineticsWriter.cpp @@ -0,0 +1,136 @@ +/** + * @file GasKineticsWriter.cpp + * + */ + +// Copyright 2001 California Institute of Technology + + +// turn off warnings under Windows +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ReactionData.h" +#include "GasKineticsWriter.h" + +#include "StoichManager.h" +#include "Enhanced3BConc.h" +#include "ThirdBodyMgr.h" +#include "RateCoeffMgr.h" + +//#include "ThermoPhase.h" + +#include +using namespace std; + +namespace Cantera { + + /** + * Construct an empty reaction mechanism. + */ + GasKineticsWriter:: + GasKineticsWriter() : m_kk(0), m_ii(0), m_nfall(0), m_nrev(0), m_nirrev(0), + m_finalized(false) {} + + void GasKineticsWriter:: + addReaction(const ReactionData& r) { + + if (r.reactionType == ELEMENTARY_RXN) addElementaryReaction(r); + else if (r.reactionType == THREE_BODY_RXN) addThreeBodyReaction(r); + else if (r.reactionType == FALLOFF_RXN) addFalloffReaction(r); + + // operations common to all reaction types + installReagents( r.reactants, r.products, r.reversible ); + m_ii++; + } + + + void GasKineticsWriter:: + addFalloffReaction(const ReactionData& r) { + + // install high and low rate coeff calculators + m_falloff_high_rates.install( m_nfall, + r.rateCoeffType, r.rateCoeffParameters.size(), + r.rateCoeffParameters.begin() ); + m_falloff_low_rates.install( m_nfall, + r.rateCoeffType, r.auxRateCoeffParameters.size(), + r.auxRateCoeffParameters.begin() ); + + // add this reaction number to the list of + // falloff reactions + m_fallindx.push_back( reactionNumber() ); + + // increment the falloff reaction counter + ++m_nfall; + } + + + void GasKineticsWriter:: + addElementaryReaction(const ReactionData& r) { + + int iloc; + // install rate coeff calculator + iloc = m_rates.install( reactionNumber(), + r.rateCoeffType, r.rateCoeffParameters.size(), + r.rateCoeffParameters.begin() ); + } + + + void GasKineticsWriter:: + addThreeBodyReaction(const ReactionData& r) { + + int iloc; + // install rate coeff calculator + iloc = m_rates.install( reactionNumber(), + r.rateCoeffType, r.rateCoeffParameters.size(), + r.rateCoeffParameters.begin() ); + } + + + void GasKineticsWriter::installReagents(const vector_int& r, + const vector_int& p, bool reversible) { + + int nr = r.size(); + int rnum = reactionNumber(); + int i; + for (i = 0; i < nr; i++) { + m_rrxn[r[i]][rnum] += 1.0; + } + + m_reactantWriter.add( reactionNumber(), r); + + int np = p.size(); + + for (i = 0; i < np; i++) { + m_prxn[p[i]][rnum] += 1.0; + } + + if (reversible) { + m_revProductWriter.add(reactionNumber(), p); + m_dn.push_back(np - nr); + m_revindex.push_back(reactionNumber()); + m_nrev++; + } + else { + m_irrevProductWriter.add(reactionNumber(), p); + m_irrev.push_back( reactionNumber() ); + m_nirrev++; + } + } + + void GasKineticsWriter::init(int nsp) { + m_rrxn.resize(nsp); + m_prxn.resize(nsp); + } + +} + + + + + + + + diff --git a/Cantera/src/kinetics/GasKineticsWriter.h b/Cantera/src/kinetics/GasKineticsWriter.h new file mode 100755 index 000000000..070cdec16 --- /dev/null +++ b/Cantera/src/kinetics/GasKineticsWriter.h @@ -0,0 +1,203 @@ +/** + * + * @file GasKineticsWriter.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_GASKINETICSWRITER_H +#define CT_GASKINETICSWRITER_H + +#define WRITE_UPDATE + +#include +#include +#include +#include + +#include "mix_defs.h" +#include "Kinetics.h" + +#include "utilities.h" +#include "StoichManager.h" +#include "ThirdBodyMgr.h" +#include "FalloffMgr.h" +#include "RateCoeffMgr.h" +#include "Phase.h" + +namespace Cantera { + + // forward references + class Enhanced3BConc; + class ReactionData; + + //! Class to write a hard-coded version of a mechanism. + /*! + * @ingroup kineticsmgr + */ + class GasKineticsWriter { + + public: + + /// Default constructor. + GasKineticsWriter(); + + /// Destructor. + virtual ~GasKineticsWriter(){} + + void init(int nsp); + doublereal reactantStoichCoeff(int k, int i) const { + return m_rrxn[k][i]; + } + + doublereal productStoichCoeff(int k, int i) const { + return m_prxn[k][i]; + } + void writeUpdateROP(){} + + void writeGetNetProductionRates(ostream& s, int nsp, int nrxns) { + int i, k; + s << "void get_wdot(const double* rop, double* wdot) {" << endl; + for (k = 0; k < nsp; k++) { + s << " wdot[" << k << "] = "; + doublereal net; + bool empty = true; + for (i = 0; i < nrxns; i++) { + net = productStoichCoeff(k,i) - reactantStoichCoeff(k,i); + if (net > 0.0) { + empty = false; + if (net == 1.0) + s << " + rop[" << i << "]"; + else + s << " + " << net << "*rop[" << i << "]"; + } + else if (net < 0.0) { + empty = false; + if (net == -1.0) + s << " - rop[" << i << "]"; + else + s << " - " << -net << "*rop[" << i << "]"; + } + } + if (empty) s << "0.0"; + s << ";" << endl; + } + s << "}" << endl; + } + + + void writeUpdateKc(ostream& s, int nsp, int nrxns) { + int i, k, n, nn, ir; + s << "void update_kc(const double* a, " + "double exp_c0, double* rkc) {" << endl; + for (i = 0; i != m_nrev; i++) { + //if (isReversible(i)) { + ir = m_revindex[i]; + s << " rkc[" << ir << "] = "; + bool empty = true; + for (k = 0; k < nsp; k++) { + n = int(productStoichCoeff(k,ir)); + for (nn = 0; nn != n; nn++) { + if (!empty) s << "*"; + s << "a[" << k << "]"; + empty = false; + } + } + if (m_dn[i] < 0.0) { + n = -m_dn[i]; + for (nn = 0; nn < n; nn++) s << "*exp_c0"; + } + s << "/("; + empty = true; + for (k = 0; k < nsp; k++) { + n = int(reactantStoichCoeff(k,ir)); + for (nn = 0; nn < n; nn++) { + if (!empty) s << "*"; + s << "a[" << k << "]"; + empty = false; + } + } + if (m_dn[i] > 0.0) { + n = m_dn[i]; + for (nn = 0; nn != n; nn++) s << "*exp_c0"; + } + s << ");" << endl; + } + s << "}" << endl; + } + + void writeEvalRopnet(ostream& s) { + int i; + s << "void eval_ropnet(const double* c, " + "const double* rf, const double* rkc, double* r) {" << endl; + for (i = 0; i < m_ii; i++) { + s << " r[" << i << "] = rf[" << i << "] * (" + << m_reactantWriter.mult(i); + if (isReversible(i)) { + s << " - rkc[" << i << "] * " + << m_revProductWriter.mult(i); + } + s << ");" << endl; + } + s << "}" << endl; + } + + + + void writeUpdateRates(ostream& s) { + s << "void update_rates(double t, double tlog, double* rf) {" << endl; + s << " double rt = 1.0/t;" << endl; + m_rates.writeUpdate(s, "rf"); + s << "}" << endl; + } + + /// Add a reaction to the mechanism. + void addReaction(const ReactionData& r); + + protected: + + int m_kk, m_ii, m_nfall, m_nrev, m_nirrev; + + vector_int m_fallindx; + + Rate1 m_falloff_low_rates; + Rate1 m_falloff_high_rates; + Rate1 m_rates; + + vector m_irrev; + + StoichWriter m_reactantWriter; + StoichWriter m_revProductWriter; + StoichWriter m_irrevProductWriter; + + mutable vector > m_rrxn; + mutable vector > m_prxn; + + vector_int m_dn; + vector_int m_revindex; + + private: + + int reactionNumber(){ return m_ii;} + void addElementaryReaction(const ReactionData& r); + void addThreeBodyReaction(const ReactionData& r); + void addFalloffReaction(const ReactionData& r); + + void installReagents(const vector_int& r, + const vector_int& p, bool reversible); + + virtual bool isReversible(int i) { + if (find(m_revindex.begin(), m_revindex.end(), i) + < m_revindex.end()) return true; + else return false; + } + bool m_finalized; + }; +} + +#endif diff --git a/Cantera/src/kinetics/Group.cpp b/Cantera/src/kinetics/Group.cpp new file mode 100755 index 000000000..e86317197 --- /dev/null +++ b/Cantera/src/kinetics/Group.cpp @@ -0,0 +1,85 @@ +/** + * @file Group.cpp + * + * Implementation file for the Group class used in reaction path analysis. + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +// reaction path analysis support + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include +#include "Group.h" +#include + +namespace Cantera { + + /** + * A group is 'valid' if all of its nonzero atom numbers have + * the same sign, either positive or negative. This method + * checks for this, and if the group is not valid it sets + * m_sign to -999, and sets all atom numbers to zero. + */ + void Group::validate() { + + int n = m_comp.size(); + + // if already checked and not valid, return + if (m_sign == -999) return; + + m_sign = 0; + bool ok = true; + for (int m = 0; m < n; m++) + { + if (m_comp[m] != 0) + { + if (m_sign == 0) { + m_sign = m_comp[m]/abs(m_comp[m]); + } + else if (m_sign * m_comp[m] < 0) { + ok = false; break; + } + } + } + if (!ok) { m_sign = -999; m_comp.resize(n,0); } + } + + std::ostream& Group::fmt(std::ostream& s, + const std::vector& esymbols) const { + s << "("; + int nm; + bool first = true; + int n = m_comp.size(); + for (int m = 0; m < n; m++) { + nm = m_comp[m]; + if (nm != 0) { + if (!first) s << "-"; + s << esymbols[m]; + if (nm != 1) s << nm; + first = false; + } + } + s << ")"; + return s; + } + + std::ostream& operator<<(std::ostream& s, const Cantera::Group& g) { + if (g.valid()) { + s << g.m_comp; + } else { + s << ""; + } + return s; + } + +} diff --git a/Cantera/src/kinetics/Group.h b/Cantera/src/kinetics/Group.h new file mode 100755 index 000000000..82e13b74f --- /dev/null +++ b/Cantera/src/kinetics/Group.h @@ -0,0 +1,131 @@ +/** + * @file Group.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + + +// Copyright 2001 California Institute of Technology + +#ifndef CT_RXNPATH_GROUP +#define CT_RXNPATH_GROUP + +#include "ct_defs.h" + +//using namespace std; +namespace Cantera { + + /** + * Class Group is an internal class used by class ReactionPath. It + * represents some subset of the atoms of a molecule. + */ + class Group { + public: + Group() : m_sign(-999) { } + Group(int n) : m_sign(0) { m_comp.resize(n,0);} + Group(const vector_int& elnumbers) : + m_comp(elnumbers), m_sign(0) { + validate(); + } + Group(const Group& g) : + m_comp(g.m_comp), m_sign(g.m_sign) { } + Group& operator=(const Group& g) { + if (&g != this) { + m_comp = g.m_comp; + m_sign = g.m_sign; + } + return *this; + } + virtual ~Group(){} + + /** + * Decrement the atom numbers by those in group 'other'. + */ + void operator-=(const Group& other) { + verifyInputs(*this, other); + int n = m_comp.size(); + for (int m = 0; m < n; m++) + m_comp[m] -= other.m_comp[m]; + validate(); + } + void operator+=(const Group& other) { + verifyInputs(*this, other); + int n = m_comp.size(); + for (int m = 0; m < n; m++) + m_comp[m] += other.m_comp[m]; + validate(); + } + void operator*=(int a) { + int n = m_comp.size(); + for (int m = 0; m < n; m++) + m_comp[m] *= a; + validate(); + } + bool operator==(const Group& other) const { + verifyInputs(*this, other); + int n = m_comp.size(); + for (int m = 0; m < n; m++) { + if (m_comp[m] != other.m_comp[m]) return false; + } + return true; + } + friend Group operator-(const Group& g1, const Group& g2) { + verifyInputs(g1, g2); + Group diff(g1); + diff -= g2; + return diff; + } + friend Group operator+(const Group& g1, const Group& g2) { + verifyInputs(g1, g2); + Group sum(g1); + sum += g2; + return sum; + } + friend void verifyInputs(const Group& g1, const Group& g2) { +// if (Debug::on) { +// if (g1.size() != g2.size()) { +// cerr << "Group: size mismatch!" << std::endl; +// cerr << " group 1 = " << g1 << std::endl; +// cerr << " group 2 = " << g2 << std::endl; +// } +// } + } + + void validate(); + + /** + * True if all non-zero atom numbers have the same sign. + */ + bool valid() const { return (m_sign != -999); } + bool operator!() const { return (m_sign == -999); } + int sign() const { return m_sign; } + int size() const { return m_comp.size(); } + + /// Number of atoms in the group (>= 0) + int nAtoms() const { + int n = m_comp.size(); + int sum = 0; + for (int m = 0; m < n; m++) sum += std::abs(m_comp[m]); + return sum; + } + /// Number of atoms of element m (positive or negative) + int nAtoms(int m) const { + if (m_comp.empty()) return 0; + return m_comp[m]; + } + + std::ostream& fmt(std::ostream& s, const std::vector& esymbols) const; + + friend std::ostream& operator<<(std::ostream& s, + const Group& g); + + private: + vector_int m_comp; + int m_sign; + }; + +} + +#endif diff --git a/Cantera/src/kinetics/ImplicitChem.cpp b/Cantera/src/kinetics/ImplicitChem.cpp new file mode 100755 index 000000000..5a05e2f76 --- /dev/null +++ b/Cantera/src/kinetics/ImplicitChem.cpp @@ -0,0 +1,89 @@ +/** + * @file ImplicitChem.cpp + */ + +/* $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ImplicitChem.h" +#include "Integrator.h" + +namespace Cantera { + + ImplicitChem::ImplicitChem(Kinetics& kin, ThermoPhase& therm) + : FuncEval(), m_kin(&kin), m_thermo(&therm), m_integ(0), + m_atol(1.e-15), m_rtol(1.e-7), m_maxstep(0.0), m_energy(false) + { + m_integ = newIntegrator("CVODE"); //CVodeInt; + //m_mix = &kin.phase(); + m_wt = m_thermo->molecularWeights(); + + // use backward differencing, with a full Jacobian computed + // numerically, and use a Newton linear iterator + m_integ->setMethod(BDF_Method); + m_integ->setProblemType(DENSE + NOJAC); + m_integ->setIterator(Newton_Iter); + m_nsp = m_thermo->nSpecies(); + } + + // overloaded method of FuncEval. Called by the integrator to + // get the initial conditions. + void ImplicitChem::getInitialConditions(double t0, size_t leny, double* y) + { + m_thermo->getMassFractions(y); + m_h0 = m_thermo->enthalpy_mass(); + m_rho = m_thermo->density(); + m_press = m_thermo->pressure(); + } + + + /** + * Must be called before calling method 'advance' + */ + void ImplicitChem::initialize(doublereal t0) { + m_integ->setTolerances(m_rtol, m_atol); + // m_integ->setMaxStep(m_maxstep); + m_integ->initialize(t0, *this); + } + + + void ImplicitChem::updateState(doublereal* y) { + m_thermo->setMassFractions(y); + if (m_energy) { + doublereal delta, temp = m_thermo->temperature(); + do { + delta = -(m_thermo->enthalpy_mass() - m_h0)/m_thermo->cp_mass(); + temp += delta; + m_thermo->setTemperature(temp); + } + while (fabs(delta) > 1.e-7); + } + m_thermo->setPressure(m_press); + } + + /** + * Called by the integrator to evaluate ydot given y at time 'time'. + */ + void ImplicitChem::eval(doublereal time, doublereal* y, + doublereal* ydot, doublereal* p) + { + updateState(y); // synchronize the mixture state with y + m_thermo->setPressure(m_press); + m_kin->getNetProductionRates(ydot); // "omega dot" + int k; + for (k = 0; k < m_nsp; k++) { + ydot[k] *= m_wt[k]/m_rho; + } + } + +} diff --git a/Cantera/src/kinetics/ImplicitChem.h b/Cantera/src/kinetics/ImplicitChem.h new file mode 100755 index 000000000..96180bdee --- /dev/null +++ b/Cantera/src/kinetics/ImplicitChem.h @@ -0,0 +1,118 @@ +/** + * @file ImplicitChem.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + +#ifndef CT_IMPCHEM_H +#define CT_IMPCHEM_H + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "FuncEval.h" +#include "Integrator.h" +#include "Kinetics.h" +#include "ThermoPhase.h" + +namespace Cantera { + + /** + * Advances the composition of an associated phase object in time + * by implicitly integrating + * \f[ + * \dot Y_k = \frac{\omega_k}{\rho} + * \f] + */ + class ImplicitChem : public FuncEval { + + public: + + /** + * Constructor. + */ + ImplicitChem(Kinetics& kin, ThermoPhase& therm); + + + /** + * Destructor. Deletes the integrator. + */ + virtual ~ImplicitChem(){ delete m_integ; } + + + /** + * Overloads the virtual function + * declared in FuncEval. + */ + virtual void initialize(doublereal t0 = 0.0); + + void adiabatic() { + m_energy = true; + } + + void isothermal() { + m_energy = false; + } + + /** + * Integrate from t0 to t1. The integrator is reinitialized + * first. + */ + void integrate(doublereal t0, doublereal t1) { + m_integ->reinitialize(t0, *this); + m_integ->setMaxStepSize(t1 - t0); + m_rho = m_thermo->density(); + m_integ->integrate(t1); + updateState(m_integ->solution()); + } + + /** + * Integrate from t0 to t1 without reinitializing the + * integrator. + */ + void integrate0(doublereal t0, doublereal t1) { + m_integ->integrate(t1); + updateState(m_integ->solution()); + } + + // overloaded methods of class FuncEval + virtual int neq() { return m_nsp; } + virtual void eval(doublereal t, doublereal* y, doublereal* ydot, + doublereal* p); + virtual void getInitialConditions(doublereal t0, size_t leny, + doublereal* y); + + + protected: + + /** + * Set the mixture to a state consistent with solution + * vector y. + */ + void updateState(doublereal* y); + + //Kinetics::phase_t* m_mix; + Kinetics* m_kin; + ThermoPhase* m_thermo; + int m_nsp; + Integrator* m_integ; // pointer to integrator + doublereal m_atol, m_rtol; // tolerances + doublereal m_maxstep; // max step size + array_fp m_wt; + doublereal m_rho; + bool m_energy; + doublereal m_h0; + doublereal m_press; + + private: + + }; +} + +#endif diff --git a/Cantera/src/kinetics/ImplicitSurfChem.cpp b/Cantera/src/kinetics/ImplicitSurfChem.cpp new file mode 100755 index 000000000..82991afc8 --- /dev/null +++ b/Cantera/src/kinetics/ImplicitSurfChem.cpp @@ -0,0 +1,114 @@ +/** + * @file ImplicitSurfChem.cpp + * + * Implicit integration of surface site density equations + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ImplicitSurfChem.h" +#include "Integrator.h" + +using namespace std; + +namespace Cantera { + + ImplicitSurfChem::ImplicitSurfChem(vector k) + : FuncEval(), m_nv(0), m_integ(0), + m_atol(1.e-14), m_rtol(1.e-7), m_maxstep(0.0) + { + m_nsurf = static_cast(k.size()); + int ns; + int nt, ntmax = 0; + for (int n = 0; n < m_nsurf; n++) { + m_kin.push_back(k[n]); + ns = k[n]->surfacePhaseIndex(); + if (ns < 0) + throw CanteraError("ImplicitSurfChem", + "kinetics manager contains no surface phase"); + m_surfindex.push_back(ns); + m_surf.push_back((SurfPhase*)&k[n]->thermo(ns)); + m_nsp.push_back(m_surf.back()->nSpecies()); + m_nv += m_nsp.back(); + nt = k[n]->nTotalSpecies(); + if (nt > ntmax) ntmax = nt; + } + m_integ = newIntegrator("CVODE");// CVodeInt; + + // use backward differencing, with a full Jacobian computed + // numerically, and use a Newton linear iterator + + m_integ->setMethod(BDF_Method); + m_integ->setProblemType(DENSE + NOJAC); + m_integ->setIterator(Newton_Iter); + m_work.resize(ntmax); + } + + + // overloaded method of FuncEval. Called by the integrator to + // get the initial conditions. + void ImplicitSurfChem::getInitialConditions(double t0, size_t lenc, + double* c) + { + int loc = 0; + for (int n = 0; n < m_nsurf; n++) { + m_surf[n]->getCoverages(c + loc); + loc += m_nsp[n]; + } + } + + + /** + * Must be called before calling method 'advance' + */ + void ImplicitSurfChem::initialize(doublereal t0) { + m_integ->setTolerances(m_rtol, m_atol); + m_integ->initialize(t0, *this); + } + + + void ImplicitSurfChem::updateState(doublereal* c) { + int loc = 0; + for (int n = 0; n < m_nsurf; n++) { + m_surf[n]->setCoverages(c + loc); + loc += m_nsp[n]; + } + } + + + /** + * Called by the integrator to evaluate ydot given y at time 'time'. + */ + void ImplicitSurfChem::eval(doublereal time, doublereal* y, + doublereal* ydot, doublereal* p) + { + int n; + updateState(y); // synchronize the surface state(s) with y + doublereal rs0, sum; + int loc, k, kstart; + for (n = 0; n < m_nsurf; n++) { + rs0 = 1.0/m_surf[n]->siteDensity(); + m_kin[n]->getNetProductionRates(DATA_PTR(m_work)); + kstart = m_kin[n]->kineticsSpeciesIndex(0,m_surfindex[n]); + sum = 0.0; + loc = 0; + for (k = 1; k < m_nsp[n]; k++) { + ydot[k + loc] = m_work[kstart + k] * rs0 * m_surf[n]->size(k); + sum -= ydot[k]; + } + ydot[loc] = sum; + loc += m_nsp[n]; + } + } + +} diff --git a/Cantera/src/kinetics/ImplicitSurfChem.h b/Cantera/src/kinetics/ImplicitSurfChem.h new file mode 100755 index 000000000..b8f4fa417 --- /dev/null +++ b/Cantera/src/kinetics/ImplicitSurfChem.h @@ -0,0 +1,117 @@ +/** + * @file ImplicitSurfChem.h + * + * Implicit integration of surface site density equations. + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_IMPSURFCHEM_H +#define CT_IMPSURFCHEM_H + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "FuncEval.h" +#include "Integrator.h" +#include "InterfaceKinetics.h" +#include "SurfPhase.h" + +namespace Cantera { + + /** + * Advances the surface coverages of an associated SurfacePhase + * object in time by implicitly integrating \f[ \dot \theta_k = + * \dot s_k (\sigma_k / s_0)\f] + */ + class ImplicitSurfChem : public FuncEval { + + public: + + /** + * Constructor. + */ + //ImplicitSurfChem(InterfaceKinetics& kin); + + /** + * Constructor for multiple surfaces. + */ + ImplicitSurfChem(std::vector k); + + /** + * Destructor. Deletes the integrator. + */ + virtual ~ImplicitSurfChem(){ delete m_integ; } + + + /** + * Overloads the virtual function + * declared in FuncEval. + */ + virtual void initialize(doublereal t0 = 0.0); + + + /** + * Integrate from t0 to t1. The integrator is reinitialized + * first. + */ + void integrate(doublereal t0, doublereal t1) { + m_integ->initialize(t0, *this); + m_integ->setMaxStepSize(t1 - t0); + m_integ->integrate(t1); + updateState(m_integ->solution()); + } + + /** + * Integrate from t0 to t1 without reinitializing the + * integrator. Use when the coverages have not changed from + * their values on return from the last call to integrate or + * integrate0. + */ + void integrate0(doublereal t0, doublereal t1) { + m_integ->integrate(t1); + updateState(m_integ->solution()); + } + + // overloaded methods of class FuncEval + virtual int neq() { return m_nv; } + virtual void eval(doublereal t, doublereal* y, doublereal* ydot, + doublereal* p); + virtual void getInitialConditions(doublereal t0, + size_t leny, doublereal* y); + + + protected: + + /** + * Set the mixture to a state consistent with solution + * vector y. + */ + void updateState(doublereal* y); + + std::vector m_surf; + std::vector m_kin; + vector_int m_nsp; + vector_int m_surfindex; + int m_nsurf; + int m_nv; + //int m_nsp, m_surfindex; + Integrator* m_integ; // pointer to integrator + doublereal m_atol, m_rtol; // tolerances + doublereal m_maxstep; // max step size + vector_fp m_work; + + private: + + }; +} + +#endif + diff --git a/Cantera/src/kinetics/InterfaceKinetics.cpp b/Cantera/src/kinetics/InterfaceKinetics.cpp new file mode 100644 index 000000000..3e439c59e --- /dev/null +++ b/Cantera/src/kinetics/InterfaceKinetics.cpp @@ -0,0 +1,836 @@ +/** + * @file InterfaceKinetics.cpp + * + */ + +// Copyright 2002 California Institute of Technology + + +// turn off warnings under Windows +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "InterfaceKinetics.h" +#include "SurfPhase.h" + +#include "ReactionData.h" +#include "RateCoeffMgr.h" + +#include "ImplicitSurfChem.h" + +using namespace std; + +namespace Cantera { + + ////////////////////////////////////////////////////////////////// + + /** + * Construct an empty InterfaceKinetics reaction mechanism. + * @param thermo This is an optional parameter that may be + * used to initialize the inherited Kinetics class with + * one ThermoPhase class object -> in other words it's + * useful for initialization of homogeneous kinetics + * mechanisms. + */ + InterfaceKinetics:: + InterfaceKinetics(thermo_t* thermo) : + Kinetics(), + m_kk(0), + m_redo_rates(false), + m_nirrev(0), + m_nrev(0), + m_surf(0), + m_integrator(0), + m_finalized(false), + m_has_coverage_dependence(false) + { + if (thermo != 0) addPhase(*thermo); + m_kdata = new InterfaceKineticsData; + m_kdata->m_temp = 0.0; + } + + /** + * Destructor + */ + InterfaceKinetics:: + ~InterfaceKinetics(){ + delete m_kdata; + delete m_integrator; + } + + + /** + * Update properties that depend on temperature + * + */ + void InterfaceKinetics:: + _update_rates_T() { + _update_rates_phi(); + if (m_has_coverage_dependence) { + m_surf->getCoverages(DATA_PTR(m_conc)); + m_rates.update_C(DATA_PTR(m_conc)); + m_redo_rates = true; + } + doublereal T = thermo(surfacePhaseIndex()).temperature(); + if (T != m_kdata->m_temp || m_redo_rates) { + m_kdata->m_logtemp = log(T); + m_rates.update(T, m_kdata->m_logtemp, DATA_PTR(m_kdata->m_rfn)); + applyButlerVolmerCorrection(DATA_PTR(m_kdata->m_rfn)); + m_kdata->m_temp = T; + updateKc(); + m_kdata->m_ROP_ok = false; + m_redo_rates = false; + } + } + + void InterfaceKinetics:: + _update_rates_phi() { + int np = nPhases(); + for (int n = 0; n < np; n++) { + if (thermo(n).electricPotential() != m_phi[n]) { + m_phi[n] = thermo(n).electricPotential(); + m_redo_rates = true; + } + } + } + + + /** + * Update properties that depend on concentrations. This method + * fills out the array of generalized concentrations by calling + * method getActivityConcentrations for each phase, which classes + * representing phases should overload to return the appropriate + * quantities. + */ + void InterfaceKinetics:: + _update_rates_C() { + int n; + + int np = nPhases(); + for (n = 0; n < np; n++) { + /* + * We call the getActivityConcentrations function of each + * ThermoPhase class that makes up this kinetics object to + * obtain the generalized concentrations for species within that + * class. This is collected in the vector m_conc. m_start[] + * are integer indecises for that vector denoting the start of the + * species for each phase. + */ + thermo(n).getActivityConcentrations(DATA_PTR(m_conc) + m_start[n]); + } + m_kdata->m_ROP_ok = false; + } + + + /** + * Update the equilibrium constants in molar units for all + * reversible reactions. Irreversible reactions have their + * equilibrium constant set to zero. + */ + void InterfaceKinetics::updateKc() { + int i, irxn; + + vector_fp& m_rkc = m_kdata->m_rkcn; + fill(m_rkc.begin(), m_rkc.end(), 0.0); + + //static vector_fp mu(nTotalSpecies()); + if (m_nrev > 0) { + + int n, nsp, k, ik=0; + doublereal rt = GasConstant*thermo(0).temperature(); + doublereal rrt = 1.0/rt; + int np = nPhases(); + for (n = 0; n < np; n++) { + thermo(n).getStandardChemPotentials(DATA_PTR(m_mu0) + m_start[n]); + nsp = thermo(n).nSpecies(); + for (k = 0; k < nsp; k++) { + m_mu0[ik] -= rt*thermo(n).logStandardConc(k); + m_mu0[ik] += Faraday * m_phi[n] * thermo(n).charge(k); + ik++; + } + } + + // compute Delta mu^0 for all reversible reactions + //m_reactantStoich.decrementReactions(m_mu0.begin(), m_rkc.begin()); + //m_revProductStoich.incrementReactions(m_mu0.begin(), m_rkc.begin()); + m_rxnstoich.getRevReactionDelta(m_ii, DATA_PTR(m_mu0), + DATA_PTR(m_rkc)); + + for (i = 0; i < m_nrev; i++) { + irxn = m_revindex[i]; + if (irxn < 0 || irxn >= nReactions()) { + throw CanteraError("InterfaceKinetics","illegal value: irxn = "+int2str(irxn)); + } + m_rkc[irxn] = exp(m_rkc[irxn]*rrt); + } + for (i = 0; i != m_nirrev; ++i) { + m_rkc[ m_irrev[i] ] = 0.0; + } + } + } + + + void InterfaceKinetics::checkPartialEquil() { + int i, irxn; + vector_fp dmu(nTotalSpecies(), 0.0); + vector_fp rmu(nReactions(), 0.0); + vector_fp frop(nReactions(), 0.0); + vector_fp rrop(nReactions(), 0.0); + vector_fp netrop(nReactions(), 0.0); + if (m_nrev > 0) { + doublereal rt = GasConstant*thermo(0).temperature(); + cout << "T = " << thermo(0).temperature() << " " << rt << endl; + int n, nsp, k, ik=0; + //doublereal rt = GasConstant*thermo(0).temperature(); + // doublereal rrt = 1.0/rt; + int np = nPhases(); + doublereal delta; + for (n = 0; n < np; n++) { + thermo(n).getChemPotentials(DATA_PTR(dmu) + m_start[n]); + nsp = thermo(n).nSpecies(); + for (k = 0; k < nsp; k++) { + delta = Faraday * m_phi[n] * thermo(n).charge(k); + cout << thermo(n).speciesName(k) << " " << (delta+dmu[ik])/rt << " " << dmu[ik]/rt << endl; + dmu[ik] += delta; + ik++; + } + } + + // compute Delta mu^ for all reversible reactions + m_rxnstoich.getRevReactionDelta(m_ii, DATA_PTR(dmu), DATA_PTR(rmu)); + getFwdRatesOfProgress(DATA_PTR(frop)); + getRevRatesOfProgress(DATA_PTR(rrop)); + getNetRatesOfProgress(DATA_PTR(netrop)); + for (i = 0; i < m_nrev; i++) { + irxn = m_revindex[i]; + cout << "Reaction " << reactionString(irxn) + << " " << rmu[irxn]/rt << endl; + printf("%12.6e %12.6e %12.6e %12.6e \n", + frop[irxn], rrop[irxn], netrop[irxn], + netrop[irxn]/(frop[irxn] + rrop[irxn])); + } + } + } + + + /** + * Get the equilibrium constants of all reactions, whether + * reversible or not. + */ + void InterfaceKinetics::getEquilibriumConstants(doublereal* kc) { + int i; + + int n, nsp, k, ik=0; + doublereal rt = GasConstant*thermo(0).temperature(); + doublereal rrt = 1.0/rt; + int np = nPhases(); + for (n = 0; n < np; n++) { + thermo(n).getStandardChemPotentials(DATA_PTR(m_mu0) + m_start[n]); + nsp = thermo(n).nSpecies(); + for (k = 0; k < nsp; k++) { + m_mu0[ik] -= rt*thermo(n).logStandardConc(k); + m_mu0[ik] += Faraday * m_phi[n] * thermo(n).charge(k); + ik++; + } + } + + fill(kc, kc + m_ii, 0.0); + + //m_reactantStoich.decrementReactions(m_mu0.begin(), kc); + //m_revProductStoich.incrementReactions(m_mu0.begin(), kc); + //m_irrevProductStoich.incrementReactions(m_mu0.begin(), kc); + m_rxnstoich.getReactionDelta(m_ii, DATA_PTR(m_mu0), kc); + + for (i = 0; i < m_ii; i++) { + kc[i] = exp(-kc[i]*rrt); + } + } + + + /** + * For reactions that transfer charge across a potential difference, + * the activation energies are modified by the potential difference. + * (see, for example, ...). This method applies this correction. + */ + void InterfaceKinetics::applyButlerVolmerCorrection(doublereal* kf) { + int i; + + int n, nsp, k, ik=0; + doublereal rt = GasConstant*thermo(0).temperature(); + doublereal rrt = 1.0/rt; + int np = nPhases(); + + // compute the electrical potential energy of each species + for (n = 0; n < np; n++) { + nsp = thermo(n).nSpecies(); + for (k = 0; k < nsp; k++) { + m_pot[ik] = Faraday*thermo(n).charge(k)*m_phi[n]; + ik++; + } + } + + // compute the change in electrical potential energy for each + // reaction. This will only be non-zero if a potential + // difference is present. + //fill(m_rwork.begin(), m_rwork.begin() + m_ii, 0.0); + //m_reactantStoich.decrementReactions(m_pot.begin(), m_rwork.begin()); + //m_revProductStoich.incrementReactions(m_pot.begin(), m_rwork.begin()); + //m_irrevProductStoich.incrementReactions(m_pot.begin(), m_rwork.begin()); + m_rxnstoich.getReactionDelta(m_ii, DATA_PTR(m_pot), + DATA_PTR(m_rwork)); + + // modify the reaction rates. Only modify those with a + // non-zero activation energy, and do not decrease the + // activation energy below zero. + doublereal ea, eamod; + + for (i = 0; i < m_ii; i++) { + eamod = 0.5*m_rwork[i]; + if (eamod != 0.0 && m_E[i] != 0.0) { + ea = GasConstant * m_E[i]; + if (eamod + ea < 0.0) { + eamod = -ea; + writelog("warning: modified E < 0.\n"); + } + kf[i] *= exp(-eamod*rrt); +// if (kf[i] == 0.0) { +// for (n = 0; n < np; n++) { +// cout << "phi " << n << " " << thermo(n).electricPotential() << " " << m_phi[n] << endl; +// } +// cout << "Zero rate coeff." << endl; +// cout << "eamod = " << eamod << " " << eamod*rrt << endl; +// cout << eamod/Faraday << endl; +// } + } + } + } + + + + + /** + * Update the rates of progress of the reactions in the reaciton + * mechanism. This routine operates on internal data. + */ + void InterfaceKinetics::getFwdRateConstants(doublereal* kfwd) { + + _update_rates_T(); + _update_rates_C(); + + const vector_fp& rf = m_kdata->m_rfn; + + // copy rate coefficients into kfwd + copy(rf.begin(), rf.end(), kfwd); + + // multiply by perturbation factor + multiply_each(kfwd, kfwd + nReactions(), m_perturb.begin()); + + } + + + /** + * Update the rates of progress of the reactions in the reaciton + * mechanism. This routine operates on internal data. + */ + void InterfaceKinetics::getRevRateConstants(doublereal* krev, bool doIrreversible) { + getFwdRateConstants(krev); + if (doIrreversible) { + doublereal *tmpKc = DATA_PTR(m_kdata->m_ropnet); + getEquilibriumConstants(tmpKc); + for (int i = 0; i < m_ii; i++) { + krev[i] /= tmpKc[i]; + } + } + else { + const vector_fp& rkc = m_kdata->m_rkcn; + multiply_each(krev, krev + nReactions(), rkc.begin()); + } + } + + + void InterfaceKinetics::getActivationEnergies(doublereal *E) { + copy(m_E.begin(), m_E.end(), E); + } + + /** + * Update the rates of progress of the reactions in the reaciton + * mechanism. This routine operates on internal data. + */ + void InterfaceKinetics::updateROP() { + + _update_rates_T(); + _update_rates_C(); + + if (m_kdata->m_ROP_ok) return; + + const vector_fp& rf = m_kdata->m_rfn; + const vector_fp& m_rkc = m_kdata->m_rkcn; + array_fp& ropf = m_kdata->m_ropf; + array_fp& ropr = m_kdata->m_ropr; + array_fp& ropnet = m_kdata->m_ropnet; + + // copy rate coefficients into ropf + copy(rf.begin(), rf.end(), ropf.begin()); + + // multiply by perturbation factor + multiply_each(ropf.begin(), ropf.end(), m_perturb.begin()); + + // copy the forward rates to the reverse rates + copy(ropf.begin(), ropf.end(), ropr.begin()); + + // for reverse rates computed from thermochemistry, multiply + // the forward rates copied into m_ropr by the reciprocals of + // the equilibrium constants + multiply_each(ropr.begin(), ropr.end(), m_rkc.begin()); + + // multiply ropf by concentration products + m_rxnstoich.multiplyReactants(DATA_PTR(m_conc), DATA_PTR(ropf)); + //m_reactantStoich.multiply(m_conc.begin(), ropf.begin()); + + // for reversible reactions, multiply ropr by concentration + // products + m_rxnstoich.multiplyRevProducts(DATA_PTR(m_conc), + DATA_PTR(ropr)); + //m_revProductStoich.multiply(m_conc.begin(), ropr.begin()); + + // do global reactions + //m_globalReactantStoich.power(m_conc.begin(), ropf.begin()); + + for (int j = 0; j != m_ii; ++j) { + ropnet[j] = ropf[j] - ropr[j]; + } + + m_kdata->m_ROP_ok = true; + } + + + /** + * + * getDeltaGibbs(): + * + * Return the vector of values for the reaction gibbs free energy + * change + * These values depend upon the concentration + * of the ideal gas. + * + * units = J kmol-1 + */ + void InterfaceKinetics::getDeltaGibbs(doublereal* deltaG) { + /* + * Get the chemical potentials of the species in the + * ideal gas solution. + */ + int np = nPhases(); + int n; + for (n = 0; n < np; n++) { + thermo(n).getChemPotentials(DATA_PTR(m_grt) + m_start[n]); + } + /* + * Use the stoichiometric manager to find deltaG for each + * reaction. + */ + m_rxnstoich.getReactionDelta(m_ii, DATA_PTR(m_grt), deltaG); + } + + /** + * + * getDeltaEnthalpy(): + * + * Return the vector of values for the reactions change in + * enthalpy. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + void InterfaceKinetics::getDeltaEnthalpy(doublereal* deltaH) { + /* + * Get the partial molar enthalpy of all species in the + * ideal gas. + */ + int np = nPhases(); + int n; + for (n = 0; n < np; n++) { + thermo(n).getPartialMolarEnthalpies(DATA_PTR(m_grt) + m_start[n]); + } + /* + * Use the stoichiometric manager to find deltaG for each + * reaction. + */ + m_rxnstoich.getReactionDelta(m_ii, DATA_PTR(m_grt), deltaH); + } + + /************************************************************************ + * + * getDeltaEntropy(): + * + * Return the vector of values for the reactions change in + * entropy. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 Kelvin-1 + */ + void InterfaceKinetics::getDeltaEntropy( doublereal* deltaS) { + /* + * Get the partial molar entropy of all species in the + * solid solution. + */ + int np = nPhases(); + int n; + for (n = 0; n < np; n++) { + thermo(n).getPartialMolarEntropies(DATA_PTR(m_grt) + m_start[n]); + } + /* + * Use the stoichiometric manager to find deltaS for each + * reaction. + */ + m_rxnstoich.getReactionDelta(m_ii, DATA_PTR(m_grt), deltaS); + } + + /** + * + * getDeltaSSGibbs(): + * + * Return the vector of values for the reaction + * standard state gibbs free energy change. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + void InterfaceKinetics::getDeltaSSGibbs(doublereal* deltaG) { + /* + * Get the standard state chemical potentials of the species. + * This is the array of chemical potentials at unit activity + * We define these here as the chemical potentials of the pure + * species at the temperature and pressure of the solution. + */ + int np = nPhases(); + int n; + for (n = 0; n < np; n++) { + thermo(n).getStandardChemPotentials(DATA_PTR(m_grt) + m_start[n]); + } + /* + * Use the stoichiometric manager to find deltaG for each + * reaction. + */ + m_rxnstoich.getReactionDelta(m_ii, DATA_PTR(m_grt), deltaG); + } + + /** + * + * getDeltaSSEnthalpy(): + * + * Return the vector of values for the change in the + * standard state enthalpies of reaction. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + void InterfaceKinetics::getDeltaSSEnthalpy(doublereal* deltaH) { + /* + * Get the standard state enthalpies of the species. + * This is the array of chemical potentials at unit activity + * We define these here as the enthalpies of the pure + * species at the temperature and pressure of the solution. + */ + int np = nPhases(); + int n; + for (n = 0; n < np; n++) { + thermo(n).getEnthalpy_RT(DATA_PTR(m_grt) + m_start[n]); + } + doublereal RT = thermo().temperature() * GasConstant; + for (int k = 0; k < m_kk; k++) { + m_grt[k] *= RT; + } + /* + * Use the stoichiometric manager to find deltaG for each + * reaction. + */ + m_rxnstoich.getReactionDelta(m_ii, DATA_PTR(m_grt), deltaH); + } + + /********************************************************************* + * + * getDeltaSSEntropy(): + * + * Return the vector of values for the change in the + * standard state entropies for each reaction. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 Kelvin-1 + */ + void InterfaceKinetics::getDeltaSSEntropy(doublereal* deltaS) { + /* + * Get the standard state entropy of the species. + * We define these here as the entropies of the pure + * species at the temperature and pressure of the solution. + */ + int np = nPhases(); + int n; + for (n = 0; n < np; n++) { + thermo(n).getEntropy_R(DATA_PTR(m_grt) + m_start[n]); + } + doublereal R = GasConstant; + for (int k = 0; k < m_kk; k++) { + m_grt[k] *= R; + } + /* + * Use the stoichiometric manager to find deltaS for each + * reaction. + */ + m_rxnstoich.getReactionDelta(m_ii, DATA_PTR(m_grt), deltaS); + } + + + /** + * Add a single reaction to the mechanism. This routine + * must be called after init() and before finalize(). + * This function branches on the types of reactions allowed + * by the interfaceKinetics manager in order to install + * the reaction correctly in the manager. + * The manager allows the following reaction types + * Elementary + * Surface + * Global + * There is no difference between elementary and surface + * reactions. + */ + void InterfaceKinetics:: + addReaction(const ReactionData& r) { + + addElementaryReaction(r); + + // operations common to all reaction types + installReagents( r ); + //installGroups(reactionNumber(), r.rgroups, r.pgroups); + incrementRxnCount(); + m_rxneqn.push_back(r.equation); + } + + + void InterfaceKinetics:: + addElementaryReaction(const ReactionData& r) { + int iloc; + // install rate coeff calculator + vector_fp rp = r.rateCoeffParameters; + int ncov = r.cov.size(); + if (ncov > 3) { + m_has_coverage_dependence = true; + } + for (int m = 0; m < ncov; m++) rp.push_back(r.cov[m]); + iloc = m_rates.install( reactionNumber(), + r.rateCoeffType, rp.size(), + DATA_PTR(rp) ); + // store activation energy + m_E.push_back(r.rateCoeffParameters[2]); + // add constant term to rate coeff value vector + m_kdata->m_rfn.push_back(r.rateCoeffParameters[0]); + registerReaction( reactionNumber(), ELEMENTARY_RXN, iloc); + } + + +// void InterfaceKinetics:: +// addGlobalReaction(const ReactionData& r) { + +// int iloc; +// // install rate coeff calculator +// vector_fp rp = r.rateCoeffParameters; +// int ncov = r.cov.size(); +// for (int m = 0; m < ncov; m++) rp.push_back(r.cov[m]); +// iloc = m_rates.install( reactionNumber(), +// r.rateCoeffType, rp.size(), +// rp.begin() ); +// // store activation energy +// m_E.push_back(r.rateCoeffParameters[2]); +// // add constant term to rate coeff value vector +// m_kdata->m_rfn.push_back(r.rateCoeffParameters[0]); + +// int nr = r.order.size(); +// vector_fp ordr(nr); +// for (int n = 0; n < nr; n++) { +// ordr[n] = r.order[n] - r.rstoich[n]; +// } +// m_globalReactantStoich.add( reactionNumber(), +// r.reactants, ordr); + +// registerReaction( reactionNumber(), GLOBAL_RXN, iloc); +// } + + + void InterfaceKinetics::installReagents(const ReactionData& r) { + + int n, ns, m; + doublereal nsFlt; + /* + * extend temporary storage by one for this rxn. + */ + m_kdata->m_ropf.push_back(0.0); + m_kdata->m_ropr.push_back(0.0); + m_kdata->m_ropnet.push_back(0.0); + m_kdata->m_rkcn.push_back(0.0); + + /* + * Obtain the current reaction index for the reaction that we + * are adding. The first reaction is labeled 0. + */ + int rnum = reactionNumber(); + + // vectors rk and pk are lists of species numbers, with + // repeated entries for species with stoichiometric + // coefficients > 1. This allows the reaction to be defined + // with unity reaction order for each reactant, and so the + // faster method 'multiply' can be used to compute the rate of + // progress instead of 'power'. + + vector_int rk; + int nr = r.reactants.size(); + for (n = 0; n < nr; n++) { + nsFlt = r.rstoich[n]; + ns = (int) nsFlt; + if ((doublereal) ns != nsFlt) { + if (ns < 1) ns = 1; + } + /* + * Add to m_rrxn. m_rrxn is a vector of maps. m_rrxn has a length + * equal to the total number of species for each species, there + * exists a map, with the reaction number being the key, and the + * reactant stoichiometric coefficient being the value. + */ + m_rrxn[r.reactants[n]][rnum] = ns; + for (m = 0; m < ns; m++) { + rk.push_back(r.reactants[n]); + } + } + /* + * Now that we have rk[], we add it into the vector m_reactants + * in the rnum index spot. Thus m_reactants[rnum] yields a vector + * of reactants for the rnum'th reaction + */ + m_reactants.push_back(rk); + + vector_int pk; + int np = r.products.size(); + for (n = 0; n < np; n++) { + nsFlt = r.pstoich[n]; + ns = (int) nsFlt; + if ((doublereal) ns != nsFlt) { + if (ns < 1) ns = 1; + } + /* + * Add to m_prxn. m_prxn is a vector of maps. m_prxn has a length + * equal to the total number of species for each species, there + * exists a map, with the reaction number being the key, and the + * product stoichiometric coefficient being the value. + */ + m_prxn[r.products[n]][rnum] = ns; + for (m = 0; m < ns; m++) { + pk.push_back(r.products[n]); + } + } + /* + * Now that we have pk[], we add it into the vector m_products + * in the rnum index spot. Thus m_products[rnum] yields a vector + * of products for the rnum'th reaction + */ + m_products.push_back(pk); + /* + * Add this reaction to the stoichiometric coefficient manager. This + * calculates rates of species production from reaction rates of + * progress. + */ + m_rxnstoich.add( reactionNumber(), r); + /* + * register reaction in lists of reversible and irreversible rxns. + */ + if (r.reversible) { + m_revindex.push_back(reactionNumber()); + m_nrev++; + } else { + m_irrev.push_back( reactionNumber() ); + m_nirrev++; + } + } + + + //void InterfaceKinetics::installGroups(int irxn, + // const vector& r, const vector& p) { + // if (!r.empty()) { + // m_rgroups[reactionNumber()] = r; + // m_pgroups[reactionNumber()] = p; + // } + //} + + /** + * Prepare the class for the addition of reactions. This function + * must be called after instantiation of the class, but before + * any reactions are actually added to the mechanism. + * This function calculates m_kk the number of species in all + * phases participating in the reaction mechanism. We don't know + * m_kk previously, before all phases have been added. + */ + void InterfaceKinetics::init() { + int n; + m_kk = 0; + int np = nPhases(); + for (n = 0; n < np; n++) { + m_kk += thermo(n).nSpecies(); + } + m_rrxn.resize(m_kk); + m_prxn.resize(m_kk); + m_conc.resize(m_kk); + m_mu0.resize(m_kk); + m_grt.resize(m_kk); + m_pot.resize(m_kk, 0.0); + m_phi.resize(np, 0.0); + } + + /** + * Finish adding reactions and prepare for use. This function + * must be called after all reactions are entered into the mechanism + * and before the mechanism is used to calculate reaction rates. + * + * Here, we resize work arrays based on the number of reactions, + * since we don't know this number up to now. + */ + void InterfaceKinetics::finalize() { + m_rwork.resize(nReactions()); + int ks = surfacePhaseIndex(); + if (ks < 0) throw CanteraError("InterfaceKinetics::finalize", + "no surface phase is present."); + m_surf = (SurfPhase*)&thermo(ks); + m_finalized = true; + } + + + bool InterfaceKinetics::ready() const { + return (m_finalized); + } + + void InterfaceKinetics:: + advanceCoverages(doublereal tstep) { + if (m_integrator == 0) { + vector k; + k.push_back(this); + m_integrator = new ImplicitSurfChem(k); + m_integrator->initialize(); + } + m_integrator->integrate(0.0, tstep); + delete m_integrator; + m_integrator = 0; + } + +} + + + + + + + + diff --git a/Cantera/src/kinetics/InterfaceKinetics.h b/Cantera/src/kinetics/InterfaceKinetics.h new file mode 100644 index 000000000..c1cafc232 --- /dev/null +++ b/Cantera/src/kinetics/InterfaceKinetics.h @@ -0,0 +1,419 @@ +/** + * @file InterfaceKinetics.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_IFACEKINETICS_H +#define CT_IFACEKINETICS_H + +#include +#include +#include +#include + +#include "mix_defs.h" +#include "Kinetics.h" + +#include "utilities.h" +#include "RateCoeffMgr.h" +#include "ReactionStoichMgr.h" + +namespace Cantera { + + // forward references + + class ReactionData; + class InterfaceKineticsData; + class ThermoPhase; + class SurfPhase; + class ImplicitSurfChem; + + + /** + * Holds mechanism-specific data. + */ + class InterfaceKineticsData { + public: + InterfaceKineticsData() : + m_ROP_ok(false), + m_temp(0.0), m_logtemp(0.0) + {} + virtual ~InterfaceKineticsData(){} + + doublereal m_logp0, m_logc0; + array_fp m_ropf, m_ropr, m_ropnet; + //array_fp m_rfn_low, m_rfn_high; + bool m_ROP_ok; + + doublereal m_temp, m_logtemp; + vector_fp m_rfn; + vector_fp m_rkcn; + }; + + + /// + /// A kinetics manager for heterogeneous reaction mechanisms. The + /// reactions are assumed to occur at a 2D interface between two + /// 3D phases. + /// + class InterfaceKinetics : public Kinetics { + + public: + + /** + * Constructor + * + * @param thermo The optional parameter may be used to initialize + * the object with one ThermoPhase object. + * HKM Note -> Since the interface kinetics + * object will probably require multiple thermophase + * objects, this is probably not a good idea + * to have this parameter. + */ + InterfaceKinetics(thermo_t* thermo = 0); + + + /// Destructor. + virtual ~InterfaceKinetics(); + + virtual int ID() { return cInterfaceKinetics; } + virtual int type() { return cInterfaceKinetics; } + + /// + /// @name Reaction Rates Of Progress + /// + //@{ + + + virtual void getFwdRatesOfProgress(doublereal* fwdROP) { + updateROP(); + std::copy(m_kdata->m_ropf.begin(), m_kdata->m_ropf.end(), fwdROP); + } + + virtual void getRevRatesOfProgress(doublereal* revROP) { + updateROP(); + std::copy(m_kdata->m_ropr.begin(), m_kdata->m_ropr.end(), revROP); + } + + virtual void getNetRatesOfProgress(doublereal* netROP) { + updateROP(); + std::copy(m_kdata->m_ropnet.begin(), m_kdata->m_ropnet.end(), netROP); + } + + virtual void getEquilibriumConstants(doublereal* kc); + + + virtual void getDeltaGibbs( doublereal* deltaG); + + /** + * Return the vector of values for the reactions change in + * enthalpy. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + virtual void getDeltaEnthalpy( doublereal* deltaH); + + /** + * Return the vector of values for the reactions change in + * entropy. + * These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 Kelvin-1 + */ + virtual void getDeltaEntropy(doublereal* deltaS); + + /** + * Return the vector of values for the reaction + * standard state gibbs free energy change. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + virtual void getDeltaSSGibbs(doublereal* deltaG); + + /** + * Return the vector of values for the change in the + * standard state enthalpies of reaction. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 + */ + virtual void getDeltaSSEnthalpy(doublereal* deltaH); + + /** + * Return the vector of values for the change in the + * standard state entropies for each reaction. + * These values don't depend upon the concentration + * of the solution. + * + * units = J kmol-1 Kelvin-1 + */ + virtual void getDeltaSSEntropy(doublereal* deltaS); + + + //@} + /** + * @name Species Production Rates + */ + //@{ + + /** + * Species creation rates [kmol/m^2/s]. Return the species + * creation rates in array cdot, which must be + * dimensioned at least as large as the total number of + * species in all phases of the kinetics + * model + * + */ + virtual void getCreationRates(doublereal* cdot) { + updateROP(); + m_rxnstoich.getCreationRates(m_kk, &m_kdata->m_ropf[0], + &m_kdata->m_ropr[0], cdot); + } + + /** + * Species destruction rates [kmol/m^2/s]. Return the species + * destruction rates in array ddot, which must be + * dimensioned at least as large as the total number of + * species in all phases of the kinetics + * model + * + */ + virtual void getDestructionRates(doublereal* ddot) { + updateROP(); + m_rxnstoich.getDestructionRates(m_kk, &m_kdata->m_ropf[0], + &m_kdata->m_ropr[0], ddot); + } + + /** + * Species net production rates [kmol/m^2/s]. Return the species + * net production rates (creation - destruction) in array + * wdot, which must be dimensioned at least as large as the + * total number of species in all phases of the kinetics + * model + */ + virtual void getNetProductionRates(doublereal* net) { + updateROP(); + m_rxnstoich.getNetProductionRates(m_kk, + &m_kdata->m_ropnet[0], + net); + } + + //@} + /** + * @name Reaction Mechanism Informational Query Routines + */ + //@{ + + /** + * Stoichiometric coefficient of species k as a reactant in + * reaction i. + */ + virtual doublereal reactantStoichCoeff(int k, int i) const { + return m_rrxn[k][i]; + } + + /** + * Stoichiometric coefficient of species k as a product in + * reaction i. + */ + virtual doublereal productStoichCoeff(int k, int i) const { + return m_prxn[k][i]; + } + + /** + * Flag specifying the type of reaction. The legal values and + * their meaning are specific to the particular kinetics + * manager. + */ + virtual int reactionType(int i) const { + return m_index[i].first; + } + + /** + * True if reaction i has been declared to be reversible. If + * isReversible(i) is false, then the reverse rate of progress + * for reaction i is always zero. + */ + virtual bool isReversible(int i) { + if (std::find(m_revindex.begin(), m_revindex.end(), i) + < m_revindex.end()) return true; + else return false; + } + + /** + * Return a string representing the reaction. + */ + virtual std::string reactionString(int i) const { + return m_rxneqn[i]; + } + + + virtual void getFwdRateConstants(doublereal* kfwd); + virtual void getRevRateConstants(doublereal* krev, + bool doIrreversible = false); + virtual void getActivationEnergies(doublereal *E); + + //@} + /** + * @name Reaction Mechanism Construction + */ + //@{ + + /** + * Prepare the class for the addition of reactions. This function + * must be called after instantiation of the class, but before + * any reactions are actually added to the mechanism. + * This function calculates m_kk the number of species in all + * phases participating in the reaction mechanism. We don't know + * m_kk previously, before all phases have been added. + */ + virtual void init(); + + /** + * Add a single reaction to the mechanism. + */ + virtual void addReaction(const ReactionData& r); + + /** + * Finish adding reactions and prepare for use. This function + * must be called after all reactions are entered into the mechanism + * and before the mechanism is used to calculate reaction rates. + */ + virtual void finalize(); + virtual bool ready() const; + + + void updateROP(); + + + //const std::vector& reactantGroups(int i) + // { return m_rgroups[i]; } + //const std::vector& productGroups(int i) + // { return m_pgroups[i]; } + + void _update_rates_T(); + void _update_rates_phi(); + void _update_rates_C(); + + void advanceCoverages(doublereal tstep); + void checkPartialEquil(); + vector_fp m_grt; + + protected: + + /** + * m_kk here is the number of species in all of the phases + * that participate in the kinetics mechanism. + */ + int m_kk; + vector_int m_revindex; + + Rate1 m_rates; + bool m_redo_rates; + + /** + * Vector of information about reactions in the + * mechanism. + * The key is the reaction index (0 < i < m_ii). + * The first pair is the reactionType of the reaction. + * The second pair is ... + */ + mutable std::map > m_index; + + std::vector m_irrev; + + ReactionStoichMgr m_rxnstoich; + + int m_nirrev; + + /** + * Number of reversible reactions in the mechanism + */ + int m_nrev; + + std::vector m_rxntype; + + /** + * m_rrxn is a vector of maps. m_rrxn has a length + * equal to the total number of species in the kinetics + * object. For each species, there exists a map, with the + * reaction number being the key, and the + * reactant stoichiometric coefficient being the value. + * HKM -> mutable because search sometimes creates extra + * entries. To be fixed in future... + */ + mutable std::vector > m_rrxn; + + /** + * m_rrxn is a vector of maps. m_rrxn has a length + * equal to the total number of species in the kinetics + * object. For each species, there exists a map, with the + * reaction number being the key, and the + * product stoichiometric coefficient being the value. + */ + mutable std::vector > m_prxn; + + + std::vector m_rxneqn; + + /** + * Temporary data storage used in calculating the rates of + * of reactions. + */ + InterfaceKineticsData* m_kdata; + + /** + * An array of generalized concentrations + * \f$ C_k \f$ that are defined such that \f$ a_k = C_k / + * C^0_k, \f$ where \f$ C^0_k \f$ is a standard concentration/ + * These generalized concentrations are used + * by this kinetics manager class to compute the forward and + * reverse rates of elementary reactions. The "units" for the + * concentrations of each phase depend upon the implementation + * of kinetics within that phase. + * The order of the species within the vector is based on + * the order of listed ThermoPhase objects in the class, and the + * order of the species within each ThermoPhase class. + */ + vector_fp m_conc; + + vector_fp m_mu0; + vector_fp m_phi; + vector_fp m_pot; + vector_fp m_rwork; + vector_fp m_E; + + SurfPhase* m_surf; + ImplicitSurfChem* m_integrator; + + private: + + int reactionNumber(){ return m_ii;} + void addElementaryReaction(const ReactionData& r); + void addGlobalReaction(const ReactionData& r); + void installReagents(const ReactionData& r); + + void updateKc(); + + void registerReaction(int rxnNumber, int type, int loc) { + m_index[rxnNumber] = std::pair(type, loc); + } + void applyButlerVolmerCorrection(doublereal* kf); + bool m_finalized; + bool m_has_coverage_dependence; + }; +} + +#endif diff --git a/Cantera/src/kinetics/Kinetics.cpp b/Cantera/src/kinetics/Kinetics.cpp new file mode 100644 index 000000000..fb0955d09 --- /dev/null +++ b/Cantera/src/kinetics/Kinetics.cpp @@ -0,0 +1,234 @@ +/** + * @file Kinetics.cpp + * Declarations for the base class for kinetics + * managers (see \ref kineticsmgr and class + * \link Cantera::Kinetics Kinetics\endlink). + * + * Kinetics managers calculate rates of progress of species due to homogeneous or heterogeneous kinetics. + */ + +// Copyright 2001-2004 California Institute of Technology + + + +#include "InterfaceKinetics.h" +#include "SurfPhase.h" +#include "ReactionData.h" +#include "StoichManager.h" +#include "RateCoeffMgr.h" + +#include "ImplicitSurfChem.h" + +#include +using namespace std; + + +namespace Cantera { + + + Kinetics::Kinetics() : m_ii(0), m_thermo(0), + m_index(-1), m_surfphase(-1), m_rxnphase(-1), + m_mindim(4) {} + + /* + Kinetics::Kinetics(thermo_t* thermo) : + m_ii(0), + m_index(-1), + m_surfphase(-1) , + m_rxnphase(0), m_mindim(4) + { + if (thermo) { + addPhase(*thermo); + } + deprecatedMethod("Kinetics","Kinetics(thermo_t*)","Kinetics()"); + removeAtVersion("Kinetics(thermo_t*)","1.6.0"); + } + */ + + Kinetics::~Kinetics(){} + + /** + * Takes as input an array of properties for all species in the + * mechanism and copies those values beloning to a particular + * phase to the output array. + * @param data Input data array. + * @param phase Pointer to one of the phase objects participating + * in this reaction mechanism + * @param phase_data Output array where the values for the the + * specified phase are to be written. + */ + void Kinetics::selectPhase(const doublereal* data, const thermo_t* phase, + doublereal* phase_data) { + int n, nsp, np = nPhases(); + for (n = 0; n < np; n++) { + if (phase == m_thermo[n]) { + nsp = phase->nSpecies(); + copy(data + m_start[n], + data + m_start[n] + nsp, phase_data); + return; + } + } + throw CanteraError("Kinetics::selectPhase", "Phase not found."); + } + + + /** + * kineticsSpeciesName(): + * + * Return the string name of the kth species in the kinetics + * manager. k is an integer from 0 to ktot - 1, where ktot is + * the number of species in the kinetics manager, which is the + * sum of the number of species in all phases participating in + * the kinetics manager. If k is out of bounds, the string + * "" is returned. + */ + string Kinetics::kineticsSpeciesName(int k) const { + int np = m_start.size(); + for (int n = np-1; n >= 0; n--) { + if (k >= m_start[n]) { + return thermo(n).speciesName(k - m_start[n]); + } + } + return ""; + } + + /** + * kineticsSpeciesIndex(): + * + * This routine will look up a species number based on + * the input string nm. The lookup of species will + * occur for all phases listed in the kinetics object, + * unless the string ph refers to a specific phase of + * the object. + * + * return + * - If a match is found, the position in the species list + * is returned. + * - If a specific phase is specified and no match is found, + * the value -1 is returned. + * - If no match is found in any phase, the value -2 is returned. + */ + int Kinetics::kineticsSpeciesIndex(std::string nm, std::string ph) const { + int np = static_cast(m_thermo.size()); + int k; + string id; + for (int n = 0; n < np; n++) { + id = thermo(n).id(); + if (ph == id) { + k = thermo(n).speciesIndex(nm); + if (k < 0) return -1; + return k + m_start[n]; + } + else if (ph == "") { + /* + * Call the speciesIndex() member function of the + * ThermoPhase object to find a match. + */ + k = thermo(n).speciesIndex(nm); + if (k >= 0) return k + m_start[n]; + } + } + return -2; + } + + /** + * This function looks up the string name of a species and + * returns a reference to the ThermoPhase object of the + * phase where the species resides. + * Will throw an error if the species string doesn't match. + */ + thermo_t& Kinetics::speciesPhase(std::string nm) { + int np = static_cast(m_thermo.size()); + int k; + string id; + for (int n = 0; n < np; n++) { + k = thermo(n).speciesIndex(nm); + if (k >= 0) return thermo(n); + } + throw CanteraError("speciesPhase", "unknown species "+nm); + } + + /** + * This function takes as an argument the kineticsSpecies index + * (i.e., the list index in the list of species in the kinetics + * manager) and returns the index of the phase owning the + * species. + */ + int Kinetics::speciesPhaseIndex(int k) { + int np = m_start.size(); + for (int n = np-1; n >= 0; n--) { + if (k >= m_start[n]) { + return n; + } + } + throw CanteraError("speciesPhaseIndex", + "illegal species index: "+int2str(k)); + } + + /** + * Add a phase to the kinetics manager object. This must + * be done before the function init() is called or + * before any reactions are input. + * The following fields are updated: + * m_start -> vector of integers, containing the + * starting position of the species for + * each phase in the kinetics mechanism. + * m_surfphase -> index of the surface phase. + * m_thermo -> vector of pointers to ThermoPhase phases + * that participate in the kinetics + * mechanism. + * m_phaseindex -> map containing the string id of each + * ThermoPhase phase as a key and the + * index of the phase within the kinetics + * manager object as the value. + */ + void Kinetics::addPhase(thermo_t& thermo) { + + // if not the first thermo object, set the start position + // to that of the last object added + the number of its species + if (m_thermo.size() > 0) { + m_start.push_back(m_start.back() + + m_thermo.back()->nSpecies()); + } + // otherwise start at 0 + else { + m_start.push_back(0); + } + + // the phase with lowest dimensionality is assumed to be the + // phase/interface at which reactions take place + if (thermo.nDim() <= m_mindim) { + m_mindim = thermo.nDim(); + m_rxnphase = nPhases(); + } + + // there should only be one surface phase + int ptype = -100; + if (type() == cEdgeKinetics) ptype = cEdge; + else if (type() == cInterfaceKinetics) ptype = cSurf; + if (thermo.eosType() == ptype) { + // if (m_surfphase >= 0) { + // throw CanteraError("Kinetics::addPhase", + // "cannot add more than one surface phase"); + // } + m_surfphase = nPhases(); + m_rxnphase = nPhases(); + } + m_thermo.push_back(&thermo); + m_phaseindex[m_thermo.back()->id()] = nPhases(); + } + + + //! Private function of the class Kinetics, indicating that a function + //! inherited from the base class hasn't had a definition assigned to it + /*! + * @param m String message + */ + void Kinetics::err(std::string m) const { + throw CanteraError("Kinetics::" + m, + "The default Base class method was called, when " + "the inherited class's method should " + "have been called"); + } + +} diff --git a/Cantera/src/kinetics/Kinetics.h b/Cantera/src/kinetics/Kinetics.h new file mode 100755 index 000000000..124ad846d --- /dev/null +++ b/Cantera/src/kinetics/Kinetics.h @@ -0,0 +1,956 @@ +/** + * @file Kinetics.h + * Base class for kinetics managers and also contains the kineticsmgr + * module documentation (see \ref kineticsmgr and class + * \link Cantera::Kinetics Kinetics\endlink). + * + * $Author$ + * $Date$ + * $Revision$ + */ + +// Copyright 2001-2004 California Institute of Technology + +#ifndef CT_KINETICS_H +#define CT_KINETICS_H + +#include "ctexceptions.h" +#include "ThermoPhase.h" +#include "mix_defs.h" + +namespace Cantera { + + // forward references + class ReactionData; + + /// @defgroup kineticsmgr Kinetics Managers + /// @section kinmodman Models and Managers + /// + /// A kinetics manager is a C++ class that implements a kinetics + /// model; a kinetics model is a set of mathematical equation + /// describing how various kinetic quanities are to be computed -- + /// reaction rates, species production rates, etc. Many different + /// kinetics models might be defined to handle different types of + /// kinetic processes. For example, one kinetics model might use + /// expressions valid for elementary reactions in ideal gas + /// mixtures. It might, for example, require the reaction orders + /// to be integral and equal to the forward stoichiometric + /// coefficients, require that each reaction be reversible with a + /// reverse rate satisfying detailed balance, include + /// pressure-dependent unimolecular reactions, etc. Another + /// kinetics model might be designed for heterogeneous chemistry + /// at interfaces, and might allow empirical reaction orders, + /// coverage-dependent activation energies, irreversible + /// reactions, and include effects of potential differences across + /// the interface on reaction rates. + /// + /// A kinetics manager implements a kinetics model. Since the + /// model equations may be complex and expensive to evaluate, a + /// kinetics manager may adopt various strategies to 'manage' the + /// computation and evaluate the expressions efficiently. For + /// example, if there are rate coefficients or other quantities + /// that depend only on temperature, a manager class may choose to + /// store these quantities internally, and re-evaluate them only + /// when the temperature has actually changed. Or a manager + /// designed for use with reaction mechanisms with a few repeated + /// activation energies might precompute the terms \f$ exp(-E/RT) + /// \f$, instead of evaluating the exponential repeatedly for each + /// reaction. There are many other possible 'management styles', + /// each of which might be better suited to some reaction + /// mechanisms than others. + /// + /// But however a manager structures the internal computation, the + /// tasks the manager class must perform are, for the most part, + /// the same. It must be able to compute reaction rates, species + /// production rates, equilibrium constants, etc. Therefore, all + /// kinetics manager classes should have a common set of public + /// methods, but differ in how they implement these methods. + /// + /// A kinetics manager computes reaction rates of progress, + /// species production rates, equilibrium constants, and similar + /// quantities for a reaction mechanism. All kinetics manager + /// classes derive from class Kinetics, which defines a common + /// public interface for all kinetics managers. Each derived class + /// overloads the virtual methods of Kinetics to implement a + /// particular kinetics model. + /// + /// For example, class GasKinetics implements reaction rate + /// expressions appropriate for homogeneous reactions in ideal gas + /// mixtures, and class InterfaceKinetics implements expressions + /// appropriate for heterogeneous mechanisms at interfaces, + /// including how to handle reactions involving charged species of + /// phases with different electric potentials --- something that + /// class GasKinetics doesn't deal with at all. + /// + /// Kinetics managers may be also created that hard-wire a + /// particular reaction mechanism in C++ code. This can often + /// result in faster performance. An example of this is the + /// kinetics manager GRI30_Kinetics that hard-wires the rate + /// expressions for the natural gas combustion mechanism GRI-3.0. + /// + /// Many of the methods of class Kinetics write into arrays the + /// values of some quantity for each species, for example the net + /// production rate. These methods always write the results into + /// flat arrays, ordered by phase in the order the phase was + /// added, and within a phase in the order the species were added + /// to the phase (which is the same ordering as in the input + /// file). Example: suppose a heterogeneous mechanism involves + /// three phases -- a bulk phase 'a', another bulk phase 'b', and + /// the surface phase 'a:b' at the a/b interface. Phase 'a' + /// contains 12 species, phase 'b' contains 3, and at the + /// interface there are 5 adsorbed species defined in phase + /// 'a:b'. Then methods like getNetProductionRates(doublereal* net) + /// will write and output array of length 20, beginning at the location + /// pointed to by 'net'. The first 12 values will be the net production + /// rates for all 12 species of phase 'a' (even if some do not participate + /// in the reactions), the next 3 will be for phase 'b', and finally the + /// net production rates for the surface species will occupy the last + /// 5 locations. + + + //! Public interface for kinetics managers. + /*! + * This class serves as a + * base class to derive 'kinetics managers', which are classes + * that manage homogeneous chemistry within one phase, or + * heterogeneous chemistry at one interface. The virtual methods + * of this class are meant to be overloaded in subclasses. The + * non-virtual methods perform generic functions and are + * implemented in Kinetics. They should not be overloaded. Only + * those methods required by a subclass need to be overloaded; + * the rest will throw exceptions if called. @ingroup kinetics + * @ingroup kineticsmgr + */ + class Kinetics { + + public: + + //! typedef for ThermoPhase + typedef ThermoPhase thermo_t; + + /** + * @name Constructors and General Information about Mechanism + */ + //@{ + + /// Default constructor. + Kinetics(); + + /// This constructor initializes with a starting phase. + /// @deprecated + // Kinetics(thermo_t* thermo); + + /// Destructor. + virtual ~Kinetics(); + + /// Identifies the kinetics manager type. Each class derived + /// from Kinetics should overload this method to return a + /// unique integer. Standard values are defined in file + /// mix_defs.h. + virtual int type() { return 0; } + + /// Number of reactions in the reaction mechanism. + int nReactions() const {return m_ii;} + + //@} + + + /** + * @name Information/Lookup Functions about Phases and Species + */ + //@{ + + /** + * The number of phases participating in the reaction + * mechanism. For a homogeneous reaction mechanism, this will + * always return 1, but for a heterogeneous mechanism it will + * return the total number of phases in the mechanism. + */ + int nPhases() const { return static_cast(m_thermo.size()); } + + /** + * Return the phase index of a phase in the list of phases + * defined within the object. + * + * @param ph std::string name of the phase + * + * If a -1 is returned, then the phase is not defined in + * the Kinetics object. + */ + int phaseIndex(std::string ph) { + if (m_phaseindex.find(ph) == m_phaseindex.end()) { + return -1; + } + else { + return m_phaseindex[ph] - 1; + } + } + + /** + * This returns the integer index of the phase which has + * ThermoPhase type cSurf. For heterogeneous mechanisms, this + * identifies the one surface phase. For homogeneous + * mechanisms, this reurns -1. + */ + int surfacePhaseIndex() { return m_surfphase; } + + /** + * Phase where the reactions occur. For heterogeneous + * mechanisms, one of the phases in the list of phases + * represents the 2D interface or 1D edge at which the + * reactions take place. This method returns the index of the + * phase with the smallest spatial dimension (1, 2, or 3) + * among the list of phases. If there is more than one, the + * index of the first one is returned. For homogeneous + * mechanisms, the value 0 is returned. + */ + int reactionPhaseIndex() { return m_rxnphase; } + + + /** + * This method returns a reference to the nth ThermoPhase + * object defined in this kinetics mechanism. It is typically + * used so that member functions of the ThermoPhase object may + * be called. For homogeneous mechanisms, there is only one + * object, and this method can be called without an argument + * to access it. + * + * @param n Index of the ThermoPhase being sought. + */ + thermo_t& thermo(int n=0) { return *m_thermo[n]; } + const thermo_t& thermo(int n=0) const { return *m_thermo[n]; } + + /** + * This method returns a reference to the nth ThermoPhase + * defined in this kinetics mechanism. + * It is typically used so that member functions of the + * ThermoPhase may be called. @deprecated This method is redundant. + * + * @param n Index of the ThermoPhase being sought. + */ + thermo_t& phase(int n=0) { + deprecatedMethod("Kinetics","phase","thermo"); + return *m_thermo[n]; + } + /** + * This method returns a reference to the nth ThermoPhase + * defined in this kinetics mechanism. + * It is typically used so that member functions of the + * ThermoPhase may be called. @deprecated This method is redundant. + * + * @param n Index of the ThermoPhase being sought. + */ + const thermo_t& phase(int n=0) const { + deprecatedMethod("Kinetics","phase","thermo"); + return *m_thermo[n]; + } + + /** + * The total number of species in all phases participating in + * the kinetics mechanism. This is useful to dimension arrays + * for use in calls to methods that return the species + * production rates, for example. + */ + int nTotalSpecies() const { + int n=0, np; + np = nPhases(); + for (int p = 0; p < np; p++) n += thermo(p).nSpecies(); + return n; + } + + /** + * Returns the starting index of the species in the nth phase + * associated with the reaction mechanism. + * + * @param n Return the index of first species in the nth phase + * associated with the reaction mechanism. + */ + int start(int n) { + deprecatedMethod("Kinetics","start","kineticsSpeciesIndex(0,n)"); + return m_start[n]; + } + + + /** + * The location of species k of phase n in species arrays. + * Kinetics manager classes return species production rates in + * flat arrays, with the species of each phases following one + * another, in the order the phases were added. This method + * is useful to find the value for a particular species of a + * particular phase in arrrays returned from methods like + * getCreationRates that return an array of species-specific + * quantities. + * + * Example: suppose a heterogeneous mechanism involves three + * phases. The first contains 12 species, the second 26, and + * the third 3. Then species arrays must have size at least + * 41, and positions 0 - 11 are the values for the species in + * the first phase, positions 12 - 37 are the values for the + * species in the second phase, etc. Then + * kineticsSpeciesIndex(7, 0) = 7, kineticsSpeciesIndex(4, 1) + * = 16, and kineticsSpeciesIndex(2, 2) = 40. + * + * @param k species index + * @param n phase index for the species + */ + int kineticsSpeciesIndex(int k, int n) const { + return m_start[n] + k; + } + + /** + * Return the std::string name of the kth species in the kinetics + * manager. k is an integer from 0 to ktot - 1, where ktot is + * the number of species in the kinetics manager, which is the + * sum of the number of species in all phases participating in + * the kinetics manager. If k is out of bounds, the std::string + * "" is returned. + * + * @param k species index + */ + std::string kineticsSpeciesName(int k) const; + + /** + * This routine will look up a species number based on + * the input std::string nm. The lookup of species will + * occur for all phases listed in the kinetics object, + * unless the std::string ph refers to a specific phase of + * the object. + * + * return + * - If a match is found, the position in the species list + * is returned. + * - If a specific phase is specified and no match is found, + * the value -1 is returned. + * - If no match is found in any phase, the value -2 is returned. + * + * @param nm Input string name of the species + * @param ph Input string name of the phase. Defaults to "" + */ + int kineticsSpeciesIndex(std::string nm, std::string ph = "") const; + + /** + * This function looks up the std::string name of a species and + * returns a reference to the ThermoPhase object of the + * phase where the species resides. + * Will throw an error if the species std::string doesn't match. + * + * @param nm String containing the name of the species. + */ + thermo_t& speciesPhase(std::string nm); + + /** + * This function takes as an argument the kineticsSpecies index + * (i.e., the list index in the list of species in the kinetics + * manager) and returns the species' owning ThermoPhase object. + * + * @param k Species index + */ + thermo_t& speciesPhase(int k) { + return thermo(speciesPhaseIndex(k)); + } + + /** + * This function takes as an argument the kineticsSpecies index + * (i.e., the list index in the list of species in the kinetics + * manager) and returns the index of the phase owning the + * species. + * + * @param k Species index + */ + int speciesPhaseIndex(int k); + + //@} + + + + /** + * @name Reaction Rates Of Progress + */ + //@{ + + //! Return the forward rates of progress of the reactions + /*! + * Forward rates of progress. Return the forward rates of + * progress in array fwdROP, which must be dimensioned at + * least as large as the total number of reactions. + * + * @param fwdROP Output vector containing forward rates + * of progress of the reactions. Length: m_ii. + */ + virtual void getFwdRatesOfProgress(doublereal* fwdROP) { + err("getFwdRatesOfProgress"); + } + + //! Return the Reverse rates of progress of the reactions + /*! + * Return the reverse rates of + * progress in array revROP, which must be dimensioned at + * least as large as the total number of reactions. + * + * @param revROP Output vector containing reverse rates + * of progress of the reactions. Length: m_ii. + */ + virtual void getRevRatesOfProgress(doublereal* revROP) { + err("getRevRatesOfProgress"); + } + + /** + * Net rates of progress. Return the net (forward - reverse) + * rates of progress in array netROP, which must be + * dimensioned at least as large as the total number of + * reactions. + * + * @param netROP Output vector of the net ROP. Length: m_ii. + */ + virtual void getNetRatesOfProgress(doublereal* netROP) { + err("getNetRatesOfProgress"); + } + + + + //! Return a vector of Equilibrium constants. + /*! + * Return the equilibrium constants of + * the reactions in concentration units in array kc, which + * must be dimensioned at least as large as the total number + * of reactions. + * + * @param kc Output vector containing the equilibrium constants. + * Length: m_ii. + */ + virtual void getEquilibriumConstants(doublereal* kc) { + err("getEquilibriumConstants"); + } + + /** + * Change in species properties. Given an array of molar species + * property values \f$ z_k, k = 1, \dots, K \f$, return the + * array of reaction values + * \f[ + * \Delta Z_i = \sum_k \nu_{k,i} z_k, i = 1, \dots, I. + * \f] + * For example, if this method is called with the array of + * standard-state molar Gibbs free energies for the species, + * then the values returned in array \c deltaProperty would be + * the standard-state Gibbs free energies of reaction for each + * reaction. + * + * @param property Input vector of property value. Length: m_kk. + * @param deltaProperty Output vector of deltaRxn. Length: m_ii. + */ + virtual void getReactionDelta(const doublereal* property, + doublereal* deltaProperty) { + err("getReactionDelta"); + } + + /** + * Return the vector of values for the reaction gibbs free + * energy change. These values depend upon the concentration + * of the solution. + * + * units = J kmol-1 + * + * @param deltaG Output vector of deltaG's for reactions + * Length: m_ii. + */ + virtual void getDeltaGibbs( doublereal* deltaG) { + err("getDeltaGibbs"); + } + + /** + * Return the vector of values for the reactions change in + * enthalpy. These values depend upon the concentration of + * the solution. + * + * units = J kmol-1 + * + * @param deltaH Output vector of deltaH's for reactions + * Length: m_ii. + */ + virtual void getDeltaEnthalpy( doublereal* deltaH) { + err("getDeltaEnthalpy"); + } + + /** + * Return the vector of values for the reactions change in + * entropy. These values depend upon the concentration of the + * solution. + * + * units = J kmol-1 Kelvin-1 + * + * @param deltaS Output vector of deltaS's for reactions + * Length: m_ii. + */ + virtual void getDeltaEntropy( doublereal* deltaS) { + err("getDeltaEntropy"); + } + + /** + * Return the vector of values for the reaction standard state + * gibbs free energy change. These values don't depend upon + * the concentration of the solution. + * + * units = J kmol-1 + * + * @param deltaG Output vector of ss deltaG's for reactions + * Length: m_ii. + */ + virtual void getDeltaSSGibbs( doublereal* deltaG) { + err("getDeltaSSGibbs"); + } + + /** + * Return the vector of values for the change in the standard + * state enthalpies of reaction. These values don't depend + * upon the concentration of the solution. + * + * units = J kmol-1 + * + * @param deltaH Output vector of ss deltaH's for reactions + * Length: m_ii. + */ + virtual void getDeltaSSEnthalpy( doublereal* deltaH) { + err("getDeltaSSEnthalpy"); + } + + /** + * Return the vector of values for the change in the standard + * state entropies for each reaction. These values don't + * depend upon the concentration of the solution. + * + * units = J kmol-1 Kelvin-1 + * + * @param deltaS Output vector of ss deltaS's for reactions + * Length: m_ii. + */ + virtual void getDeltaSSEntropy( doublereal* deltaS) { + err("getDeltaSSEntropy"); + } + + + //@} + /** + * @name Species Production Rates + */ + //@{ + + /** + * Species creation rates [kmol/m^3/s or kmol/m^2/s]. Return the + * species creation rates in array cdot, which must be + * dimensioned at least as large as the total number of + * species in all phases. @see nTotalSpecies. + * + * @param cdot Output vector of creation rates. + * Length: m_kk. + */ + virtual void getCreationRates(doublereal* cdot) { + err("getCreationRates"); + } + + /** + * Species destruction rates [kmol/m^3/s or kmol/m^2/s]. Return + * the species destruction rates in array ddot, which must be + * dimensioned at least as large as the total number of + * species. @see nTotalSpecies. + * + * @param ddot Output vector of destruction rates. + * Length: m_kk. + */ + virtual void getDestructionRates(doublereal* ddot) { + err("getDestructionRates"); + } + + /** + * Species net production rates [kmol/m^3/s or kmol/m^2/s]. Return + * the species net production rates (creation - destruction) + * in array wdot, which must be dimensioned at least as large + * as the total number of species. @see nTotalSpecies. + * + * @param wdot Output vector of net production rates. + * Length: m_kk. + */ + virtual void getNetProductionRates(doublereal* wdot) { + err("getNetProductionRates"); + } + + //@} + + + /** + * @name Reaction Mechanism Informational Query Routines + */ + //@{ + + /** + * Stoichiometric coefficient of species k as a reactant in + * reaction i. + * + * @param k species index + * @param i reaction index + */ + virtual doublereal reactantStoichCoeff(int k, int i) const { + err("reactantStoichCoeff"); + return -1.0; + } + + /** + * Stoichiometric coefficient of species k as a product in + * reaction i. + * + * @param k species index + * @param i reaction index + */ + virtual doublereal productStoichCoeff(int k, int i) const { + err("productStoichCoeff"); + return -1.0; + } + + /** + * reactant Order of species k in reaction i. + * + * @param k species index + * @param i reaction index + */ + virtual doublereal reactantOrder(int k, int i) const { + err("reactantOrder"); + return -1.0; + } + + /** + * Returns a read-only reference to the vector of reactant + * index numbers for reaction i. + * + * @param i reaction index + */ + virtual const vector_int& reactants(int i) const { + return m_reactants[i]; + } + + /** + * Returns a read-only reference to the vector of product + * index numbers for reaction i. + * + * @param i reaction index + */ + virtual const vector_int& products(int i) const { + return m_products[i]; + } + + /** + * Flag specifying the type of reaction. The legal values and + * their meaning are specific to the particular kinetics + * manager. + * + * @param i reaction index + */ + virtual int reactionType(int i) const { + err("reactionType"); + return -1; + } + + /** + * True if reaction i has been declared to be reversible. If + * isReversible(i) is false, then the reverse rate of progress + * for reaction i is always zero. + * + * @param i reaction index + */ + virtual bool isReversible(int i){ + err("isReversible"); + return false; + } + + /** + * Return a std::string representing the reaction. + * + * @param i reaction index + */ + virtual std::string reactionString(int i) const { + err("reactionStd::String"); return ""; + } + + /** + * Return the forward rate constants + * + * length is the number of reactions. units depends + * on many issues. @todo DGG: recommend changing name to + * getFwdRateCoefficients. + * + * @param kfwd Output vector containing the foward reaction rate constants. + * Length: m_ii. + */ + virtual void getFwdRateConstants(doublereal *kfwd) { + err("getFwdRateConstants"); + } + + /** + * Return the reverse rate constants. + * + * length is the number of reactions. units depends + * on many issues. Note, this routine will return rate constants + * for irreversible reactions if the default for + * doIrreversible is overridden. @todo DGG: recommend changing name to + * getRevRateCoefficients. + * + * @param krev Output vector of reverse rate constants. + * @param doIrreversible boolean indicating whether irreversible reactions + * should be included. + */ + virtual void getRevRateConstants(doublereal *krev, + bool doIrreversible = false) { + err("getFwdRateConstants"); + } + + + /** + * Return the activation energies in Kelvin. + * + * length is the number of reactions + * + * @param E Ouptut vector of activation energies. + * Length: m_ii. + */ + virtual void getActivationEnergies(doublereal *E) { + err("getActivationEnergies"); + } + + + //@} + /** + * @name Reaction Mechanism Construction + */ + //@{ + + /** + * Add a phase to the kinetics manager object. This must + * be done before the function init() is called or + * before any reactions are input. + * The following fields are updated: + * m_start -> vector of integers, containing the + * starting position of the species for + * each phase in the kinetics mechanism. + * m_surfphase -> index of the surface phase. + * m_thermo -> vector of pointers to ThermoPhase phases + * that participate in the kinetics + * mechanism. + * m_phaseindex -> map containing the std::string id of each + * ThermoPhase phase as a key and the + * index of the phase within the kinetics + * manager object as the value. + * + * @param thermo Reference to the ThermoPhase to be added. + */ + void addPhase(thermo_t& thermo); + + /** + * Prepare the class for the addition of reactions. This + * method is called by function importKinetics after all + * phases have been added but before any reactions have + * been. The base class method does nothing, but derived + * classes may use this to perform any initialization + * (allocating arrays, etc.) that requires knowing the phases + * and species, but before any reactions are added. + */ + virtual void init() {} + + /** + * Finish adding reactions and prepare for use. This method is + * called by function importKinetics after all reactions have + * been entered into the mechanism and before the mechanism is + * used to calculate reaction rates. The base class method + * does nothing, but derived classes may use this to perform + * any initialization (allocating arrays, etc.) that must be + * done after the reactions are entered. + */ + virtual void finalize() {} + + /** + * Add a single reaction to the mechanism. This routine + * must be called after init() and before finalize(). + * + * @param r Reference to the ReactionRate object for the reaction + * to be added. + */ + virtual void addReaction(const ReactionData& r) { + err("addReaction"); + } + + virtual const std::vector& reactantGroups(int i) { + //err("reactantGroups"); + return m_dummygroups; + } + + virtual const std::vector& productGroups(int i) { + //err("productGroups"); + return m_dummygroups; + } + + + //@} + /** + * @name Altering Reaction Rates + * + * These methods alter reaction rates. They are designed + * primarily for carrying out sensitivity analysis, but may be + * used for any purpose requiring dynamic alteration of rate + * constants. For each reaction, a real-valued multiplier may + * be defined that multiplies the reaction rate + * coefficient. The multiplier may be set to zero to + * completely remove a reaction from the mechanism. + */ + //@{ + + /// The current value of the multiplier for reaction i. + /*! + * @param i index of the reaction + */ + doublereal multiplier(int i) const {return m_perturb[i];} + + /// Set the multiplier for reaction i to f. + /*! + * @param i index of the reaction + * @param f value of the multiplier. + */ + void setMultiplier(int i, doublereal f) {m_perturb[i] = f;} + + //@} + + /** + * Increment the number of reactions in the mechanism by one. + * @todo Should be protected? + */ + void incrementRxnCount() { m_ii++; m_perturb.push_back(1.0); } + + /** + * Returns true if the kinetics manager has been properly + * initialized and finalized. + */ + virtual bool ready() const { + return false; + } + + + /** + * Extract from array \c data the portion pertaining to phase \c phase. + * + * @param data data + * @param phase phase + * @param phase_data phase_data + */ + void selectPhase(const doublereal* data, const thermo_t* phase, + doublereal* phase_data); + + /// For internal use. May be removed in a future release. + int index(){ return m_index; } + + //! Set the index of the Kinetics Manager + /*! + * @param index input index + */ + void setIndex(int index) { m_index = index; } + + + protected: + + + //! Number of reactions in the mechanism + int m_ii; + + /// Vector of perturbation factors for each reaction's rate of + /// progress vector. It is initialized to one. + /// + vector_fp m_perturb; + + /** + * This is a vector of vectors containing the reactants for + * each reaction. The outer vector is over the number of + * reactions, m_ii. The inner vector is a list of species + * indices. If the stoichiometric coefficient for a reactant + * is greater than one, then the reactant is listed + * contiguously in the vector a number of times equal to its + * stoichiometric coefficient. + * NOTE: These vectors will be wrong if there are real + * stoichiometric coefficients in the expression. + */ + std::vector m_reactants; + + /** + * This is a vector of vectors containing the products for + * each reaction. The outer vector is over the number of + * reactions, m_ii. The inner vector is a list of species + * indeces. If the stoichiometric coefficient for a product is + * greater than one, then the reactant is listed contiguously + * in the vector a number of times equal to its stoichiometric + * coefficient. + * NOTE: These vectors will be wrong if there are real + * stoichiometric coefficients in the expression. + */ + std::vector m_products; + + /** + * m_thermo is a vector of pointers to ThermoPhase + * objects. For homogeneous kinetics applications, this vector + * will only have one entry. For interfacial reactions, this + * vector will consist of multiple entries; some of them will + * be surface phases, and the other ones will be bulk phases. + * The order that the objects are listed determines the order + * in which the species comprising each phase are listed in + * the source term vector, originating from the reaction + * mechanism. + */ + std::vector m_thermo; + + /** + * m_start is a vector of integers specifying the beginning position + * for the species vector for the n'th phase in the kinetics + * class. + */ + vector_int m_start; + + /** + * Mapping of the phase id, i.e., the id attribute in the xml + * phase element to the position of the phase within the + * kinetics object. Positions start with the value of 1. The + * member function, phaseIndex() decrements by one before + * returning the index value, so that missing phases return + * -1. + */ + std::map m_phaseindex; + //! Index of the Kinetics Manager + int m_index; + + /** + * Index in the list of phases of the one surface phase. + */ + int m_surfphase; + + /** + * Index in the list of phases of the one phase where the reactions + * occur. + */ + int m_rxnphase; + + /// number of spatial dimensions of lowest-dimensional phase. + int m_mindim; + + private: + + //! Vector of group lists + std::vector m_dummygroups; + + //! Function for unhandled situations + /*! + * @param m String error message + */ + void err(std::string m) const; + + }; + + //! typedef for the kinetics base class + typedef Kinetics kinetics_t; + +} + + + +#endif diff --git a/Cantera/src/kinetics/KineticsFactory.cpp b/Cantera/src/kinetics/KineticsFactory.cpp new file mode 100644 index 000000000..8e66fdfe9 --- /dev/null +++ b/Cantera/src/kinetics/KineticsFactory.cpp @@ -0,0 +1,152 @@ +/** + * @file KineticsFactory.cpp + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifdef WIN32 +#pragma warning(disable:4786) +#endif + +#include "KineticsFactory.h" + +#include "GasKinetics.h" +#include "GRI_30_Kinetics.h" +#include "InterfaceKinetics.h" +#include "EdgeKinetics.h" +#include "importKinetics.h" + +using namespace std; + +namespace Cantera { + + KineticsFactory* KineticsFactory::s_factory = 0; + + static int ntypes = 5; + static string _types[] = {"none", "GasKinetics", "GRI30", "Interface", "Edge"}; + static int _itypes[] = {0, cGasKinetics, cGRI30, cInterfaceKinetics, cEdgeKinetics}; + + /** + * Return a new kinetics manager that implements a reaction + * mechanism specified in a CTML file. In other words, the + * kinetics manager, given the rate constants and formulation of the + * reactions that make up a kinetics mechanism, is responsible for + * calculating the rates of progress of the reactions and for + * calculating the source terms for species. + * + * Input + * ------ + * phaseData = This is an XML_Node that contains the xml data + * describing the phase. Of particular note to this + * routine is the child xml element called "kinetics". + * The element has one attribute called "model", + * with a string value. The value of this string + * is used to decide which kinetics manager is used + * to calculate the reacton mechanism. + * + * Return + * --------- + * Pointer to the new kinetics manager. + */ + + Kinetics* KineticsFactory:: + newKinetics(XML_Node& phaseData, vector th) { + /* + * Look for a child of the xml element phase called + * "kinetics". It has an attribute name "model". + * Store the value of that attribute in the variable kintype + */ + string kintype = phaseData.child("kinetics")["model"]; + /* + * look up the string kintype in the list of known + * kinetics managers (list is kept at the top of this file). + * Translate it to an integer value, ikin. + */ + int ikin=-1; + int n; + for (n = 0; n < ntypes; n++) { + if (kintype == _types[n]) ikin = _itypes[n]; + } + /* + * Assign the kinetics manager based on the value of ikin. + * Kinetics managers are classes derived from the base + * Kinetics class. Unknown kinetics managers will throw a + * CanteraError here. + */ + Kinetics* k=0; + switch (ikin) { + + case 0: + k = new Kinetics; + break; + + case cGasKinetics: + k = new GasKinetics; + break; + + case cGRI30: + k = new GRI_30_Kinetics; + break; + + case cInterfaceKinetics: + k = new InterfaceKinetics; + break; + + case cEdgeKinetics: + k = new EdgeKinetics; + break; + + default: + throw UnknownKineticsModel("KineticsFactory::newKinetics", + kintype); + } + + // Now that we have the kinetics manager, we can + // import the reaction mechanism into it. + importKinetics(phaseData, th, k); + + // Return the pointer to the kinetics manager + return k; + } + + + /** + * Return a new, empty kinetics manager. + */ + Kinetics* KineticsFactory::newKinetics(string model) { + + int ikin = -1; + int n; + for (n = 0; n < ntypes; n++) { + if (model == _types[n]) ikin = _itypes[n]; + } + Kinetics* k=0; + switch (ikin) { + + case cGasKinetics: + k = new GasKinetics; + break; + + case cGRI30: + k = new GRI_30_Kinetics; + break; + + case cInterfaceKinetics: + k = new InterfaceKinetics; + break; + + default: + throw UnknownKineticsModel("KineticsFactory::newKinetics", + model); + } + return k; + } + +} diff --git a/Cantera/src/kinetics/KineticsFactory.h b/Cantera/src/kinetics/KineticsFactory.h new file mode 100644 index 000000000..9270dbc1a --- /dev/null +++ b/Cantera/src/kinetics/KineticsFactory.h @@ -0,0 +1,91 @@ +/** + * @file KineticsFactory.h + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef KINETICS_FACTORY_H +#define KINETICS_FACTORY_H + +#include "Kinetics.h" +#include "xml.h" + +namespace Cantera { + + + class UnknownKineticsModel : public CanteraError { + public: + UnknownKineticsModel(std::string proc, std::string kineticsModel) : + CanteraError(proc, "Specified Kinetics model " + + kineticsModel + + " does not match any known type.") {} + virtual ~UnknownKineticsModel() {} + }; + + + /** + * Factory for kinetics managers. + */ + class KineticsFactory { + + public: + + static KineticsFactory* factory() { + if (!s_factory) s_factory = new KineticsFactory; + return s_factory; + } + + virtual ~KineticsFactory() { + delete s_factory; + s_factory = 0; + } + + /** + * Create a new kinetics manager. + */ + virtual Kinetics* newKinetics(XML_Node& phase, + std::vector th); + + virtual Kinetics* newKinetics(std::string model); + + private: + + static KineticsFactory* s_factory; + KineticsFactory(){} + }; + + + /** + * Create a new kinetics manager. + */ + inline Kinetics* newKineticsMgr(XML_Node& phase, + std::vector th, KineticsFactory* f=0) { + if (f == 0) { + f = KineticsFactory::factory(); + } + Kinetics* kin = f->newKinetics(phase, th); + return kin; + } + + /** + * Create a new kinetics manager. + */ + inline Kinetics* newKineticsMgr(std::string model, KineticsFactory* f=0) { + if (f == 0) { + f = KineticsFactory::factory(); + } + Kinetics* kin = f->newKinetics(model); + return kin; + } +} + +#endif + + diff --git a/Cantera/src/kinetics/RateCoeffMgr.h b/Cantera/src/kinetics/RateCoeffMgr.h new file mode 100755 index 000000000..792c14617 --- /dev/null +++ b/Cantera/src/kinetics/RateCoeffMgr.h @@ -0,0 +1,164 @@ +/** + * @file RateCoeffMgr.h + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_RATECOEFF_MGR_H +#define CT_RATECOEFF_MGR_H + +#include "utilities.h" +#include "RxnRates.h" + +#include "ct_defs.h" +#include "ctexceptions.h" + + +namespace Cantera { + + /** + * This rate coefficient manager supports one parameterization of + * the rate constant of any type. + */ + template + class Rate1 { + + public: + + Rate1(){} + virtual ~Rate1(){} + + /** + * Install a rate coefficient calculator. + * @param rxnNumber the reaction number + * @param rateType the rate type + * @param m length of coefficient array + * @param coefficients + */ + int install( int rxnNumber, int rateType, int m, + const doublereal* c ) { + /* + * Check to see if the current reaction rate type + * is the same as the type of this class. If not, + * throw an error condition. + */ + if (rateType != R::type()) + throw CanteraError("Rate1::install", + "incorrect rate coefficient type: "+int2str(rateType)); + + // if any coefficient other than the first is non-zero, or + // if alwaysComputeRate() is true, install a rate + // calculator and return the index of the calculator. + for (int i = 1; i < m; i++) { + if (c[i] != 0.0 || R::alwaysComputeRate() ) { + m_rxn.push_back(rxnNumber); + m_rates.push_back(R(m, c)); + return static_cast(m_rates.size()) - 1; + } + } + return -1; + } + + /** + * Return a reference to the nth rate coefficient calculator. + * Note that this is not the same as the calculator for + * reaction n, since reactions with constant rate coefficients + * do not have a calculator. + */ + const R& rateCoeff(int loc) const { return m_rates[loc]; } + + /** + * Update the concentration-dependent parts of the rate + * coefficient, if any. Used by class SurfaceArrhenius to + * compute coverage-dependent * modifications to the Arrhenius + * parameters. The array c should contain whatever data the + * particular rate coefficient class needs to update its + * rates. Note that this method does not return anything. To + * get the updated rates, method update must be called after + * the call to update_C. + */ + void update_C(const doublereal* c) { + TYPENAME_KEYWORD std::vector::iterator b = m_rates.begin(); + TYPENAME_KEYWORD std::vector::iterator e = m_rates.end(); + int i = 0; + for (; b != e; ++b, ++i) { + b->update_C(c); + } + } + + /** + * Write the rate coefficients into array values. Each + * calculator writes one entry in values, at the location + * specified by the reaction number when it was + * installed. Note that nothing will be done for reactions + * that have constant rates. The array values should be + * preloaded with the constant rate coefficients. + */ + void update(doublereal T, doublereal logT, doublereal* values) { + TYPENAME_KEYWORD std::vector::const_iterator b = m_rates.begin(); + TYPENAME_KEYWORD std::vector::const_iterator e = m_rates.end(); + doublereal recipT = 1.0/T; + int i = 0; + for (; b != e; ++b, ++i) { + // values[m_rxn[i]] = exp(b->update(logT, recipT)); + values[m_rxn[i]] = b->updateRC(logT, recipT); + } + } + + void writeUpdate(std::ostream & output1, std::string key) { + output1 << key; + } + + protected: + std::vector m_rates; + std::vector m_rxn; + array_fp m_const; // not used + }; + + + + /** + * This rate coefficient manager supports two parameterizations of + * any type. + */ + template + class Rate2 { + public: + + Rate2(){} + virtual ~Rate2(){} + + int install( int rxnNumber, int rateType, int m, + const doublereal* c) { + if (rateType == R1::type()) + return m_r1.install(rxnNumber, rateType, m, c); + else if (rateType == R2::type()) + return m_r2.install(rxnNumber, rateType, m, c); + else + throw CanteraError("Rate2::install", + "unknown rate coefficient type"); + return -1; + } + + void update(doublereal T, doublereal logT, + doublereal* values) { + m_r1.update(T, logT, values); + m_r2.update(T, logT, values); + } + + protected: + + Rate1 m_r1; + Rate1 m_r2; + }; + +} + +#endif diff --git a/Cantera/src/kinetics/ReactionData.h b/Cantera/src/kinetics/ReactionData.h new file mode 100755 index 000000000..79c30cb65 --- /dev/null +++ b/Cantera/src/kinetics/ReactionData.h @@ -0,0 +1,61 @@ +/** + * @file ReactionData.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_REACTION_DATA_H +#define CT_REACTION_DATA_H + +#include "reaction_defs.h" + +namespace Cantera { + + class ReactionData { + public: + ReactionData() { + reactionType = ELEMENTARY_RXN; + number = 0; + rxn_number = 0; + reversible = true; + rateCoeffType = ARRHENIUS; + falloffType = NONE; + error = 0; + equation = ""; + default_3b_eff = 1.0; + global = false; + beta = 0.0; + } + ~ReactionData(){} + + int reactionType; + int number, rxn_number; + vector_int reactants; + vector_int products; + vector_fp order; + vector_fp rstoich; + vector_fp pstoich; + std::vector rgroups; + std::vector pgroups; + std::map thirdBodyEfficiencies; + bool reversible; + int rateCoeffType; + vector_fp rateCoeffParameters; + vector_fp auxRateCoeffParameters; + int falloffType; + vector_fp falloffParameters; + int error; + std::string equation; + doublereal default_3b_eff; + vector_fp cov; + bool global; + doublereal beta; // for electrochemical reactions + }; +} + +#endif diff --git a/Cantera/src/kinetics/ReactionPath.cpp b/Cantera/src/kinetics/ReactionPath.cpp new file mode 100755 index 000000000..28fcc9eda --- /dev/null +++ b/Cantera/src/kinetics/ReactionPath.cpp @@ -0,0 +1,1017 @@ +/** + * @file ReactionPath.cpp + * Implementation file for classes used in reaction path analysis. + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "ReactionPath.h" +#include "Kinetics.h" +#include "reaction_defs.h" +#include "Group.h" + +using namespace std; + +namespace Cantera { + + /// add a path to or from this node + void SpeciesNode::addPath(Path* path) { + m_paths.push_back(path); + if (path->begin() == this) m_out += path->flow(); + else if (path->end() == this) m_in += path->flow(); + else throw CanteraError("addPath","path added to wrong node"); + } + + void SpeciesNode::printPaths() { + for (int i = 0; i < int(m_paths.size()); i++) { + cout << m_paths[i]->begin()->name << " --> " + << m_paths[i]->end()->name << ": " + << m_paths[i]->flow() << endl; + } + } + + + /** + * Construct a path connecting two species nodes. + */ + Path::Path(SpeciesNode* begin, SpeciesNode* end) + : m_a(begin), m_b(end), m_total(0.0) + { + begin->addPath(this); + end->addPath(this); + } + + + /** + * add a reaction to the path. Increment the flow from this + * reaction, the total flow, and the flow associated with this + * label. + */ + void Path::addReaction(int rxnNumber, doublereal value, + string label) { + m_rxn[rxnNumber] += value; + m_total += value; + if (label != "") m_label[label] += value; + } + + + /** + * Write the label for a path connecting two species, indicating + * the percent of the total flow due to each reaction. + */ + void Path::writeLabel(ostream& s, doublereal threshold) + { + int nn = static_cast(m_label.size()); + if (nn == 0) return; + doublereal v; + map::const_iterator i = m_label.begin(); + for (; i != m_label.end(); ++i) { + v = i->second/m_total; + if (nn == 1) s << i->first << "\\l"; + else if (v > threshold) { + s << i->first; + int percent = int(100*v + 0.5); + if (percent < 100) + s << " (" << percent << "%)\\l"; + else + s << "\\l"; + } + } + } + + + /** + * Default constructor. + */ + ReactionPathDiagram::ReactionPathDiagram() { + name = "reaction_paths"; + m_flxmax = 0.0; + bold_color = "blue"; + normal_color = "steelblue"; + dashed_color = "gray"; + dot_options = "center=1;"; + m_font = RXNPATH_FONT; + bold_min = 0.2; + dashed_max = 0.0; + label_min = 0.0; + threshold = 0.005; + flow_type = NetFlow; + scale = -1; + x_size = -1.0; + y_size = -1.0; + arrow_width = -5.0; + show_details = false; + arrow_hue = 0.6666; + title = ""; + m_local = -1; + } + + + /** + * Destructor. Deletes all nodes and paths in the diagram. + */ + ReactionPathDiagram::~ReactionPathDiagram() + { + // delete the nodes + map::const_iterator i = m_nodes.begin(); + for (; i != m_nodes.end(); ++i) delete i->second; + + // delete the paths + int nn = nPaths(); + int n; + for (n = 0; n < nn; n++) delete m_pathlist[n]; + } + + + vector_int ReactionPathDiagram::reactions() { + int i, npaths = nPaths(); + double flmax = 0.0, flxratio; + Path* p; + for (i = 0; i < npaths; i++) + { + p = path(i); + if (p->flow() > flmax) flmax = p->flow(); + } + m_rxns.clear(); + for (i = 0; i < npaths; i++) + { + p = path(i); + const Path::rxn_path_map& rxns = p->reactionMap(); + Path::rxn_path_map::const_iterator m = rxns.begin(); + for (; m != rxns.end(); ++m) { + flxratio = m->second/flmax; + if (flxratio > threshold) { + m_rxns[m->first] = 1; + } + } + } + vector_int r; + map::const_iterator begin = m_rxns.begin(); + for (; begin != m_rxns.end(); ++begin) r.push_back(abs(begin->first)); + return r; + } + + void ReactionPathDiagram::add(ReactionPathDiagram& d) { +// double f1, f2; +// int nnodes = nNodes(); +// if (nnodes != d.nNodes()) { +// throw CanteraError("ReactionPathDiagram::add", +// "number of nodes must be the same"); +// } + int np = nPaths(); + int n, k1, k2; + Path* p = 0; + for (n = 0; n < np; n++) { + p = path(n); + k1 = p->begin()->number; + k2 = p->end()->number; + p->setFlow(p->flow() + d.flow(k1,k2)); + } + } + + void ReactionPathDiagram::findMajorPaths(doublereal athreshold, int lda, + doublereal* a) { + int nn = nNodes(); + int n, m, k1, k2; + doublereal fl, netmax = 0.0; + for (n = 0; n < nn; n++) { + for (m = n+1; m < nn; m++) { + k1 = m_speciesNumber[n]; + k2 = m_speciesNumber[m]; + fl = fabs(netFlow(k1,k2)); + if (fl > netmax) netmax = fl; + } + } + for (n = 0; n < nn; n++) { + for (m = n+1; m < nn; m++) { + k1 = m_speciesNumber[n]; + k2 = m_speciesNumber[m]; + fl = fabs(netFlow(k1,k2)); + if (fl > athreshold*netmax) + a[lda*k1 + k2] = 1; + } + } + } + + void ReactionPathDiagram::writeData(ostream& s) { + double f1, f2; + int nnodes = nNodes(); + int i1, i2, k1, k2; + s << title << endl; + for (i1 = 0; i1 < nnodes; i1++) + { + k1 = m_speciesNumber[i1]; + s << m_nodes[k1]->name << " "; + } + s << endl; + for (i1 = 0; i1 < nnodes; i1++) + { + k1 = m_speciesNumber[i1]; + for (i2 = i1+1; i2 < nnodes; i2++) + { + k2 = m_speciesNumber[i2]; + f1 = flow(k1, k2); + f2 = flow(k2, k1); + //if (f1 > 0.001 || f2 > 0.001) { + s << m_nodes[k1]->name << " " << m_nodes[k2]->name + << " " << f1 << " " << -f2 << endl; + //} + } + } + } + + + /** + * Export the reaction path diagram. This method writes to stream + * \c s the commands for the 'dot' program in the \c GraphViz + * package from AT&T. (GraphViz may be downloaded from + * www.graphviz.org.) + * + * To generate a postscript reaction path diagram from the + * output of this method saved in file paths.dot, for example, give + * the command: + * \code + * dot -Tps paths.dot > paths.ps + * \endcode + * To generate a GIF image, replace -Tps with -Tgif + */ + void ReactionPathDiagram::exportToDot(ostream& s) + { + int i; + doublereal flxratio, flmax = 0.0, lwidth; + //s.flags(std::ios_base::showpoint+std::ios_base::fixed); + s.precision(3); + + // a directed graph + s << "digraph " << name << " {" << endl; + + // the graph will be no larger than x_size, y_size + if (x_size > 0.0) + { + if (y_size < 0.0) y_size = x_size; + s << "size = \"" + << x_size << "," + << y_size << "\";" + << endl; + } + + //s << "color = white;" << endl; + if (dot_options != "") + s << dot_options << endl; + + int npaths = nPaths(); + Path* p; + + int nnodes = nNodes(); + int kbegin, kend, i1, i2, k1, k2; + double flx; + + // draw paths representing net flows + if (flow_type == NetFlow) + { + + // if no scale was specified, normalize + // net flows by the maximum net flow + if (scale <= 0.0) + { + for (i1 = 0; i1 < nnodes; i1++) + { + k1 = m_speciesNumber[i1]; + node(k1)->visible = false; + for (i2 = i1+1; i2 < nnodes; i2++) + { + k2 = m_speciesNumber[i2]; + flx = netFlow(k1, k2); + if (flx < 0.0) flx = -flx; + if (flx > flmax) flmax = flx; + } + } + } + else + flmax = scale; + + if (flmax < 1.e-10) flmax = 1.e-10; + + // loop over all unique pairs of nodes + + for (i1 = 0; i1 < nnodes; i1++) + { + k1 = m_speciesNumber[i1]; + for (i2 = i1+1; i2 < nnodes; i2++) + { + k2 = m_speciesNumber[i2]; + flx = netFlow(k1, k2); + if (m_local >= 0) { + if (k1 != m_local && k2 != m_local) flx = 0.0; + } + if (flx != 0.0) + { + // set beginning and end of the path based on the + // sign of the net flow + + if (flx > 0.0) + { + kbegin = k1; + kend = k2; + flxratio = flx/flmax; + } + else + { + kbegin = k2; + kend = k1; + flxratio = -flx/flmax; + } + + // write out path specification if the net flow + // is greater than the threshold + + if (flxratio >= threshold) + { + // make nodes visible + node(kbegin)->visible = true; + node(kend)->visible = true; + + s << "s" << kbegin << " -> s" << kend; + + if (arrow_width < 0) { + lwidth = 1.0 - 4.0 + * log10(flxratio/threshold)/log10(threshold) + 1.0; + s << "[fontname=\""+m_font+"\", style=\"setlinewidth(" + << lwidth << ")\""; + s << ", arrowsize=" + << min(6.0, 0.5*lwidth); + } + else + { + s << ", style=\"setlinewidth(" + << arrow_width << ")\""; + s << ", arrowsize=" << flxratio + 1; + } + + doublereal hue = 0.7; + doublereal bright = 0.9; + s << ", color=" << "\"" << hue << ", " + << flxratio + 0.5 + << ", " << bright << "\"" << endl; + + if (flxratio > label_min) + { + s << ", label=\" " << flxratio; + if (show_details) { + if (flow(kbegin, kend) > 0.0) { + s << "\\l fwd: " + << flow(kbegin, kend)/flmax << "\\l"; + path(kbegin, kend)->writeLabel(s); + } + if (flow(kend, kbegin) > 0.0) { + s << " \\l rev: " + << flow(kend,kbegin)/flmax << "\\l"; + path(kend, kbegin)->writeLabel(s); + } + } + s << "\""; + } + s << "];" << endl; + } + } + } + } + } + + else { + for (i = 0; i < npaths; i++) + { + p = path(i); + if (p->flow() > flmax) flmax = p->flow(); + } + + for (i = 0; i < npaths; i++) { + p = path(i); + flxratio = p->flow()/flmax; + if (m_local >= 0) { + if (p->begin()->number != m_local + && p->end()->number != m_local) flxratio = 0.0; + } + if (flxratio > threshold) { + p->begin()->visible = true; + p->end()->visible = true; + s << "s" << p->begin()->number + << " -> s" << p->end()->number; + + if (arrow_width < 0) { + lwidth = 1.0 - 4.0 * log10(flxratio/threshold)/log10(threshold) + + 1.0; + s << "[fontname=\""+m_font+"\", style=\"setlinewidth(" + //<< 1.0 - arrow_width*flxratio + << lwidth + << ")\""; + s << ", arrowsize=" + << min(6.0, 0.5*lwidth); // 1 - arrow_width*flxratio; + } + else + { + s << ", style=\"setlinewidth(" + << arrow_width << ")\""; + s << ", arrowsize=" << flxratio + 1; + } + doublereal hue = 0.7; //2.0/(1.0 + pow(log10(flxratio),2)) ; + doublereal bright = 0.9; + s << ", color=" << "\"" << hue << ", " << flxratio + 0.5 + << ", " << bright << "\"" << endl; + + if (flxratio > label_min) { + s << ", label = \" " << flxratio; + if (show_details) { + s << "\\l"; p->writeLabel(s); + } + s << "\""; + } + s << "];" << endl; + } + } + } + s.precision(2); + map::const_iterator b = m_nodes.begin(); + for (; b != m_nodes.end(); ++b) { + if (b->second->visible) { + s << "s" << b->first << " [ fontname=\""+m_font+"\", label=\"" << b->second->name + //<< " \\n " << b->second->value + << "\"];" << endl; + } + } + s << " label = " << "\"" << "Scale = " + << flmax << "\\l " << title << "\";" << endl; //created with Cantera (www.cantera.org)\\l\";" + s << " fontname = \""+m_font+"\";" << endl << "}" << endl; + } + + + void ReactionPathDiagram::addNode(int k, string nm, doublereal x) { + if (!m_nodes[k]) { + m_nodes[k] = new SpeciesNode; + m_nodes[k]->number = k; + m_nodes[k]->name = nm; + m_nodes[k]->value = x; + m_speciesNumber.push_back(k); + } + } + + void ReactionPathDiagram::linkNodes(int k1, int k2, int rxn, + doublereal value, string legend) { + SpeciesNode* begin = m_nodes[k1]; + SpeciesNode* end = m_nodes[k2]; + Path* ff = m_paths[k1][k2]; + if (!ff) { + ff= new Path(begin, end); + m_paths[k1][k2] = ff; + m_pathlist.push_back(ff); + } + ff->addReaction(rxn, value, legend); + m_rxns[rxn] = 1; + if (ff->flow() > m_flxmax) m_flxmax = ff->flow(); + } + + vector_int ReactionPathDiagram::species(){ + return m_speciesNumber; + } + + + /** + * analyze a reaction to determine which reactants lead to which products. + */ + int ReactionPathBuilder::findGroups(ostream& logfile, Kinetics& s) + { + m_groups.resize(m_nr); + map net; + + for (int i = 0; i < m_nr; i++) // loop over reactions + { + logfile << endl << "Reaction " << i+1 << ": " + << s.reactionString(i); + + int nrnet = m_reac[i].size(); + int npnet = m_prod[i].size(); + const vector_int& r = s.reactants(i); + const vector_int& p = s.products(i); + + int nr = s.reactants(i).size(); + int np = s.products(i).size(); + + Group b0, b1, bb; + + vector& e = m_elementSymbols; + + const vector& rgroups = s.reactantGroups(i); + const vector& pgroups = s.productGroups(i); + + + if (m_determinate[i]) { + logfile << " ... OK." << endl; + } + + else if (rgroups.size() > 0) { + logfile << " ... specified groups." << endl; + int nrg = static_cast(rgroups.size()); + int npg = static_cast(pgroups.size()); + int kr, kp, ngrpr, ngrpp; + Group gr, gp; + + if (nrg != nr || npg != np) return -1; + + // loop over reactants + for (int igr = 0; igr < nrg; igr++) { + kr = r[igr]; + ngrpr = static_cast(rgroups[igr].size()); + + // loop over products + for (int igp = 0; igp < npg; igp++) { + kp = p[igp]; + ngrpp = static_cast(pgroups[igp].size()); + + // loop over pairs of reactant and product groups + for (int kgr = 0; kgr < ngrpr; kgr++) { + gr = Group(rgroups[igr][kgr]); + for (int kgp = 0; kgp < ngrpp; kgp++) { + gp = Group(pgroups[igp][kgp]); + if (gr == gp) { + m_transfer[i][kr][kp] = gr; + } + } + } + } + } + } + + else if (nrnet == 2 && npnet == 2) + { + // indices for the two reactants + int kr0 = m_reac[i][0]; + int kr1 = m_reac[i][1]; + + // indices for the two products + int kp0 = m_prod[i][0]; + int kp1 = m_prod[i][1]; + + // references to the Group objects representing the + // reactants + const Group& r0 = m_sgroup[kr0]; + const Group& r1 = m_sgroup[kr1]; + const Group& p0 = m_sgroup[kp0]; + const Group& p1 = m_sgroup[kp1]; + + const Group *group_a0=0, *group_b0=0, *group_c0=0, + *group_a1=0, *group_b1=0, *group_c1=0; + b0 = p0 - r0; + b1 = p1 - r0; + if (b0.valid() && b1.valid()) { + logfile << " ... ambiguous." << endl; + } + else if (!b0.valid() && !b1.valid()) { + logfile << " ... cannot express as A + BC = AB + C" << endl; + } + else logfile << endl; + + if (b0.valid()) { + if (b0.sign() > 0) { + group_a0 = &r0; + group_b0 = &b0; + group_c0 = &p1; + m_transfer[i][kr0][kp0] = r0; + m_transfer[i][kr1][kp0] = b0; + m_transfer[i][kr1][kp1] = p1; + } + else { + group_a0 = &r1; + group_c0 = &p0; + b0 *= -1; + group_b0 = &b0; + m_transfer[i][kr1][kp1] = r1; + m_transfer[i][kr0][kp1] = b0; + m_transfer[i][kr0][kp0] = p0; + } + logfile << " "; + group_a0->fmt(logfile,e); + logfile << " + "; + group_b0->fmt(logfile,e); + group_c0->fmt(logfile,e); + logfile << " = "; + group_a0->fmt(logfile,e); + group_b0->fmt(logfile,e); + logfile << " + "; + group_c0->fmt(logfile,e); + if (b1.valid()) + logfile << " [<= default] " << endl; + else + logfile << endl; + } + + + if (b1.valid()) { + if (b1.sign() > 0) { + group_a1 = &r0; + group_b1 = &b1; + group_c1 = &p0; + if (!b0.valid()) { + m_transfer[i][kr0][kp1] = r0; + m_transfer[i][kr1][kp1] = b0; + m_transfer[i][kr1][kp0] = p0; + } + } + else { + group_a1 = &r1; + group_c1 = &p1; + b1 *= -1; + group_b1 = &b1; + if (!b0.valid()) { + m_transfer[i][kr1][kp0] = r1; + m_transfer[i][kr0][kp0] = b0; + m_transfer[i][kr0][kp1] = p1; + } + } + logfile << " "; + group_a1->fmt(logfile,e); logfile << " + "; + group_b1->fmt(logfile,e); + group_c1->fmt(logfile,e); logfile << " = "; + group_a1->fmt(logfile,e); group_b1->fmt(logfile,e); + logfile << " + "; group_c1->fmt(logfile,e); + logfile << endl; + } + } + else { + logfile << "... cannot parse. [ignored]" << endl; + } + } + return 1; + } + + void ReactionPathBuilder::writeGroup(ostream& out, const Group& g) + { + g.fmt(out, m_elementSymbols); + } + + void ReactionPathBuilder::findElements(Kinetics& kin) { + + string ename; + m_enamemap.clear(); + m_nel = 0; + int i, np = kin.nPhases(); + ThermoPhase* p; + map enamemap; + for (i = 0; i < np; i++) { + p = &kin.thermo(i); + // iterate over the elements in this phase + int m, nel = p->nElements(); + for (m = 0; m < nel; m++) { + ename = p->elementName(m); + + // if no entry is found for this element name, then + // it is a new element. In this case, add the name + // to the list of names, increment the element count, + // and add an entry to the name->(index+1) map. + if (m_enamemap.find(ename) == m_enamemap.end()) { + m_enamemap[ename] = m_nel + 1; + m_elementSymbols.push_back(ename); + m_nel++; + } + } + } + m_atoms.resize(kin.nTotalSpecies(), m_nel, 0.0); + string sym; + int k, ip, nsp, mlocal, kp, m; + // iterate over the elements + for (m = 0; m < m_nel; m++) { + sym = m_elementSymbols[m]; + k = 0; + // iterate over the phases + for (ip = 0; ip < np; ip++) { + phase_t* p = &kin.thermo(ip); + nsp = p->nSpecies(); + mlocal = p->elementIndex(sym); + for (kp = 0; kp < nsp; kp++) { + if (mlocal >= 0) { + m_atoms(k, m) = p->nAtoms(kp, mlocal); + } + k++; + } + } + } + } + + + + int ReactionPathBuilder::init(ostream& logfile, Kinetics& kin) { + //m_warn.clear(); + m_transfer.clear(); + + //const Kinetics::thermo_t& ph = kin.thermo(); + + m_elementSymbols.clear(); + findElements(kin); + //m_nel = ph.nElements(); + m_ns = kin.nTotalSpecies(); //ph.nSpecies(); + m_nr = kin.nReactions(); + + int m, i; + //for (m = 0; m < m_nel; m++) { + // m_elementSymbols.push_back(ph.elementName(m)); + //} + + // all reactants / products, even ones appearing on both sides + // of the reaction + // mod 8/18/01 dgg + vector allProducts; + vector allReactants; + for (i = 0; i < m_nr; i++) { + allReactants.push_back(kin.reactants(i)); + allProducts.push_back(kin.products(i)); + } + + // m_reac and m_prod exclude indices for species that appear on + // both sides of the reaction, so that the diagram contains no loops. + + m_reac.resize(m_nr); + m_prod.resize(m_nr); + + m_ropf.resize(m_nr); + m_ropr.resize(m_nr); + m_determinate.resize(m_nr); + + m_x.resize(m_ns); // not currently used ? + m_elatoms.resize(m_nel, m_nr); + + int nr, np, n, k; + int nmol; + map net; + + for (i = 0; i < m_nr; i++) { + + // construct the lists of reactant and product indices, not + // including molecules that appear on both sides. + + m_reac[i].clear(); + m_prod[i].clear(); + net.clear(); + nr = allReactants[i].size(); + np = allProducts[i].size(); + for (int ir = 0; ir < nr; ir++) net[allReactants[i][ir]]--; + for (int ip = 0; ip < np; ip++) net[allProducts[i][ip]]++; + + for (k = 0; k < m_ns; k++) { + if (net[k] < 0) { + nmol = -net[k]; + for (int jr = 0; jr < nmol; jr++) m_reac[i].push_back(k); + } + else if (net[k] > 0) { + nmol = net[k]; + for (int jp = 0; jp < nmol; jp++) m_prod[i].push_back(k); + } + } + + int nrnet = m_reac[i].size(); + // int npnet = m_prod[i].size(); + + // compute number of atoms of each element in each reaction, + // excluding molecules that appear on both sides of the + // reaction. We only need to compute this for the reactants, + // since the elements are conserved. + + for (n = 0; n < nrnet; n++) { + k = m_reac[i][n]; + for (int m = 0; m < m_nel; m++) { + m_elatoms(m,i) += m_atoms(k,m); //ph.nAtoms(k,m); + } + } + } + + // build species groups + vector_int comp(m_nel); + m_sgroup.resize(m_ns); + int j; + for (j = 0; j < m_ns; j++) { + for (int m = 0; m < m_nel; m++) comp[m] = int(m_atoms(j,m)); //ph.nAtoms(j,m)); + m_sgroup[j] = Group(comp); + } + + + // determine whether or not the reaction is "determinate", meaning + // that there is no ambiguity about which reactant is the source for + // any element in any product. This is false if more than one + // reactant contains a given element, *and* more than one product + // contains the element. In this case, additional information is + // needed to determine the partitioning of the reactant atoms of + // that element among the products. + + int nar, nap; + for (i = 0; i < m_nr; i++) { + nr = m_reac[i].size(); + np = m_prod[i].size(); + m_determinate[i] = true; + for (m = 0; m < m_nel; m++) { + nar = 0; + nap = 0; + for (j = 0; j < nr; j++) { + // if (ph.nAtoms(m_reac[i][j],m) > 0) nar++; + if (m_atoms(m_reac[i][j],m) > 0) nar++; + } + for (j = 0; j < np; j++) { + if (m_atoms(m_prod[i][j],m) > 0) nap++; + } + if (nar > 1 && nap > 1) { + m_determinate[i] = false; break; + } + } + } + + findGroups(logfile, kin); + return 1; + } + + string reactionLabel(int i, int kr, int nr, const vector_int& slist, + const Kinetics& s) { + + //int np = s.nPhases(); + string label = ""; + int l; + for (l = 0; l < nr; l++) { + if (l != kr) + label += " + "+ s.kineticsSpeciesName(slist[l]); + } + if (s.reactionType(i) == THREE_BODY_RXN) + label += " + M "; + else if (s.reactionType(i) == FALLOFF_RXN) + label += " (+ M)"; + return label; + } + + + int ReactionPathBuilder::build(Kinetics& s, + string element, ostream& output, ReactionPathDiagram& r, bool quiet) + { + int i, nr, np, kr, kp, kkr, kkp; + doublereal f, ropf, ropr, fwd, rev; + string fwdlabel, revlabel; + map warn; + + doublereal threshold = 0.0; + bool fwd_incl, rev_incl, force_incl; + + // const Kinetics::thermo_t& ph = s.thermo(); + int m = m_enamemap[element]-1; //ph.elementIndex(element); + + r.element = element; + if (m < 0) return -1; + + //int k; + int kk = s.nTotalSpecies(); + + s.getFwdRatesOfProgress(DATA_PTR(m_ropf)); + s.getRevRatesOfProgress(DATA_PTR(m_ropr)); + + //ph.getMoleFractions(m_x.begin()); + + //doublereal sum = 0.0; + //for (k = 0; k < kk; k++) { + // sum += m_x[k] * ph.nAtoms(k,m); + //} + //sum *= ph.molarDensity(); + + // species explicitly included or excluded + vector& in_nodes = r.included(); + vector& out_nodes = r.excluded(); + int nin = static_cast(in_nodes.size()); + int nout = static_cast(out_nodes.size()); + + vector_int status; + status.resize(kk,0); + for (int ni = 0; ni < nin; ni++) + status[s.kineticsSpeciesIndex(in_nodes[ni])] = 1; + for (int ne = 0; ne < nout; ne++) + status[s.kineticsSpeciesIndex(out_nodes[ne])] = -1; + + for (i = 0; i < m_nr; i++) + { + ropf = m_ropf[i]; + ropr = m_ropr[i]; + + // loop over reactions involving element m + if (m_elatoms(m, i) > 0) + { + nr = m_reac[i].size(); + np = m_prod[i].size(); + + for (kr = 0; kr < nr; kr++) + { + kkr = m_reac[i][kr]; + int l; + + fwdlabel = reactionLabel(i, kr, nr, m_reac[i], s); + + for (kp = 0; kp < np; kp++) + { + kkp = m_prod[i][kp]; + revlabel = ""; + for (l = 0; l < np; l++) { + if (l != kp) + revlabel += " + "+ s.kineticsSpeciesName(m_prod[i][l]); + } + if (s.reactionType(i) == THREE_BODY_RXN) + revlabel += " + M "; + else if (s.reactionType(i) == FALLOFF_RXN) + revlabel += " (+ M)"; + + + // calculate the flow only for pairs that are + // not the same species, both contain atoms of + // element m, and both are allowed to appear in + // the diagram + + if ((kkr != kkp) && (m_atoms(kkr,m) > 0 + && m_atoms(kkp,m) > 0) + && status[kkr] >= 0 && status[kkp] >= 0) + { + + // if neither species contains the full + // number of atoms of element m in the + // reaction, then we must consider the + // type of reaction to determine which + // reactant species was the source of a + // given m-atom in the product + + if ( (m_atoms(kkp,m) < m_elatoms(m, i)) && + (m_atoms(kkr,m) < m_elatoms(m, i)) ) + { + map >& g = m_transfer[i]; + if (g.empty()) { + if (!warn[i]) { + if (!quiet) { + output << endl; + output << "*************** REACTION IGNORED ***************" << endl; + output << "Warning: no rule to determine partitioning of " << element + << endl << " in reaction " << s.reactionString(i) << "." << endl + << "*************** REACTION IGNORED **************" << endl; + output << endl; + warn[i] = 1; + } + } + f = 0.0; + } + else { + if (!g[kkr][kkp]) f = 0.0; + else f = g[kkr][kkp].nAtoms(m); + } + } + + // no ambiguity about where the m-atoms come + // from or go to. Either all reactant m atoms + // end up in one product, or only one reactant + // contains all the m-atoms. In either case, + // the number of atoms transferred is given by + // the same expression. + + else { + f = m_atoms(kkp,m) * m_atoms(kkr,m) / m_elatoms(m, i); + } + + fwd = ropf*f; + rev = ropr*f; + force_incl = ((status[kkr] == 1) || (status[kkp] == 1)); + + fwd_incl = ((fwd > threshold) || + (fwd > 0.0 && force_incl)); + rev_incl = ((rev > threshold) || + (rev > 0.0 && force_incl)); + if (fwd_incl || rev_incl) + { + if (!r.hasNode(kkr)) { + r.addNode(kkr, s.kineticsSpeciesName(kkr), m_x[kkr]); + } + if (!r.hasNode(kkp)) { + r.addNode(kkp, s.kineticsSpeciesName(kkp), m_x[kkp]); + } + } + if (fwd_incl) { + r.linkNodes(kkr, kkp, i, fwd, fwdlabel); + } + if (rev_incl) { + r.linkNodes(kkp, kkr, -i, rev, revlabel); + } + } + } + } + } + } + return 1; + } + + +} diff --git a/Cantera/src/kinetics/ReactionPath.h b/Cantera/src/kinetics/ReactionPath.h new file mode 100755 index 000000000..f2ccf67d6 --- /dev/null +++ b/Cantera/src/kinetics/ReactionPath.h @@ -0,0 +1,278 @@ +/** + * @file ReactionPath.h + * + * Classes for reaction path analysis. + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_RXNPATH_H +#define CT_RXNPATH_H + +// Cantera includes +#include "ct_defs.h" +#include "DenseMatrix.h" +#include "Group.h" +#include "Kinetics.h" + +namespace Cantera { + + enum flow_t { NetFlow, OneWayFlow }; + + Group parseGroupString(std::string str, std::vector& esyms); + + // forward references + class Path; + + /** + * Nodes in reaction path graphs. + */ + class SpeciesNode { + public: + + typedef std::vector path_list; + + /// Default constructor + SpeciesNode() : number(-1), name(""), value(0.0), + visible(false), m_in(0.0), m_out(0.0) {} + + /// Destructor + virtual ~SpeciesNode() {} + + // public attributes + int number; ///< Species number + std::string name; ///< Label on graph + doublereal value; ///< May be used to set node appearance + bool visible; ///< Visible on graph; + + + // public methods + + /** + * @name References. + * Return a reference to a path object connecting this node + * to another node. + */ + //@{ + Path* path(int n) { return m_paths[n]; } + const Path* path(int n) const { return m_paths[n]; } + //@} + + + /// Total number of paths to or from this node + int nPaths() const { return static_cast(m_paths.size()); } + + /// add a path to or from this node + void addPath(Path* path); + + double outflow() {return m_out;} + double inflow() {return m_in;} + double netOutflow() {return m_out - m_in;} + + void printPaths(); + + + protected: + double m_in, m_out; + path_list m_paths; + }; + + + + class Path { + + public: + + typedef std::map rxn_path_map; + + /** + * Constructor. Construct a one-way path from + * \c begin to \c end. + */ + Path(SpeciesNode* begin, SpeciesNode* end); + + /// Destructor + virtual ~Path() {} + + void addReaction(int rxnNumber, doublereal value, std::string label = ""); + + /// Upstream node. + const SpeciesNode* begin() const { return m_a; } + SpeciesNode* begin() { return m_a; } + + /// Downstream node. + const SpeciesNode* end() const { return m_b; } + SpeciesNode* end() { return m_b; } + + /** + * If \c n is one of the nodes this path connects, then + * the other node is returned. Otherwise zero is returned. + */ + SpeciesNode* otherNode(SpeciesNode* n) { + return (n == m_a ? m_b : (n == m_b ? m_a : 0)); + } + + /// The total flow in this path + doublereal flow() { return m_total; } + void setFlow(doublereal v) { m_total = v; } + + /// Number of reactions contributing to this path + int nReactions() { + return static_cast(m_rxn.size()); + } + + /// Map from reaction number to flow from that reaction in this path. + const rxn_path_map& reactionMap() { return m_rxn; } + + void writeLabel(std::ostream& s, doublereal threshold = 0.005); + + protected: + + std::map m_label; + SpeciesNode *m_a, *m_b; + rxn_path_map m_rxn; + doublereal m_total; + }; + + + /** + * Reaction path diagrams (graphs). + */ + class ReactionPathDiagram { + + public: + + ReactionPathDiagram(); + + virtual ~ReactionPathDiagram(); + + /// The largest one-way flow value in any path + doublereal maxFlow() { return m_flxmax; } + + /// The net flow from node \c k1 to node \c k2 + doublereal netFlow(int k1, int k2) { + return flow(k1, k2) - flow(k2, k1); + } + + /// The one-way flow from node \c k1 to node \c k2 + doublereal flow(int k1, int k2) { + return (m_paths[k1][k2] ? m_paths[k1][k2]->flow() : 0.0); + } + + /// True if a node for species k exists + bool hasNode(int k) { + return (m_nodes[k] != 0); + } + + void writeData(std::ostream& s); + void exportToDot(std::ostream& s); + void add(ReactionPathDiagram& d); + SpeciesNode* node(int k) { return m_nodes[k]; } + Path* path(int k1, int k2) { return m_paths[k1][k2]; } + Path* path(int n) { return m_pathlist[n]; } + int nPaths() { return static_cast(m_pathlist.size()); } + int nNodes() { return static_cast(m_nodes.size()); } + + void addNode(int k, std::string nm, doublereal x = 0.0); + + void displayOnly(int k=-1) { m_local = k; } + + void linkNodes(int k1, int k2, int rxn, doublereal value, + std::string legend = ""); + + void include(std::string aaname) { m_include.push_back(aaname); } + void exclude(std::string aaname) { m_exclude.push_back(aaname); } + void include(std::vector& names) { + int n = static_cast(names.size()); + for (int i = 0; i < n; i++) m_include.push_back(names[i]); + } + void exclude(std::vector& names) { + int n = static_cast(names.size()); + for (int i = 0; i < n; i++) m_exclude.push_back(names[i]); + } + std::vector& included() { return m_include; } + std::vector& excluded() { return m_exclude; } + vector_int species(); + vector_int reactions(); + void findMajorPaths(doublereal threshold, int lda, doublereal* a); + void setFont(std::string font) { + m_font = font; + } + // public attributes + + std::string title; + std::string bold_color; + std::string normal_color; + std::string dashed_color; + std::string element; + std::string m_font; + doublereal threshold, + bold_min, dashed_max, label_min; + doublereal x_size, y_size; + std::string name, dot_options; + flow_t flow_type; + double scale; + double arrow_width; + bool show_details; + double arrow_hue; + + protected: + + doublereal m_flxmax; + std::map > m_paths; + std::map m_nodes; + std::vector m_pathlist; + std::vector m_include; + std::vector m_exclude; + vector_int m_speciesNumber; + std::map m_rxns; + int m_local; + }; + + + + class ReactionPathBuilder { + + public: + ReactionPathBuilder() {} + virtual ~ReactionPathBuilder() {} + + int init(std::ostream& logfile, Kinetics& s); + + int build(Kinetics& s, std::string element, std::ostream& output, + ReactionPathDiagram& r, bool quiet=false); + + int findGroups(std::ostream& logfile, Kinetics& s); + + void writeGroup(std::ostream& out, const Group& g); + + protected: + void findElements(Kinetics& kin); + + int m_nr; + int m_ns; + int m_nel; + vector_fp m_ropf; + vector_fp m_ropr; + array_fp m_x; + std::vector m_reac; + std::vector m_prod; + DenseMatrix m_elatoms; + std::vector > m_groups; + std::vector m_sgroup; + std::vector m_elementSymbols; + // std::map m_warn; + std::map > > m_transfer; + std::vector m_determinate; + Array2D m_atoms; + std::map m_enamemap; + }; + +} + +#endif diff --git a/Cantera/src/kinetics/ReactionStoichMgr.h b/Cantera/src/kinetics/ReactionStoichMgr.h new file mode 100644 index 000000000..c3bb2d7e9 --- /dev/null +++ b/Cantera/src/kinetics/ReactionStoichMgr.h @@ -0,0 +1,241 @@ +/** + * @file ReactionStoichMgr.h + * + * Header file declaring class ReactionStoichMgr. + */ + +/* + * $Author$ + * $Revision$ + * $Date$ + */ + +#ifndef CT_RXN_STOICH +#define CT_RXN_STOICH + +#include "ct_defs.h" + +namespace Cantera { + + class StoichManagerN; + class ReactionData; + + /** + * Reaction mechanism stoichiometry manager. This is an internal class used + * by kinetics manager classes, and is not meant for direct use in + * user programs. + * + * Class ReactionStoichMgr handles the calculation of quantities involving + * the stoichiometry of a set of reactions. The reactions must have integer + * stoichiometric coefficients. Specifically, its methods compute + * - species creation rates + * - species destruction rates + * - species net production rates + * - the change in molar species properties in the reactions + * - concentration products + * + * To use this class, method 'add' is first used to add each reaction. + * Once all reactions have been added, the methods that compute various + * quantities may be called. + * + * The nomenclature used below to document the methods is as follows. + * + * - \f$ N_r \f$ + * Integer reactant stoichiometric coefficient matrix. The (k,i) + * element of this matrix is the stoichiometric coefficient of + * species \i k as a reactant in reaction \i i. + * - \f$ N_p \f$ + * Integer product stoichiometric coefficient matrix. The (k,i) + * element of this matrix is the stoichiometric coefficient of + * species \i k as a product in reaction \i i. + * - \f$ Q_{\rm fwd} \f$ + * Vector of length I of forward rates of progress. + * - \f$ Q_{\rm rev} \f$ + * Vector of length I of reverse rates of progress. + * - \f$ C \f$ + * Vector of K species creation rates. + * - \f$ D \f$ + * Vector of K species destruction rates. + * - \f$ W = C - D \f$ + * Vector of K species net production rates. + * + */ + class ReactionStoichMgr { + + public: + + /// Constructor. + ReactionStoichMgr(); + + /// Destructor. + virtual ~ReactionStoichMgr(); + + /** + * Add a reaction with mass-action kinetics. Vectors + * 'reactants' and 'products' contain the integer species + * indices of the reactants and products, respectively. Note + * that if more than one molecule of a given species is + * involved in the reaction, then its index is repeated. + * + * For example, suppose a reaction mechanism involves the + * species N2, O2, O, N, NO. N2 is assigned index number 0, O2 + * number 1, and so on through NO with number 4. Then the + * representation of the following reactions is as shown here. + * + * - N + O = NO + * - reactants: (3, 2) + * - products: (4) + * + * - O + O = O2 + * - reactants: (2, 2) [ note repeated index ] + * - products: (1) + * + * @param rxn Reaction number. This number will be used as the index + * into the rate of progess vector in the methods below. + * @param reactants vector of integer reactant indices + * @param products vector of integer product indices + * @param reversible true if the reaction is reversible, false otherwise + */ + virtual void add(int rxn, const vector_int& reactants, const vector_int& products, + bool reversible); + + /** + * Add a reaction with specified, possibly non-integral, reaction orders. + * @param rxn Reaction number + * @param reactants vector of integer reactant indices + * @param products vector of integer product indices + * @param reversible true if the reaction is reversible, false otherwise. + * If the reaction is reversible, its reverse rate will be computed from + * the reaction stoichiometry. + * @param fwdOrder reaction orders for the reactants. This vector must + * be the same length as 'reactants,' and the reaction orders are for the + * species with index in the corresponding location in 'reactants.' + * + */ + // void add(int rxn, const vector_int& reactants, const vector_int& products, + // bool reversible, const vector_fp& fwdOrder); + + + virtual void add(int rxn, const ReactionData& r); + + /** + * Species creation rates. + * Given the arrays of the forward and reverse rates of + * progress for all reactions, compute the species creation + * rates, given by + * \f[ + * C = N_p Q_f + N_r Q_r. + * \f] + */ + virtual void getCreationRates(int nSpecies, + const doublereal* fwdRatesOfProgress, + const doublereal* revRatesOfProgress, + doublereal* creationRates); + + + /** + * Species destruction rates. + * Given the arrays of the forward and reverse rates of + * progress for all reactions, compute the species destruction + * rates, given by + * \f[ + * D = N_r Q_f + N_p Q_r, + * \f] + * Note that the stoichiometric coefficient matrices are very sparse, integer + * matrices. + */ + virtual void getDestructionRates(int nSpecies, + const doublereal* fwdRatesOfProgress, + const doublereal* revRatesOfProgress, + doublereal* destructionRates); + + + /** + * Given the array of the net rates of progress for all + * reactions, compute the species net production rates and + * return them in array w. + */ + /** + * Species net production rates. + * Given the array of the net rates of + * progress for all reactions, compute the species net production + * rates, given by + * \f[ + * W = (N_r - N_p) Q_{\rm net}, + * \f] + */ + virtual void getNetProductionRates(int nsp, const doublereal* ropnet, doublereal* w); + + + + /** + * Change of a molar species property in a reaction. Given an + * array of species properties 'g', return in array 'dg' the + * change in this quantity in the reactions. Array 'g' must + * have a length at least as great as the number of species, + * and array 'dg' must have a length as great as the total + * number of reactions. + */ + virtual void getReactionDelta(int nReactions, + const doublereal* g, + doublereal* dg); + + + /** + * Given an array of species properties 'g', return in array + * 'dg' the change in this quantity in the reversible + * reactions. Array 'g' must have a length at least as great + * as the number of species, and array 'dg' must have a length + * as great as the total number of reactions. This method + * only computes 'dg' for the reversible reactions, and the + * entries of 'dg' for the irreversible reactions are + * unaltered. This is primarily designed for use in + * calculating reveerse rate coefficients from thermochemistry + * for reversible reactions. + */ + virtual void getRevReactionDelta(int nr, const doublereal* g, doublereal* dg); + + + /** + * Given an array of concentrations C, multiply the entries in array R by + * the concentration products for the reactants: + * \f[ + * R_i = R_i * \prod_k C_k^{o_{k,i}} + * \f] + * Here \f$ o_{k,i} \f$ is the reaction order of species k in reaction i. + */ + virtual void multiplyReactants(const doublereal* C, doublereal* R); + + + /** + * Given an array of concentrations C, multiply the entries in array R by + * the concentration products for the products: + * \f[ + * R_i = R_i * \prod_k C_k^{\nu^{(p)}_{k,i}} + * \f] + * Here \f$ \nu^{(p)}_{k,i} \f$ is the product stoichiometric coefficient + * of species k in reaction i. + */ + virtual void multiplyRevProducts(const doublereal* c, doublereal* r); + + virtual void write(std::string filename); + + protected: + + void writeCreationRates(std::ostream& f); + void writeDestructionRates(std::ostream& f); + void writeNetProductionRates(std::ostream& f); + void writeMultiplyReactants(std::ostream& f); + void writeMultiplyRevProducts(std::ostream& f); + StoichManagerN* m_reactants; + StoichManagerN* m_revproducts; + StoichManagerN* m_irrevproducts; + vector_fp m_dummy; + +#ifdef INCL_STOICH_WRITER + StoichWriter* m_rwriter; +#endif + }; +} + +#endif diff --git a/Cantera/src/kinetics/RxnRates.h b/Cantera/src/kinetics/RxnRates.h new file mode 100755 index 000000000..44f0c6a70 --- /dev/null +++ b/Cantera/src/kinetics/RxnRates.h @@ -0,0 +1,372 @@ +/** + * @file RxnRates.h + * + */ + +/* $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_RXNRATES_H +#define CT_RXNRATES_H + +#include "reaction_defs.h" +#include "ctexceptions.h" + +namespace Cantera { + + /** + * A rate coefficient of the form + * \f[ + * A T^b \exp (-E/RT) + * \f] + */ + class Arrhenius { + + public: + + /// return the rate coefficient type. + static int type(){ return ARRHENIUS; } + + /// Default constructor. + Arrhenius() : + m_logA(-1.0E300), + m_b (0.0), + m_E (0.0), + m_A(0.0) {} + + /// Constructor with Arrhenius parameters specified with an array. + Arrhenius(int csize, const doublereal* c) : + m_b (c[1]), + m_E (c[2]), + m_A (c[0]) + { + if (m_A <= 0.0) { + m_logA = -1.0E300; + } else { + m_logA = log(m_A); + } + } + + /// Constructor. + /// @param A pre-exponential. The unit system is + /// (kmol, m, s). The actual units depend on the reaction + /// order and the dimensionality (surface or bulk). + /// @param b Temperature exponent. Non-dimensional. + /// @param E Activation energy in temperature units. Kelvin. + Arrhenius(doublereal A, doublereal b, doublereal E) : + m_b (b), + m_E (E), + m_A (A) + { + if (m_A <= 0.0) { + m_logA = -1.0E300; + } else { + m_logA = log(m_A); + } + } + + /// Update concentration-dependent parts of the rate + /// coefficient. For this class, there are no + /// concentration-dependent parts, so this method does + /// nothing. + void update_C(const doublereal* c) {} + + /** + * Update the value of the logarithm of the rate constant. + * + * Note, this function should never be called for negative A values. + * If it does then it will produce a negative overflow result, and + * a zero net forwards reaction rate, instead of a negative reaction + * rate constant that is the expected result. + */ + doublereal update(doublereal logT, doublereal recipT) const { + return m_logA + m_b*logT - m_E*recipT; + } + + /** + * Update the value the rate constant. + * + * This function returns the actual value of the rate constant. + * It can be safely called for negative values of the pre-exponential + * factor. + */ + doublereal updateRC(doublereal logT, doublereal recipT) const { + return m_A * exp(m_b*logT - m_E*recipT); + } + + + void writeUpdateRHS(std::ostream& s) const { + s << " exp(" << m_logA; + if (m_b != 0.0) s << " + " << m_b << " * tlog"; + if (m_E != 0.0) s << " - " << m_E << " * rt"; + s << ");" << std::endl; + } + + doublereal activationEnergy_R() const { + return m_E; + } + + static bool alwaysComputeRate() { return false;} + + protected: + doublereal m_logA, m_b, m_E, m_A; + }; + + + class ArrheniusSum { + + public: + static int type(){ return ARRHENIUS_SUM; } + ArrheniusSum() : m_nterms(0) {} + + void addArrheniusTerm(doublereal A, doublereal b, doublereal E) { + if (A > 0.0) { + m_terms.push_back(Arrhenius(A, b, E)); + m_sign.push_back(1); + } + else if (A < 0.0) { + m_terms.push_back(Arrhenius(-A, b, E)); + m_sign.push_back(-1); + } + m_nterms++; + } + + void update_C(const doublereal* c) {} + + /** + * Update the value of the logarithm of the rate constant. + * + */ + doublereal update(doublereal logT, doublereal recipT) const { + int n; + doublereal f, fsum = 0.0; + for (n = 0; n < m_nterms; n++) { + f = m_terms[n].updateRC(logT, recipT); + fsum += m_sign[n]*f; + } + return log(fsum); + } + + /** + * Update the value the rate constant. + * + * This function returns the actual value of the rate constant. + * It can be safely called for negative values of the pre-exponential + * factor. + */ + doublereal updateRC(doublereal logT, doublereal recipT) const { + int n; + doublereal f, fsum = 0.0; + for (n = 0; n < m_nterms; n++) { + f = m_terms[n].updateRC(logT, recipT); + fsum += m_sign[n]*f; + } + return fsum; + } + + void writeUpdateRHS(std::ostream& s) const { + ; + } + + static bool alwaysComputeRate() { return false;} + + protected: + std::vector m_terms; + vector_int m_sign; + int m_nterms; + }; + + + /** + * An Arrhenius rate with coverage-dependent terms. + */ + class SurfaceArrhenius { + + public: + static int type(){ return ARRHENIUS; } + SurfaceArrhenius() : + m_logA(-1.0E300), + m_b (0.0), + m_E (0.0), + m_A(0.0), + m_acov(0.0), + m_ecov(0.0), + m_mcov(0.0), + m_ncov(0), + m_nmcov(0) + { + } + + SurfaceArrhenius( int csize, const doublereal* c ) : + m_b (c[1]), + m_E (c[2]), + m_A (c[0]), + m_acov(0.0), + m_ecov(0.0), + m_mcov(0.0), + m_ncov(0), + m_nmcov(0) + { + if (m_A <= 0.0) { + m_logA = -1.0E300; + } else { + m_logA = log(c[0]); + } + if (csize >= 7) { + for (int n = 3; n < csize-3; n += 4) { + addCoverageDependence(int(c[n]), + c[n+1], c[n+2], c[n+3]); + } + } + } + + void addCoverageDependence(int k, doublereal a, + doublereal m, doublereal e) { + m_ncov++; + m_sp.push_back(k); + m_ac.push_back(a); + m_ec.push_back(e); + if (m != 0.0) { + m_msp.push_back(k); + m_mc.push_back(m); + m_nmcov++; + } + } + + void update_C(const doublereal* theta) { + m_acov = 0.0; + m_ecov = 0.0; + m_mcov = 0.0; + int n, k; + doublereal th; + for (n = 0; n < m_ncov; n++) { + k = m_sp[n]; + m_acov += m_ac[n] * theta[k]; + m_ecov += m_ec[n] * theta[k]; + } + for (n = 0; n < m_nmcov; n++) { + k = m_msp[n]; + // changed n to k, dgg 1/22/04 + th = fmaxx(theta[k], Tiny); + // th = fmaxx(theta[n], Tiny); + m_mcov += m_mc[n]*log(th); + } + } + + /** + * Update the value of the logarithm of the rate constant. + * + * This calculation is not safe for negative values of + * the preexponential. + */ + doublereal update(doublereal logT, doublereal recipT) const { + return m_logA + m_acov + m_b*logT + - (m_E + m_ecov)*recipT + m_mcov; + } + + /** + * Update the value the rate constant. + * + * This function returns the actual value of the rate constant. + * It can be safely called for negative values of the pre-exponential + * factor. + */ + doublereal updateRC(doublereal logT, doublereal recipT) const { + return m_A * exp(m_acov + m_b*logT - (m_E + m_ecov)*recipT + m_mcov); + } + + doublereal activationEnergy_R() const { + return m_E + m_ecov; + } + + static bool alwaysComputeRate() { return true;} + + protected: + doublereal m_logA, m_b, m_E, m_A; + doublereal m_acov, m_ecov, m_mcov; + vector_int m_sp, m_msp; + vector_fp m_ac, m_ec, m_mc; + int m_ncov, m_nmcov; + }; + + +#ifdef INCL_TST + + class TST { + + public: + static int type(){ return TSTRATE; } + TST() {} + TST( const vector_fp& c ) { + m_b.resize(10); + copy(c.begin(), c.begin() + 10, m_b.begin()); + m_k = int(c[10]); + } + + void update_C(const vector_fp& c) { + doublereal ck = c[m_k]; + delta_s0 = m_b[0] + m_b[1]*ck + m_b[2]*ck*ck; + delta_e0 = m_b[5] + m_b[6]*ck + m_b[7]*ck*ck; + } + + doublereal update(doublereal logT, doublereal recipT) const { + doublereal delta_s = delta_s0*(1.0 + m_b[3]*logT + m_b[4]*recipT); + doublereal delta_E = delta_e0*(1.0 + m_b[8]*logT + m_b[9]*recipT); + return logBoltz_Planck + logT + delta_s - delta_E*recipT; + } + + doublereal updateRC(doublereal logT, doublereal recipT) const { + double lres = update(logT, recipT); + return exp(lres); + } + + void writeUpdateRHS(std::ostream& s) const {} + + protected: + doublereal delta_s0, delta_e0; + int m_k; + vector_fp m_b; + }; + +#endif + +} + + +// class LandauTeller { + +// public: +// static int type(){ return LANDAUTELLER; } +// LandauTeller(){} +// LandauTeller( const vector_fp& c ) : m_c(c) { m_c[0] = log(c[0]); } + +// doublereal update(doublereal logT, doublereal recipT) const { +// return m_c[0] + m_c[1]*tt[1] - m_c[2]*tt[2] +// + m_c[3]*tt[3] + m_c[4]*tt[4]; +// } + +// //void writeUpdateRHS(ostream& s) const { +// // s << exp(m_logA); +// // s << " * exp("; +// // if (m_b != 0.0) s << m_b << " * tlog"; +// // if (m_E != 0.0) s << " - " << m_E << " * rt"; +// // if (m_E != 0.0) s << " - " << m_E << " * rt"; +// // s << ");" << endl; +// // } +// //} + +// protected: +// doublereal m_logA, m_b, m_E; +// }; + +//} + + +#endif + + diff --git a/Cantera/src/kinetics/StoichManager.h b/Cantera/src/kinetics/StoichManager.h new file mode 100755 index 000000000..c1a2dc7d0 --- /dev/null +++ b/Cantera/src/kinetics/StoichManager.h @@ -0,0 +1,824 @@ +/** + * @file StoichManager.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_STOICH_MGR_H +#define CT_STOICH_MGR_H + +#include "stringUtils.h" + +namespace Cantera { + + /** + * @defgroup Stoichiometry Stoichiometry + * + * Note: these classes are designed for internal use in class + * ReactionStoichManager. + * + * The classes defined here implement simple operations that are + * used by class ReactionStoichManager to compute things like + * rates of progress, species production rates, etc. In general, a + * reaction mechanism may involve many species and many reactions, + * but any given reaction typically only involves a few species as + * reactants, and a few as products. Therefore, the matrix of + * stoichiometric coefficients is very sparse. Not only is it + * sparse, but the non-zero matrix elements often have the value + * 1, and in many cases no more than three coefficients are + * non-zero for the reactants and/or the products. + * + + * For the present purposes, we will consider each direction of a + * reversible reaction to be a separate reaction. We often need to + * compute quantities that can formally be written as a matrix + * product of a stoichiometric coefficient matrix and a vector of + * reaction rates. For example, the species creation rates are + * given by + * \f[ + * \dot C_k = \sum_k \nu^{(p)}_{k,i} R_i + * \f] + * where \f$ \nu^{(p)_{k,i}} \f$ is the product-side stoichiometric + * coefficient of species \a k in reaction \a i. + * This could be done be straightforward matrix multiplication, but would be inefficient, since most of the matrix elements of \f$ \nu^{(p)}_{k,i} \f$ are zero. We could do better by using sparse-matrix algorithms to compute this product. + +If the reactions are general ones, with non-integral stoichiometric +coefficients, this is about as good as we can do. But we are +particularly concerned here with the performance for very large +reaction mechanisms, which are usually composed of elementary +reactions, which have integral stoichiometric +coefficients. Furthermore, very few elementary reactions involve more +than 3 product or reactant molecules. This means that instead of + + +But we can do even better if we take account of the special structure +of this matrix for elementary reactions. + +involve three or fewer product molecules (or reactant molecules). + + * To take advantage of this structure, reactions are divided int +These classes are + * designed to take advantage of this sparse structure when + * computing quantities that can be written as matrix multiplies + +They are designed to explicitly unroll loops over species or reactions for + + * Operations on reactions that require knowing the reaction + * stoichiometry. + * This module consists of class StoichManager, and + * classes C1, C2, and C3. Classes C1, C2, and C3 handle operations + * involving one, two, or three species, respectively, in a + * reaction. Instances are instantiated with a reaction number, and n + * species numbers (n = 1 for C1, etc.). All three classes have the + * same interface. + * + * These classes are designed for use by StoichManager, and the + * operations implemented are those needed to efficiently compute + * quantities such as rates of progress, species production rates, + * reaction thermochemistry, etc. The compiler will inline these + * methods into the body of the corresponding StoichManager method, + * and so there is no performance penalty (unless inlining is turned + * off). + * + * To describe the methods, consider class C3 and suppose an instance + * is created with reaction number irxn and species numbers k0, k1, + * and k2. + * + * - multiply(in, out) : out[irxn] is multiplied by + * in[k0] * in[k1] * in[k2] + * + * - power(in, out) : out[irxn] is multiplied by + * (in[k0]^order0) * (in[k1]^order1) * (in[k2]^order2) + * + * - incrementReaction(in, out) : out[irxn] is incremented by + * in[k0] + in[k1] + in[k2] + * + * - decrementReaction(in, out) : out[irxn] is decremented by + * in[k0] + in[k1] + in[k2] + * + * - incrementSpecies(in, out) : out[k0], out[k1], and out[k2] + * are all incremented by in[irxn] + * + * - decrementSpecies(in, out) : out[k0], out[k1], and out[k2] + * are all decremented by in[irxn] + * + * The function multiply() is usually used when evaluating the + * forward and reverse rates of progress of reactions. + * The rate constants are usually loaded into out[]. Then + * multply() is called to add in the dependence of the + * species concentrations to yield a forward and reverse rop. + * + * The function incrementSpecies() and its cousin decrementSpecies() + * is used to translate from rates of progress to species production + * rates. The vector in[] is preloaed with the rates of progess of + * all reactions. Then incrementSpecies() is called to + * increment the species production vector, out[], with the rates + * of progress. + * + * The functions incrementReaction() and decrementReaction() are + * used to find the standard state equilibrium constant for + * a reaction. Here, output[] is a vector of length + * number of reactions, usually the standard gibbs free energies + * of reaction, while input, usually the standard state + * gibbs free energies of species, is a vector of length number of + * species. + * + * Note the stoichiometric coefficient for a species in a reaction + * is handled by always assuming it is equal to one and then + * treating reactants and products for a reaction separately. + * Bimolecular reactions involving the identical species are + * treated as involving separate species. + * + * @internal This class should be upgraded to include cases where + * real stoichiometric coefficients are used. Shouldn't be that + * hard to do, and they occur in engineering simulations with some + * regularity. + * + */ + + static doublereal ppow(doublereal x, doublereal order) { + if (x > 0.0) + return std::pow(x, order); + else + return 0.0; + } + + inline static std::string fmt(std::string r, int n) { return r + "[" + int2str(n) + "]"; } + + + /** + * Handles one species in a reaction. + * @ingroup Stoichiometry + * @internal + */ + class C1 { + + public: + + C1( int rxn = 0, int ic0 = 0) + : m_rxn (rxn), m_ic0 (ic0) {} + + int data(std::vector& ic) { + ic.resize(1); + ic[0] = m_ic0; + return m_rxn; + } + + void incrementSpecies(const doublereal* R, doublereal* S) const { + S[m_ic0] += R[m_rxn]; + } + + void decrementSpecies(const doublereal* R, doublereal* S) const { + S[m_ic0] -= R[m_rxn]; + } + + void multiply(const doublereal* S, doublereal* R) const { + R[m_rxn] *= S[m_ic0]; + } + + void incrementReaction(const doublereal* S, doublereal* R) const { + R[m_rxn] += S[m_ic0]; + } + + void decrementReaction(const doublereal* S, doublereal* R) const { + R[m_rxn] -= S[m_ic0]; + } + + int rxnNumber() const { return m_rxn; } + int speciesIndex(int n) const { return m_ic0; } + int nSpecies() { return 1;} + + void writeMultiply(std::string r, std::map& out) { + out[m_rxn] = fmt(r, m_ic0); + } + + void writeIncrementReaction(std::string r, std::map& out) { + out[m_rxn] += " + "+fmt(r, m_ic0); + } + void writeDecrementReaction(std::string r, std::map& out) { + out[m_rxn] += " - "+fmt(r, m_ic0); + } + + void writeIncrementSpecies(std::string r, std::map& out) { + out[m_ic0] += " + "+fmt(r, m_rxn); + } + void writeDecrementSpecies(std::string r, std::map& out) { + out[m_ic0] += " - "+fmt(r, m_rxn); + } + + private: + int m_rxn, m_ic0; + }; + + + + /** + * Handles two species in a single reaction. + * @ingroup Stoichiometry + */ + class C2 { + public: + C2( int rxn = 0, int ic0 = 0, int ic1 = 0) + : m_rxn (rxn), m_ic0 (ic0), m_ic1 (ic1) {} + + int data(std::vector& ic) { + ic.resize(2); + ic[0] = m_ic0; + ic[1] = m_ic1; + return m_rxn; + } + + void incrementSpecies(const doublereal* R, doublereal* S) const { + S[m_ic0] += R[m_rxn]; + S[m_ic1] += R[m_rxn]; + } + + void decrementSpecies(const doublereal* R, doublereal* S) const { + S[m_ic0] -= R[m_rxn]; + S[m_ic1] -= R[m_rxn]; + } + + void multiply(const doublereal* S, doublereal* R) const { + R[m_rxn] *= S[m_ic0] * S[m_ic1]; + } + + void incrementReaction(const doublereal* S, doublereal* R) const { + R[m_rxn] += S[m_ic0] + S[m_ic1]; + } + + void decrementReaction(const doublereal* S, doublereal* R) const { + R[m_rxn] -= (S[m_ic0] + S[m_ic1]); + } + + int rxnNumber() const { return m_rxn; } + int speciesIndex(int n) const { return (n == 0 ? m_ic0 : m_ic1); } + int nSpecies() { return 2;} + + void writeMultiply(std::string r, std::map& out) { + out[m_rxn] = fmt(r, m_ic0) + " * " + fmt(r, m_ic1); + } + void writeIncrementReaction(std::string r, std::map& out) { + out[m_rxn] += " + "+fmt(r, m_ic0)+" + "+fmt(r, m_ic1); + } + void writeDecrementReaction(std::string r, std::map& out) { + out[m_rxn] += " - "+fmt(r, m_ic0)+" - "+fmt(r, m_ic1); + } + + void writeIncrementSpecies(std::string r, std::map& out) { + std::string s = " + "+fmt(r, m_rxn); + out[m_ic0] += s; + out[m_ic1] += s; + } + void writeDecrementSpecies(std::string r, std::map& out) { + std::string s = " - "+fmt(r, m_rxn); + out[m_ic0] += s; + out[m_ic1] += s; + } + + private: + + /** + * Reaction index -> index into the ROP vector + */ + int m_rxn; + + /** + * Species indecise -> index into the species vector for the + * two species. + */ + int m_ic0, m_ic1; + }; + + + /** + * Handles three species in a reaction. + * @ingroup Stoichiometry + */ + class C3 { + public: + C3( int rxn = 0, int ic0 = 0, int ic1 = 0, int ic2 = 0) + : m_rxn (rxn), m_ic0 (ic0), m_ic1 (ic1), m_ic2 (ic2) {} + + int data(std::vector& ic) { + ic.resize(3); + ic[0] = m_ic0; + ic[1] = m_ic1; + ic[2] = m_ic2; + return m_rxn; + } + + void incrementSpecies(const doublereal* R, doublereal* S) const { + S[m_ic0] += R[m_rxn]; + S[m_ic1] += R[m_rxn]; + S[m_ic2] += R[m_rxn]; + } + + void decrementSpecies(const doublereal* R, doublereal* S) const { + S[m_ic0] -= R[m_rxn]; + S[m_ic1] -= R[m_rxn]; + S[m_ic2] -= R[m_rxn]; + } + + void multiply(const doublereal* S, doublereal* R) const { + R[m_rxn] *= S[m_ic0] * S[m_ic1] * S[m_ic2]; + } + + void incrementReaction(const doublereal* S, doublereal* R) const { + R[m_rxn] += S[m_ic0] + S[m_ic1] + S[m_ic2]; + } + + void decrementReaction(const doublereal* S, doublereal* R) const { + R[m_rxn] -= (S[m_ic0] + S[m_ic1] + S[m_ic2]); + } + + int rxnNumber() const { return m_rxn; } + int speciesIndex(int n) const { return (n == 0 ? m_ic0 : (n == 1 ? m_ic1 : m_ic2)); } + int nSpecies() { return 3;} + + void writeMultiply(std::string r, std::map& out) { + out[m_rxn] = fmt(r, m_ic0) + " * " + fmt(r, m_ic1) + " * " + fmt(r, m_ic2); + } + void writeIncrementReaction(std::string r, std::map& out) { + out[m_rxn] += " + "+fmt(r, m_ic0)+" + "+fmt(r, m_ic1)+" + "+fmt(r, m_ic2); + } + void writeDecrementReaction(std::string r, std::map& out) { + out[m_rxn] += " - "+fmt(r, m_ic0)+" - "+fmt(r, m_ic1)+" - "+fmt(r, m_ic2); + } + void writeIncrementSpecies(std::string r, std::map& out) { + std::string s = " + "+fmt(r, m_rxn); + out[m_ic0] += s; + out[m_ic1] += s; + out[m_ic2] += s; + } + void writeDecrementSpecies(std::string r, std::map& out) { + std::string s = " - "+fmt(r, m_rxn); + out[m_ic0] += s; + out[m_ic1] += s; + out[m_ic2] += s; + } + private: + int m_rxn, m_ic0, m_ic1, m_ic2; + }; + + + /** + * Handles any number of species in a reaction, including fractional + * stoichiometric coefficients, and arbitrary reaction orders. + * @ingroup Stoichiometry + */ + class C_AnyN { + public: + C_AnyN() : m_rxn (-1) {} + + C_AnyN( int rxn, const vector_int& ic, const vector_fp& order, + const vector_fp& stoich) + : m_rxn (rxn) { + m_n = ic.size(); + m_ic.resize(m_n); + m_order.resize(m_n); + m_stoich.resize(m_n); + for (int n = 0; n < m_n; n++) { + m_ic[n] = ic[n]; + m_order[n] = order[n]; + m_stoich[n] = stoich[n]; + } + } + + int data(std::vector& ic) { + ic.resize(m_n); + int n; + for (n = 0; n < m_n; n++) ic[n] = m_ic[n]; + return m_rxn; + } + + doublereal order(int n) const {return m_order[n];} + doublereal stoich(int n) const {return m_stoich[n];} + int speciesIndex(int n) const {return m_ic[n];} + + void multiply(const doublereal* input, doublereal* output) const { + for (int n = 0; n < m_n; n++) { + output[m_rxn] *= + ppow(input[m_ic[n]],m_order[n]); + } + } + + void incrementSpecies(const doublereal* input, + doublereal* output) const { + doublereal x = input[m_rxn]; + for (int n = 0; n < m_n; n++) output[m_ic[n]] += m_stoich[n]*x; + } + + void decrementSpecies(const doublereal* input, + doublereal* output) const { + doublereal x = input[m_rxn]; + for (int n = 0; n < m_n; n++) output[m_ic[n]] -= m_stoich[n]*x; + } + + void incrementReaction(const doublereal* input, + doublereal* output) const { + for (int n = 0; n < m_n; n++) output[m_rxn] + += m_stoich[n]*input[m_ic[n]]; + } + + void decrementReaction(const doublereal* input, + doublereal* output) const { + for (int n = 0; n < m_n; n++) output[m_rxn] + -= m_stoich[n]*input[m_ic[n]]; + } + + void writeMultiply(std::string r, std::map& out) { + int n; + out[m_rxn] = ""; + for (n = 0; n < m_n; n++) { + if (m_order[n] == 1.0) + out[m_rxn] += fmt(r, m_ic[n]); + else + out[m_rxn] += "pow("+fmt(r, m_ic[n])+","+fp2str(m_order[n])+")"; + if (n < m_n-1) + out[m_rxn] += " * "; + } + } + void writeIncrementReaction(std::string r, std::map& out) { + int n; + for (n = 0; n < m_n; n++) { + out[m_rxn] += " + "+fp2str(m_stoich[n]) + "*" + fmt(r, m_ic[n]); + } + } + void writeDecrementReaction(std::string r, std::map& out) { + int n; + for (n = 0; n < m_n; n++) { + out[m_rxn] += " - "+fp2str(m_stoich[n]) + "*" + fmt(r, m_ic[n]); + } + } + void writeIncrementSpecies(std::string r, std::map& out) { + std::string s = fmt(r, m_rxn); + int n; + for (n = 0; n < m_n; n++) { + out[m_ic[n]] += " + "+fp2str(m_stoich[n]) + "*" + s; + } + } + + void writeDecrementSpecies(std::string r, std::map& out) { + std::string s = fmt(r, m_rxn); + int n; + for (n = 0; n < m_n; n++) { + out[m_ic[n]] += " - "+fp2str(m_stoich[n]) + "*" + s; + } + } + + private: + int m_n, m_rxn; + vector_int m_ic; + vector_fp m_order; + vector_fp m_stoich; + }; + + + template + inline static void _multiply(InputIter begin, InputIter end, + const Vec1& input, Vec2& output) { + for (; begin != end; ++begin) + begin->multiply(input, output); + } + + template + inline static void _incrementSpecies(InputIter begin, + InputIter end, const Vec1& input, Vec2& output) { + for (; begin != end; ++begin) + begin->incrementSpecies(input, output); + } + + template + inline static void _decrementSpecies(InputIter begin, + InputIter end, const Vec1& input, Vec2& output) { + for (; begin != end; ++begin) + begin->decrementSpecies(input, output); + } + + template + inline static void _incrementReactions(InputIter begin, + InputIter end, const Vec1& input, Vec2& output) { + for (; begin != end; ++begin) + begin->incrementReaction(input, output); + } + + template + inline static void _decrementReactions(InputIter begin, + InputIter end, const Vec1& input, Vec2& output) { + for (; begin != end; ++begin) + begin->decrementReaction(input, output); + } + + + template + inline static void _writeIncrementSpecies(InputIter begin, InputIter end, std::string r, + std::map& out) { + for (; begin != end; ++begin) begin->writeIncrementSpecies(r, out); + } + + template + inline static void _writeDecrementSpecies(InputIter begin, InputIter end, std::string r, + std::map& out) { + for (; begin != end; ++begin) begin->writeDecrementSpecies(r, out); + } + + template + inline static void _writeIncrementReaction(InputIter begin, InputIter end, std::string r, + std::map& out) { + for (; begin != end; ++begin) begin->writeIncrementReaction(r, out); + } + + template + inline static void _writeDecrementReaction(InputIter begin, InputIter end, std::string r, + std::map& out) { + for (; begin != end; ++begin) begin->writeDecrementReaction(r, out); + } + + template + inline static void _writeMultiply(InputIter begin, InputIter end, std::string r, + std::map& out) { + for (; begin != end; ++begin) begin->writeMultiply(r, out); + } + + /* + * This class handles operations involving the stoichiometric + * coefficients on one side of a reaction (reactant or product) for + * a set of reactions comprising a reaction mechanism. This class is + * used by class ReactionStoichMgr, which contains three instances + * of this class (one to handle operations on the reactions, one for + * the products of reversible reactions, and one for the products of + * irreversible reactions). + * + * This class is designed for use with elementary reactions, or at + * least ones with integral stoichiometric coefficients. Let \f$ M(i) \f$ + * be the number of molecules on the product or reactant side of + * reaction number i. + * \f[ + * r_i = \sum_m^{M_i} s_{k_{m,i}} + * \f] + * To understand the operations performed by this class, let + * \f$ N_{k,i}\f$ denote the stoichiometric coefficient of species k on + * one side (reactant or product) in reaction i. Then \b N is a sparse + * K by I matrix of stoichiometric coefficients. + * + * The following matrix operations may be carried out with a vector + * S of length K, and a vector R of length I: + * + * - \f$ S = S + N R\f$ (incrementSpecies) + * - \f$ S = S - N R\f$ (decrementSpecies) + * - \f$ R = R + N^T S \f$ (incrementReaction) + * - \f$ R = R - N^T S \f$ (deccrementReaction) + * + * The actual implementation, however, does not compute these + * quantities by matrix multiplication. A faster algorithm is used + * that makes use of the fact that the \b integer-valued N matrix is + * very sparse, and the non-zero terms are small positive integers. + * \f[ + * S_k = R_{i1} + \dots + R_{iM} + * \f] + * where M is the number of molecules, and $\f i(m) \f$ is the + * @ingroup Stoichiometry + */ + class StoichManagerN { + public: + + /** + * Constructor for the StoichManagerN class. + * + * @internal Consider adding defaulted entries here that supply + * the total number of reactions in the mechanism and the total + * number of species in the species list. Then, we could use those + * numbers to provide error checks during the construction of the + * object. Those numbers would also provide some clarity to the + * purpose and utility of this class. + * + * DGG - the problem is that the number of reactions and species + * are not known initially. + */ + StoichManagerN() {} + + /** + * Add a single reaction to the list of reactions that this + * stoichiometric manager object handles. + * + * This function is the same as the add() function below. However, + * the order of each species in the power list expression is + * set to one automatically. + */ + void add(int rxn, const vector_int& k) { + vector_fp order(k.size(), 1.0); + vector_fp stoich(k.size(), 1.0); + add(rxn, k, order, stoich); + } + + void add(int rxn, const vector_int& k, const vector_fp& order) { + vector_fp stoich(k.size(), 1.0); + add(rxn, k, order, stoich); + } + + /** + * Add a single reaction to the list of reactions that this + * stoichiometric manager object handles. + * + * @param rxn Reaction index of the current reaction. This is used + * as an index into vectors which have length n_total_rxn. + * @param k This is a vector of integer values specifying the + * species indecises. The length of this vector species + * the number of different species in the description. + * The value of the entries are the species indices. + * These are used as indexes into vectors which have + * length n_total_species. + * @param order This is a vector of the same length as vector k. + * The order is used for the routine power(), which produces + * a power law expression involving the species vector. + * @param stoich This is used to handle fractional stoichiometric coefficients + * on the product side of irreversible reactions. + */ + void add(int rxn, const vector_int& k, const vector_fp& order, + const vector_fp& stoich) { + m_n[rxn] = static_cast(k.size()); + int ns = stoich.size(); + int n; + bool frac = false; + for (n = 0; n < ns; n++) { + if (stoich[n] != 1.0) frac = true; + } + if (frac) { + m_loc[rxn] = static_cast(m_cn_list.size()); + m_cn_list.push_back(C_AnyN(rxn, k, order, stoich)); + } + else { + switch (k.size()) { + case 1: + m_loc[rxn] = static_cast(m_c1_list.size()); + m_c1_list.push_back(C1(rxn, k[0])); + break; + case 2: + m_loc[rxn] = static_cast(m_c2_list.size()); + m_c2_list.push_back(C2(rxn, k[0], k[1])); + break; + case 3: + m_loc[rxn] = static_cast(m_c3_list.size()); + m_c3_list.push_back(C3(rxn, k[0], k[1], k[2])); + break; + default: + m_loc[rxn] = static_cast(m_cn_list.size()); + m_cn_list.push_back(C_AnyN(rxn, k, order, stoich)); + } + } + } + + void multiply(const doublereal* input, doublereal* output) const { + _multiply(m_c1_list.begin(), m_c1_list.end(), input, output); + _multiply(m_c2_list.begin(), m_c2_list.end(), input, output); + _multiply(m_c3_list.begin(), m_c3_list.end(), input, output); + _multiply(m_cn_list.begin(), m_cn_list.end(), input, output); + } + + void incrementSpecies(const doublereal* input, doublereal* output) const { + _incrementSpecies(m_c1_list.begin(), m_c1_list.end(), input, output); + _incrementSpecies(m_c2_list.begin(), m_c2_list.end(), input, output); + _incrementSpecies(m_c3_list.begin(), m_c3_list.end(), input, output); + _incrementSpecies(m_cn_list.begin(), m_cn_list.end(), input, output); + } + + void decrementSpecies(const doublereal* input, doublereal* output) const { + _decrementSpecies(m_c1_list.begin(), m_c1_list.end(), input, output); + _decrementSpecies(m_c2_list.begin(), m_c2_list.end(), input, output); + _decrementSpecies(m_c3_list.begin(), m_c3_list.end(), input, output); + _decrementSpecies(m_cn_list.begin(), m_cn_list.end(), input, output); + } + + void incrementReactions(const doublereal* input, doublereal* output) const { + _incrementReactions(m_c1_list.begin(), m_c1_list.end(), input, output); + _incrementReactions(m_c2_list.begin(), m_c2_list.end(), input, output); + _incrementReactions(m_c3_list.begin(), m_c3_list.end(), input, output); + _incrementReactions(m_cn_list.begin(), m_cn_list.end(), input, output); + } + + void decrementReactions(const doublereal* input, doublereal* output) const { + _decrementReactions(m_c1_list.begin(), m_c1_list.end(), input, output); + _decrementReactions(m_c2_list.begin(), m_c2_list.end(), input, output); + _decrementReactions(m_c3_list.begin(), m_c3_list.end(), input, output); + _decrementReactions(m_cn_list.begin(), m_cn_list.end(), input, output); + } + + void writeIncrementSpecies(std::string r, std::map& out) { + _writeIncrementSpecies(m_c1_list.begin(), m_c1_list.end(), r, out); + _writeIncrementSpecies(m_c2_list.begin(), m_c2_list.end(), r, out); + _writeIncrementSpecies(m_c3_list.begin(), m_c3_list.end(), r, out); + _writeIncrementSpecies(m_cn_list.begin(), m_cn_list.end(), r, out); + } + + void writeDecrementSpecies(std::string r, std::map& out) { + _writeDecrementSpecies(m_c1_list.begin(), m_c1_list.end(), r, out); + _writeDecrementSpecies(m_c2_list.begin(), m_c2_list.end(), r, out); + _writeDecrementSpecies(m_c3_list.begin(), m_c3_list.end(), r, out); + _writeDecrementSpecies(m_cn_list.begin(), m_cn_list.end(), r, out); + } + + void writeIncrementReaction(std::string r, std::map& out) { + _writeIncrementReaction(m_c1_list.begin(), m_c1_list.end(), r, out); + _writeIncrementReaction(m_c2_list.begin(), m_c2_list.end(), r, out); + _writeIncrementReaction(m_c3_list.begin(), m_c3_list.end(), r, out); + _writeIncrementReaction(m_cn_list.begin(), m_cn_list.end(), r, out); + } + + void writeDecrementReaction(std::string r, std::map& out) { + _writeDecrementReaction(m_c1_list.begin(), m_c1_list.end(), r, out); + _writeDecrementReaction(m_c2_list.begin(), m_c2_list.end(), r, out); + _writeDecrementReaction(m_c3_list.begin(), m_c3_list.end(), r, out); + _writeDecrementReaction(m_cn_list.begin(), m_cn_list.end(), r, out); + } + + void writeMultiply(std::string r, std::map& out) { + _writeMultiply(m_c1_list.begin(), m_c1_list.end(), r, out); + _writeMultiply(m_c2_list.begin(), m_c2_list.end(), r, out); + _writeMultiply(m_c3_list.begin(), m_c3_list.end(), r, out); + _writeMultiply(m_cn_list.begin(), m_cn_list.end(), r, out); + } + + + private: + + std::vector m_c1_list; + std::vector m_c2_list; + std::vector m_c3_list; + std::vector m_cn_list; + /** + * Std::Mapping with the Reaction Number as key and the Number of species + * as the value. + */ + std::map m_n; + /** + * Std::Mapping with the Reaction Number as key and the placement in the + * vector of reactions list( i.e., m_c1_list[]) as key + */ + std::map m_loc; + }; + +#undef INCL_STOICH_WRITER +#ifdef INCL_STOICH_WRITER + + class StoichWriter { + public: + + StoichWriter() {} + + void add(int rxn, const vector_int& k) { + int n, nn = k.size(); + for (n = 0; n < nn; n++) { + if (m_mult[rxn] != "") m_mult[rxn] += " * "; + m_mult[rxn] += "c[" + int2str(k[n]) + "]"; + m_is[k[n]] += " + rop[" + int2str(rxn) + "]"; + m_ds[k[n]] += " - rop[" + int2str(rxn) + "]"; + m_ir[rxn] += " + grt[" + int2str(k[n]) + "]"; + m_dr[rxn] += " - grt[" + int2str(k[n]) + "]"; + } + } + + void add(int rxn, const vector_int& k, const vector_fp& order, + const vector_fp& stoich) { + int n, nn = k.size(); + std::string s; + for (n = 0; n < nn; n++) { + if (order[n] == 1.0) + m_mult[rxn] += "*c[" + int2str(k[n]) + "]"; + else + m_mult[rxn] += "*pow(c[" _ int2str(k[n]) + "],"+fp2str(order[n])+")"; + if (stoich[n] == 1.0) { + m_is[k[n]] += " + r[" + int2str(rxn) + "]"; + m_ds[k[n]] += " - r[" + int2str(rxn) + "]"; + m_ir[rxn] += " + g[" + int2str(k[n]) + "]"; + m_dr[rxn] += " - g[" + int2str(k[n]) + "]"; + } + else { + s = fp2str(stoich[n]); + m_is[k[n]] += " + "+s+"*r[" + int2str(rxn) + "]"; + m_ds[k[n]] += " - "+s+"*r[" + int2str(rxn) + "]"; + m_ir[rxn] += " + "+s+"*g[" + int2str(k[n]) + "]"; + m_dr[rxn] += " - "+s+"*g[" + int2str(k[n]) + "]"; + } + } + } + + std::string mult(int rxn) { return m_mult[rxn]; } + std::string incrSpec(int k, std::string) { return m_is[k]; } + std::string decrSpec(int k) { return m_ds[k]; } + std::string incrRxn(int rxn) { return m_ir[rxn]; } + std::string decrRxn(int rxn) { return m_dr[rxn]; } + + private: + std::map m_mult, m_ir, m_dr, m_is, m_ds; + }; +#endif + + +} + +#endif diff --git a/Cantera/src/kinetics/ThirdBodyMgr.h b/Cantera/src/kinetics/ThirdBodyMgr.h new file mode 100755 index 000000000..33ab92c22 --- /dev/null +++ b/Cantera/src/kinetics/ThirdBodyMgr.h @@ -0,0 +1,187 @@ +/** + * @file ThirdBodyMgr.h + * + * $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2001 California Institute of Technology + + +#ifndef CT_THIRDBODY_MGR_H +#define CT_THIRDBODY_MGR_H + +#include + +#include "ct_defs.h" +#include "utilities.h" +#include "Enhanced3BConc.h" + + +namespace Cantera { + + template + class ThirdBodyMgr{ + + public: + + ThirdBodyMgr<_E>() : m_n(0) {} + + void install( int rxnNumber, const std::map& enhanced, + doublereal dflt=1.0) { + m_n++; + m_reaction_index.push_back( rxnNumber ); + m_concm.push_back( _E(static_cast(enhanced.size()), + enhanced, dflt ) ); + } + + void update(const vector_fp& conc, doublereal ctot, workPtr work) { + TYPENAME_KEYWORD std::vector<_E>::const_iterator b = m_concm.begin(); + //doublereal* v = m_values.begin(); + for (; b != m_concm.end(); ++b, ++work) + *work = b->update(conc, ctot); + } + + void multiply(doublereal* output, const_workPtr work) { + scatter_mult(work, work + m_n, + output, m_reaction_index.begin()); + } + + size_t workSize() { return m_concm.size(); } + bool contains(int rxnNumber) { + return (find(m_reaction_index.begin(), + m_reaction_index.end(), rxnNumber) + != m_reaction_index.end()); + } + + protected: + + int m_n; + vector_int m_reaction_index; + std::vector<_E> m_concm; + }; + +} + +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cantera/src/kinetics/importKinetics.cpp b/Cantera/src/kinetics/importKinetics.cpp new file mode 100644 index 000000000..d4d387541 --- /dev/null +++ b/Cantera/src/kinetics/importKinetics.cpp @@ -0,0 +1,1069 @@ +/** + * @file importKinetics.cpp + * Declarations of global routines for the importing + * of kinetics data from XML files (see \ref inputfiles). + * + * This file contains routines which are global routines, i.e., + * not part of any object. These routine take as input, ctml + * pointers to data, and pointers to %Cantera objects. The purpose + * of these routines is to intialize the %Cantera objects with data + * from the ctml tree structures. + */ + +/* $Author$ + * $Revision$ + * $Date$ + */ + +// Copyright 2002 California Institute of Technology + +#ifdef WIN32 +#pragma warning(disable:4786) +#pragma warning(disable:4503) +#endif + +#include "importKinetics.h" +#include "mix_defs.h" +#include + +// Cantera includes +#include "speciesThermoTypes.h" +#include "ThermoPhase.h" +#include "SurfPhase.h" +#include "EdgePhase.h" +#include "ThermoFactory.h" +#include "SpeciesThermoFactory.h" +#include "KineticsFactory.h" +#include "reaction_defs.h" +#include "ReactionData.h" +#include "global.h" +#include "stringUtils.h" + +#include "xml.h" +#include "ctml.h" + +using namespace ctml; +using namespace std; + +//! these are all used to check for duplicate reactions +class rxninfo { +public: + //! rdata + std::vector< std::map > rdata; + //! string name + std::vector eqn; + //! string vector of ints + std::vector dup; + //! string vector of ints + std::vector nr; + //! string vector of ints + std::vector typ; + //! vector of bools. + std::vector rev; +}; + +//! Temporary storage of rxninfo +rxninfo* _rxns = 0; +//! @name utilitydefines. +//@{ +#define _reactiondata _rxns->rdata +#define _eqn _rxns->eqn +#define _dup _rxns->dup +#define _nr _rxns->nr +#define _typ _rxns->typ +#define _rev _rxns->rev +//@} + +namespace Cantera { + + /* + * First we define a couple of typedefs that will + * be used throught this file + */ + //! typedef for a pointer to an XML_Node + typedef const vector nodeset_t; + //! typedef for an XML_Node + typedef XML_Node node_t; + + + + /* + * Check a reaction to see if the elements balance. + */ + void checkRxnElementBalance(Kinetics& kin, + const ReactionData &rdata, doublereal errorTolerance) { + int index, klocal, n, kp, kr, m, nel; + double kstoich; + + map bal, balr, balp; + bal.clear(); + balp.clear(); + balr.clear(); + + int np = rdata.products.size(); + + // iterate over the products + for (index = 0; index < np; index++) { + kp = rdata.products[index]; // index of the product in 'kin' + n = kin.speciesPhaseIndex(kp); // phase this product belongs to + klocal = kp - kin.kineticsSpeciesIndex(0,n); // index within this phase + kstoich = rdata.pstoich[index]; // product stoichiometric coeff + const ThermoPhase& ph = kin.speciesPhase(kp); + nel = ph.nElements(); + for (m = 0; m < nel; m++) { + bal[ph.elementName(m)] += kstoich*ph.nAtoms(klocal,m); + balp[ph.elementName(m)] += kstoich*ph.nAtoms(klocal,m); + } + } + int nr = rdata.reactants.size(); + for (index = 0; index < nr; index++) { + kr = rdata.reactants[index]; + n = kin.speciesPhaseIndex(kr); + //klocal = kr - kin.start(n); + klocal = kr - kin.kineticsSpeciesIndex(0,n); + kstoich = rdata.rstoich[index]; + const ThermoPhase& ph = kin.speciesPhase(kr); + nel = ph.nElements(); + for (m = 0; m < nel; m++) { + bal[ph.elementName(m)] -= kstoich*ph.nAtoms(klocal,m); + balr[ph.elementName(m)] += kstoich*ph.nAtoms(klocal,m); + } + } + + map::iterator b = bal.begin(); + string msg = "\n\tElement Reactants Products"; + bool ok = true; + doublereal err; + for (; b != bal.end(); ++b) { + err = fabs(b->second/(balr[b->first] + balp[b->first])); + if (err > errorTolerance) { + ok = false; + msg += "\n\t"+b->first+" "+ fp2str(balr[b->first]) + +" "+ fp2str(balp[b->first]); + } + } + if (!ok) { + msg = "The following reaction is unbalanced:\n\t" + + rdata.equation + "\n" + msg + "\n"; + throw CanteraError("checkRxnElementBalance",msg); + } + } + + + /** + * Get the reactants or products of a reaction. The information + * is returned in the spnum, stoich, and order vectors. The + * length of the vectors is the number of different types of + * reactants or products found for the reaction. + * + * Input + * -------- + * rxn -> xml node pointing to the reaction element + * in the xml tree. + * kin -> Reference to the kinetics object to install + * the information into. + * rp = 1 -> Go get the reactants for a reaction + * -1 -> Go get the products for a reaction + * default_phase = String name for the default phase + * to loop up species in. + * Output + * ----------- + * spnum = vector of species numbers found. + * Length is number of reactants or products. + * stoich = stoichiometric coefficient of the reactant or product + * Length is number of reactants or products. + * order = Order of the reactant and product in the reaction + * rate expression + * rule = If we fail to find a species, we will throw an error + * if rule != 1. If rule = 1, we simply return false, + * allowing the calling routine to skip this reaction + * and continue. + */ + static bool getReagents(const XML_Node& rxn, kinetics_t& kin, int rp, + string default_phase, + vector_int& spnum, vector_fp& stoich, vector_fp& order, + int rule) { + + string rptype; + + /* + * The id of reactants and products are kept in child elements + * of reaction, named "reactants" and "products". We search + * the xml tree for these children based on the value of rp, + * and store the xml element pointer here. + */ + if (rp == 1) rptype = "reactants"; + else rptype = "products"; + const XML_Node& rg = rxn.child(rptype); + + /* + * The species and stoichiometric coefficient for the species + * are stored as a colon seperated pair. Get all of these + * pairs in the reactions/products object. + */ + vector key, val; + getPairs(rg, key, val); + + int ns = static_cast(key.size()); + + /* + * Loop over each of the pairs and process them + */ + int isp; + doublereal ord, stch; + string ph, sp; + map speciesMap; + for (int n = 0; n < ns; n++) { + sp = key[n]; // sp is the string name for species + ph = ""; + /* + * Search for the species in the kinetics object using the + * member function kineticsSpeciesIndex(). We will search + * for the species in all phases defined in the kinetics operator. + */ + isp = kin.kineticsSpeciesIndex(sp,""); + if (isp < 0) { + if (rule == 1) + return false; + else { + throw CanteraError("getReagents", + "Undeclared reactant or product species "+sp); + return false; + } + } + + /* + * For each reagent, we store the the species number, isp + * the stoichiometric coefficient, val[n], and the order + * species in the reaction rate expression. We assume mass + * action kinetics here, but will modify this below for + * specified species. + */ + spnum.push_back(isp); + stch = atof(val[n].c_str()); + stoich.push_back(stch); + ord = doublereal(stch); + order.push_back(ord); + /* + * Needed to process reaction orders below. + */ + speciesMap[sp] = order.size(); + } + + /* + * Check to see if reactant reaction orders have been specified. + */ + if (rp == 1 && rxn.hasChild("order")) { + vector ord; + rxn.getChildren("order",ord); + int norder = static_cast(ord.size()); + int loc; + doublereal forder; + for (int nn = 0; nn < norder; nn++) { + const XML_Node& oo = *ord[nn]; + string sp = oo["species"]; + loc = speciesMap[sp]; + if (loc == 0) + throw CanteraError("getReagents", + "reaction order specified for non-reactant: " + +sp); + forder = fpValue(oo()); + if (forder < 0.0) { + throw CanteraError("getReagents", + "reaction order must be non-negative"); + } + // replace the forward stoichiometric coefficient + // stored above in 'order' with the specified + // reaction order + order[loc-1] = forder; + } + } + return true; + } + + + /** + * getArrhenius() parses the xml element called Arrhenius. + * The Arrhenius expression is + * \f[ k = A T^(b) exp (-E_a / RT). \f] + */ + static void getArrhenius(const XML_Node& node, int& highlow, + doublereal& A, doublereal& b, doublereal& E) { + + if (node["name"] == "k0") + highlow = 0; + else highlow = 1; + /* + * We parse the children for the A, b, and E conponents. + */ + A = getFloat(node, "A", "-"); + b = getFloat(node, "b"); + E = getFloat(node, "E", "actEnergy"); + E /= GasConstant; + } + + /** + * getStick() processes the XML element called Stick that specifies + * the sticking coefficient reaction. This routine will + * translate the sticking coefficient value into a "normal" + * rate constant for the surface reaction. + * + * Output + * ----------- + * Output is the normal Arrhenius expressions for a surface + * reaction rate constant. + * + * A - units such that rate of rxn has kmol/m^2/s when + * A is multiplied by activity concentrations of + * reactants in the normal manner. + * n - unitless + * E - Units 1/Kelvin + */ + static void getStick(const XML_Node& node, Kinetics& kin, + ReactionData& r, doublereal& A, doublereal& b, doublereal& E) { + int nr = r.reactants.size(); + int k, klocal, not_surf = 0; + int np = 0; + doublereal f = 1.0; + doublereal order; + /* + * species is the name of the special reactant whose surface + * flux rate will be calculated. + * isp = species # in the local phase + * ispKinetics = species # in the kinetics object + * ispPhaseIndex = phase # of the special species + */ + string spname = node["species"]; + ThermoPhase& th = kin.speciesPhase(spname); + int isp = th.speciesIndex(spname); + int ispKinetics = kin.kineticsSpeciesIndex(spname); + int ispPhaseIndex = kin.speciesPhaseIndex(ispKinetics); + + double ispMW = th.molecularWeights()[isp]; + double sc; + + // loop over the reactants + for (int n = 0; n < nr; n++) { + k = r.reactants[n]; + order = r.order[n]; // stoich coeff + + // get the phase species k belongs to + np = kin.speciesPhaseIndex(k); + const ThermoPhase& p = kin.thermo(np); + + // get the local index of species k in this phase + klocal = p.speciesIndex(kin.kineticsSpeciesName(k)); + + // if it is a surface species, divide f by the standard + // concentration for this species, in order to convert + // from concentration units used in the law of mass action + // to coverages used in the sticking probability + // expression + if (p.eosType() == cSurf || p.eosType() == cEdge) { + sc = p.standardConcentration(klocal); + f /= pow(sc, order); + } + // Otherwise: + else { + // We only allow one species to be in the phase + // containing the special sticking coefficient + // species. + if (ispPhaseIndex == np) { + not_surf++; + } + // Other bulk phase species on the other side + // of ther interface are treated like surface + // species. + else { + sc = p.standardConcentration(klocal); + f /= pow(sc, order); + } + } + } + if (not_surf != 1) { + throw CanteraError("getStick", + "reaction probabilities can only be used in " + "reactions with exactly 1 gas/liquid species."); + } + + doublereal cbar = sqrt(8.0*GasConstant/(Pi*ispMW)); + A = 0.25 * getFloat(node, "A", "-") * cbar * f; + b = getFloat(node, "b") + 0.5; + E = getFloat(node, "E", "actEnergy"); + E /= GasConstant; + } + + static void getCoverageDependence(const node_t& node, + thermo_t& surfphase, ReactionData& rdata) { + vector cov; + node.getChildren("coverage", cov); + int k, nc = static_cast(cov.size()); + doublereal e; + string spname; + if (nc > 0) { + for (int n = 0; n < nc; n++) { + const XML_Node& cnode = *cov[n]; + spname = cnode["species"]; + k = surfphase.speciesIndex(spname); + rdata.cov.push_back(doublereal(k)); + rdata.cov.push_back(getFloat(cnode, "a")); + rdata.cov.push_back(getFloat(cnode, "m")); + e = getFloat(cnode, "e", "actEnergy"); + rdata.cov.push_back(e/GasConstant); + } + } + } + + /** + * Get falloff parameters for a reaction. + */ + static void getFalloff(const node_t& f, ReactionData& rdata) { + string type = f["type"]; + vector p; + getStringArray(f,p); + vector_fp c; + int np = static_cast(p.size()); + for (int n = 0; n < np; n++) { + c.push_back(fpValue(p[n])); + } + if (type == "Troe") { + if (np == 4) rdata.falloffType = TROE4_FALLOFF; + else rdata.falloffType = TROE3_FALLOFF; + } + else if (type == "SRI") { + if (np == 5) rdata.falloffType = SRI5_FALLOFF; + else rdata.falloffType = SRI3_FALLOFF; + } + rdata.falloffParameters = c; + } + + /** + * Get the enhanced collision efficiencies. It is assumed that the + * reaction mechanism is homogeneous, so that all species belong + * to phase(0) of 'kin'. + */ + static void getEfficiencies(const node_t& eff, kinetics_t& kin, ReactionData& rdata) { + + // set the default collision efficiency + rdata.default_3b_eff = fpValue(eff["default"]); + + vector key, val; + getPairs(eff, key, val); + int ne = static_cast(key.size()); + string nm; + string phse = kin.thermo(0).id(); + int n, k; + for (n = 0; n < ne; n++) { // ; bb != ee; ++bb) { + nm = key[n];// bb->first; + k = kin.kineticsSpeciesIndex(nm, phse); + rdata.thirdBodyEfficiencies[k] = fpValue(val[n]); // bb->second; + } + } + + /* + * Extract the rate coefficient for a reaction from the xml node, kf. + * kf should point to a XML element named "rateCoeff". + * rdata is the partially filled ReactionData object for the reaction. + * This function will fill in more fields in the ReactionData object. + */ + void getRateCoefficient(const node_t& kf, kinetics_t& kin, + ReactionData& rdata, int negA) { + + int nc = kf.nChildren(); + nodeset_t& kf_children = kf.children(); + vector_fp clow(3,0.0), chigh(3,0.0); + // int nr = nReacMolecules(rdata); + for (int m = 0; m < nc; m++) { + const node_t& c = *kf_children[m]; + string nm = c.name(); + int highlow=0; + + if (nm == "Arrhenius") { + vector_fp coeff(3); + if (c["type"] == "stick") { + getStick(c, kin, rdata, coeff[0], coeff[1], coeff[2]); + chigh = coeff; + } + else { + getArrhenius(c, highlow, coeff[0], coeff[1], coeff[2]); + if (highlow == 1 || rdata.reactionType == THREE_BODY_RXN + || rdata.reactionType == ELEMENTARY_RXN) + chigh = coeff; + else clow = coeff; + } + if (rdata.reactionType == SURFACE_RXN) { + getCoverageDependence(c, + kin.thermo(kin.surfacePhaseIndex()), rdata); + } + + if (coeff[0] <= 0.0 && negA == 0) { + throw CanteraError("getRateCoefficient", + "negative or zero A coefficient for reaction "+int2str(rdata.number)); + } + } + else if (nm == "falloff") { + getFalloff(c, rdata); + } + else if (nm == "efficiencies") { + getEfficiencies(c, kin, rdata); + } + else if (nm == "electrochem") { + rdata.beta = fpValue(c["beta"]); + } + } + /* + * Store the coefficients in the ReactionData object for return + * from this function. + */ + if (rdata.reactionType == CHEMACT_RXN) + rdata.rateCoeffParameters = clow; + else + rdata.rateCoeffParameters = chigh; + + if (rdata.reactionType == FALLOFF_RXN) + rdata.auxRateCoeffParameters = clow; + else if (rdata.reactionType == CHEMACT_RXN) + rdata.auxRateCoeffParameters = chigh; + } + + + + + /* + * This function returns true if two reactions are duplicates of + * one another, and false otherwise. The input arguments are two + * maps from species number to stoichiometric coefficient, one for + * each reaction. The reactions are considered duplicates if their + * stoichiometric coefficients have the same ratio for all + * species. + */ + doublereal isDuplicateReaction(std::map& r1, + std::map& r2) { + + map::const_iterator b = r1.begin(), e = r1.end(); + int k1 = b->first; + doublereal ratio = 0.0; + if (r1[k1] == 0.0 || r2[k1] == 0.0) goto next; + ratio = r2[k1]/r1[k1]; + ++b; + for (; b != e; ++b) { + k1 = b->first; + if (r1[k1] == 0.0 || r2[k1] == 0.0) goto next; + if (fabs(r2[k1]/r1[k1] - ratio) > 1.e-8) + goto next; + } + return ratio; + next: + ratio = 0.0; + b = r1.begin(); + k1 = b->first; + if (r1[k1] == 0.0 || r2[-k1] == 0.0) return 0.0; + ratio = r2[-k1]/r1[k1]; + ++b; + for (; b != e; ++b) { + k1 = b->first; + if (r1[k1] == 0.0 || r2[-k1] == 0.0) return 0.0; + if (fabs(r2[-k1]/r1[k1] - ratio) > 1.e-8) + return 0.0; + } + return ratio; + } + + + /** + * Install an individual reaction into a kinetics manager. The + * data for the reaction is in the xml_node r. In other words, r + * points directly to a ctml element named "reaction". i refers + * to the number id of the reaction in the kinetics object. + * + * @param i Reaction number. + * @param r XML_Node containing reaction data. + * @param k Kinetics manager to which reaction will be added. + * @param default_phase Default phase for locating a species + * @param rule Rule for handling reactions with missing species + * (skip or flag as error) + * @param validate_rxn If true, check that this reaction is not a + * duplicate of one already entered, and check that the reaction + * balances. + * + * @ingroup kineticsmgr + */ + static bool installReaction(int i, const XML_Node& r, Kinetics* k, + string default_phase, int rule, + bool validate_rxn) { + + Kinetics& kin = *k; + /* + * We use the ReactionData object to store initial values read + * in from the xml data. Then, when we have collected everything + * we add the reaction to the kinetics object, k, at the end + * of the routine. (Someday this may be rewritten to skip building + * the ReactionData object). + */ + ReactionData rdata; + rdata.reactionType = ELEMENTARY_RXN; // default + vector_int reac, prod; + string eqn, type; + int nn, eqlen; + vector_fp dummy; + + // check to see if the reaction is specified to be a duplicate + // of another reaction, or to allow a negative pre-exponential. + int dup = 0; + if (r.hasAttrib("duplicate")) dup = 1; + int negA = 0; + if (r.hasAttrib("negative_A")) negA = 1; + + + /* + * This seemingly simple expression goes and finds the child element, + * "equation". Then it treats all of the contents of the "equation" + * as a string, and returns it the variable eqn. We post process + * the string to convert [ and ] characters into < and >, which + * cannot be stored in an XML file. + */ + if (r.hasChild("equation")) + eqn = r("equation"); + else + eqn = ""; + + eqlen = static_cast(eqn.size()); + for (nn = 0; nn < eqlen; nn++) { + if (eqn[nn] == '[') eqn[nn] = '<'; + if (eqn[nn] == ']') eqn[nn] = '>'; + } + + + bool ok; + // get the reactants + ok = getReagents(r, kin, 1, default_phase, rdata.reactants, + rdata.rstoich, rdata.order, rule); + + /* + * Get the products. We store the id of products in rdata.products + */ + ok = ok && getReagents(r, kin, -1, default_phase, rdata.products, + rdata.pstoich, dummy, rule); + + // if there was a problem getting either the reactants or the products, + // then abort. + if (!ok) return false; + + // check whether the reaction is specified to be + // reversible. Default is irreversible. + rdata.reversible = false; + string isrev = r["reversible"]; + if (isrev == "yes" || isrev == "true") + rdata.reversible = true; + + + string typ = r["type"]; + + /* + * If reaction orders are specified, then this reaction + * does not follow mass-action kinetics, and is not + * an elementary reaction. So check that it is not reversible, + * since computing the reverse rate from thermochemistry only + * works for elementary reactions. Set the type to global, + * so that kinetics managers will know to process the reaction + * orders. + */ + if (r.hasChild("order")) { + if (rdata.reversible == true) + throw CanteraError("installReaction", + "reaction orders may only be given for " + "irreversible reactions"); + rdata.global = true; + } + + + /* + * Seaarch the reaction element for the attribute "type". + * If found, then branch on the type, to fill in appropriate + * fields in rdata. + */ + + if (typ == "falloff") { + rdata.reactionType = FALLOFF_RXN; + rdata.falloffType = SIMPLE_FALLOFF; + } + else if (typ == "chemAct") { + rdata.reactionType = CHEMACT_RXN; + rdata.falloffType = SIMPLE_FALLOFF; + } + else if (typ == "threeBody") { + rdata.reactionType = THREE_BODY_RXN; + } + else if (typ == "surface") { + rdata.reactionType = SURFACE_RXN; + } + else if (typ == "edge") { + rdata.reactionType = EDGE_RXN; + } + else if (typ != "") + throw CanteraError("installReaction", + "Unknown reaction type: " + typ); + + /* + * Look for undeclared duplicate reactions. + */ + if (validate_rxn) { + doublereal c = 0.0; + + map rxnstoich; + rxnstoich.clear(); + int nr = rdata.reactants.size(); + for (nn = 0; nn < nr; nn++) { + rxnstoich[-1 - rdata.reactants[nn]] -= rdata.rstoich[nn]; + } + int np = rdata.products.size(); + for (nn = 0; nn < np; nn++) { + rxnstoich[rdata.products[nn]+1] += rdata.pstoich[nn]; + } + int nrxns = static_cast(_reactiondata.size()); + for (nn = 0; nn < nrxns; nn++) { + if ((int(rdata.reactants.size()) == _nr[nn]) + && (rdata.reactionType == _typ[nn])) { + c = isDuplicateReaction(rxnstoich, _reactiondata[nn]); + if (c > 0.0 + || (c < 0.0 && rdata.reversible) + || (c < 0.0 && _rev[nn])) { + if ((!dup || !_dup[nn])) { + string msg = string("Undeclared duplicate reactions detected: \n") + +"Reaction "+int2str(nn+1)+": "+_eqn[nn] + +"\nReaction "+int2str(i+1)+": "+eqn+"\n"; + _reactiondata.clear(); + _eqn.clear(); + _rev.clear(); + _nr.clear(); + _typ.clear(); + _dup.clear(); + throw CanteraError("installReaction",msg); + } + } + } + } + _dup.push_back(dup); + _rev.push_back(rdata.reversible); + _eqn.push_back(eqn); + _nr.push_back(rdata.reactants.size()); + _typ.push_back(rdata.reactionType); + _reactiondata.push_back(rxnstoich); + } + + rdata.equation = eqn; + rdata.number = i; + rdata.rxn_number = i; + + /* + * Read the rate coefficient data from the XML file. Trigger an + * exception for negative A unless specifically authorized. + */ + getRateCoefficient(r.child("rateCoeff"), kin, rdata, negA); + + /* + * Check to see that the elements balance in the reaction. + * Throw an error if they don't + */ + if (validate_rxn) { + checkRxnElementBalance(kin, rdata); + } + + /* + * Ok we have read everything in about the reaction. Add it + * to the kinetics object by calling the Kinetics member function, + * addReaction() + */ + kin.addReaction(rdata); + return true; + } + + /* + * Take information from the XML tree, p, about reactions + * and install them into the kinetics object, kin. + * default_phase is the default phase to assume when + * looking up species. + * + * At this point, p usually refers to the phase xml element. + * One of the children of this element is reactionArray, + * the element which determines where in the xml file to + * look up the reaction rate data pertaining to the phase. + * + * On return, if reaction instantiation goes correctly, return true. + * If there is a problem, return false. + */ + bool installReactionArrays(const XML_Node& p, Kinetics& kin, + std::string default_phase, bool check_for_duplicates) { + + if (_rxns == 0) { + _rxns = new rxninfo; + } + _eqn.clear(); + _dup.clear(); + _nr.clear(); + _typ.clear(); + _reactiondata.clear(); + _rev.clear(); + + vector rarrays; + int itot = 0; + /* + * Search the children of the phase element for the + * xml element named reactionArray. If we can't find it, + * then return signaling having not found any reactions. + * Apparently, we allow multiple reactionArray elements here + * Each one will be processed sequentially, with the + * end result being purely additive. + */ + p.getChildren("reactionArray",rarrays); + int na = static_cast(rarrays.size()); + if (na == 0) return false; + for (int n = 0; n < na; n++) { + /* + * Go get a reference to the current xml element, + * reactionArray. We will process this element now. + */ + const XML_Node& rxns = *rarrays[n]; + /* + * The reactionArray element has an attribute called, + * datasrc. The value of the attribute is the xml + * element comprising the top of the + * tree of reactions for the phase. + * Find this datasrc element starting with the root + * of the current xml node. + */ + const XML_Node* rdata = get_XML_Node(rxns["datasrc"], &rxns.root()); + //const XML_Node* rdata = find_XML(rxns["datasrc"],&rxns.root(), + // "","","reactionData"); + /* + * If the reactionArray element has a child element named + * "skip", and if the attribute of skip called "species" has + * a value of "undeclared", we will set rxnrule = 1. + * rxnrule is passed to the routine that parses each individual + * reaction. I believe what this means is that the parser will + * skip all reactions containing an undefined species without + * throwing an error condition. + */ + int rxnrule = 0; + if (rxns.hasChild("skip")) { + const XML_Node& sk = rxns.child("skip"); + string sskip = sk["species"]; + if (sskip == "undeclared") { + rxnrule = 1; + } + } + int i, nrxns = 0; + /* + * Search for child elements called include. We only include + * a reaction if it's tagged by one of the include fields. + * Or, we include all reactions if there are no include fields. + */ + vector incl; + rxns.getChildren("include",incl); + int ninc = static_cast(incl.size()); + + vector allrxns; + rdata->getChildren("reaction",allrxns); + nrxns = static_cast(allrxns.size()); + // if no 'include' directive, then include all reactions + if (ninc == 0) { + for (i = 0; i < nrxns; i++) { + const XML_Node* r = allrxns[i]; + if (r) { + if (installReaction(itot, *r, &kin, + default_phase, rxnrule, check_for_duplicates)) ++itot; + } + } + } + else { + for (int nii = 0; nii < ninc; nii++) { + const XML_Node& ii = *incl[nii]; + string imin = ii["min"]; + string imax = ii["max"]; + + string::size_type iwild = string::npos; + if (imax == imin) { + iwild = imin.find("*"); + if (iwild != string::npos) { + imin = imin.substr(0,iwild); + imax = imin; + } + } + + for (i = 0; i < nrxns; i++) { + const XML_Node* r = allrxns[i]; + string rxid; + if (r) { + rxid = (*r)["id"]; + if (iwild != string::npos) { + rxid = rxid.substr(0,iwild); + } + /* + * To decide whether the reaction is included or not + * we do a lexical min max and operation. This + * sometimes has surprising results. + */ + if ((rxid >= imin) && (rxid <= imax)) { + if (installReaction(itot, *r, &kin, + default_phase, rxnrule, check_for_duplicates)) ++itot; + } + } + } + } + } + } + + /* + * Finalize the installation of the kinetics, now that we know + * the true number of reactions in the mechanism, itot. + */ + kin.finalize(); + //writer = 0; + _eqn.clear(); + _dup.clear(); + _nr.clear(); + _typ.clear(); + _reactiondata.clear(); + delete _rxns; + _rxns = 0; + return true; + } + + + /* + * Import a reaction mechanism for a phase or an interface. + * + * @param phase This is an xml node containing a description + * of a phase. Within the phase is a XML element + * called reactionArray containing the location + * of the description of the reactions that make + * up the kinetics object. + * Also within the phase is an XML element called + * phaseArray containing a listing of other phases + * that participate in the kinetics mechanism. + * + * @param th This is a list of ThermoPhase pointers containing + * the phases that participate in the kinetics + * reactions. All of the phases must have already + * been initialized and formed within Cantera. + * However, their pointers should not have been + * added to the Kinetics object; this addition + * is carried out here. + * + * @param k This is a pointer to the kinetics manager class + * that will be initialized with a kinetics + * mechanism. + */ + bool importKinetics(const XML_Node& phase, std::vector th, + Kinetics* k) { + + if (k == 0) return false; + + Kinetics& kin = *k; + + // This phase will be the default one + string default_phase = phase["id"]; + + bool check_for_duplicates = false; + if (phase.parent()->hasChild("validate")) { + const XML_Node& d = phase.parent()->child("validate"); + if (d["reactions"] == "yes") check_for_duplicates = true; + } + + // if other phases are involved in the reaction mechanism, + // they must be listed in a 'phaseArray' child + // element. Homogeneous mechanisms do not need to include a + // phaseArray element. + + vector phase_ids; + if (phase.hasChild("phaseArray")) { + const XML_Node& pa = phase.child("phaseArray"); + getStringArray(pa, phase_ids); + } + phase_ids.push_back(default_phase); + + int np = static_cast(phase_ids.size()); + int nt = static_cast(th.size()); + + // for each referenced phase, attempt to find its id among those + // phases specified. + bool phase_ok; + + string phase_id; + string msg = ""; + for (int n = 0; n < np; n++) { + phase_id = phase_ids[n]; + phase_ok = false; + + // loop over the supplied 'ThermoPhase' objects representing + // phases, to find an object with the same id. + for (int m = 0; m < nt; m++) { + if (th[m]->id() == phase_id) { + phase_ok = true; + + // if no phase with this id has been added to + //the kinetics manager yet, then add this one + if (kin.phaseIndex(phase_id) < 0) { + kin.addPhase(*th[m]); + } + } + msg += " "+th[m]->id(); + } + if (!phase_ok) { + throw CanteraError("importKinetics", + "phase "+phase_id+" not found. Supplied phases are:"+msg); + } + } + + // allocates arrays, etc. Must be called after the phases have + // been added to 'kin', so that the number of species in each + // phase is known. + kin.init(); + + // Install the reactions. + return installReactionArrays(phase, kin, default_phase, check_for_duplicates); + } + + /* + * Build a single-phase ThermoPhase object with associated kinetics + * mechanism. + */ + bool buildSolutionFromXML(XML_Node& root, std::string id, std::string nm, + ThermoPhase* th, Kinetics* k) { + XML_Node* x; + try { + + x = get_XML_NameID(nm, string("#")+id, &root); + // x = get_XML_Node(string("#")+id, &root); + + /* + * Fill in the ThermoPhase object by querying the + * const XML_Node tree located at x. + */ + importPhase(*x, th); + /* + * Create a vector of ThermoPhase pointers of length 1 + * having the current th ThermoPhase as the entry. + */ + vector phases(1); + phases[0] = th; + /* + * Fill in the kinetics object k, by querying the + * const XML_Node tree located by x. The source terms and + * eventually the source term vector will be constructed + * from the list of ThermoPhases in the vector, phases. + */ + importKinetics(*x, phases, k); + + return true; + } + catch (CanteraError) { + throw CanteraError("buildSolutionFromXML","error encountered"); + return false; + } + } + + + +} + diff --git a/Cantera/src/kinetics/importKinetics.h b/Cantera/src/kinetics/importKinetics.h new file mode 100644 index 000000000..2bd7ef58a --- /dev/null +++ b/Cantera/src/kinetics/importKinetics.h @@ -0,0 +1,217 @@ +/** + * @file importKinetics.h + * Definitions of global routines for the importing + * of data from XML files (see \ref inputfiles). + * + * This file contains routines which are global routines, i.e., + * not part of any object. These routine take as input, ctml + * pointers to data, and pointers to %Cantera objects. The purpose + * of these routines is to intialize the %Cantera objects with data + * from the ctml tree structures. + */ +/* + * $Author$ + * $Revision$ + * $Date$ + * + */ + +// Copyright 2002 California Institute of Technology + + +#ifndef CT_IMPORTCTML_H +#define CT_IMPORTCTML_H + +#include + +#include "ThermoPhase.h" +#include "Kinetics.h" + +namespace Cantera { + + class Kinetics; + class SpeciesThermoFactory; + class XML_Node; + + + //!This function returns a ratio if two reactions are duplicates of + //!one another, and 0.0 otherwise. + /*! + * The input arguments are two + * maps from species number to stoichiometric coefficient, one for + * each reaction. The reactions are considered duplicates if their + * stoichiometric coefficients have the same ratio for all + * species. + * + * @param r1 map 1 + * @param r2 map 2 + * + * @return + * Returns 0.0 if the reactions are not the same. + * If the reactions are the same, it returns the ratio of the + * stoichiometric coefficients. + * + * @ingroup kineticsmgr + */ + doublereal isDuplicateReaction(std::map& r1, + std::map& r2); + + + //! This function will check a specific reaction to see if the elements balance. + /*! + * @param kin Kinetics object + * @param rdata Object containing the information about one reaction + * @param errorTolerance double containing the error tolerance. + * + * @ingroup kineticsmgr + */ + void checkRxnElementBalance(Kinetics& kin, + const ReactionData &rdata, + doublereal errorTolerance = 1.0e-3); + + //! Read the rate coefficient data from the XML file. + /*! + * Extract the rate coefficient for a reaction from the xml node, kf. + * kf should point to a XML element named "rateCoeff". + * rdata is the partially filled ReactionData object for the reaction. + * This function will fill in more fields in the ReactionData object. + * + * @param kf XML_Node containing information about the rate coefficients. + * @param kin kinetics manager + * @param rdata ReactionData referece + * @param negA Boolean indicating whether negative A's are ok. + * + * Trigger anexception for negative A unless specifically authorized. + * + * @ingroup kineticsmgr + */ + void getRateCoefficient(const XML_Node& kf, kinetics_t& kin, + ReactionData& rdata, int negA); + + + //! Create a new ThermoPhase object and initializes it according to the XML tree database. + + + //! Install information about reactions into the kinetics object, kin. + /*! + * At this point, parent usually refers to the phase xml element. + * One of the children of this element is reactionArray, + * the element which determines where in the xml file to + * look up the reaction rate data. + * + * This is a wrapper routine around the static function installReaction() + * + * @param p parent XML phase element + * @param kin Kinetics object to install reactions into + * @param default_phase The default_phase is the default phase to assume when + * looking up species. + * @param check_for_duplicates Check for reactions with exactly the same + * reactants and products. + * + * @return + * On return, if reaction instantiation goes correctly, return true. + * If there is a problem, return false. + * + * @ingroup kineticsmgr + */ + bool installReactionArrays(const XML_Node& p, Kinetics& kin, + std::string default_phase, + bool check_for_duplicates = false); + + //! Import a reaction mechanism for a phase or an interface. + /*! + * This routine will import a reaction mechanism into a + * kinetics object. The reaction + * mechanism may either be homogeneous or heterogeneous, + * involving multiple ThermoPhase objects. + * The hosting phase should be included as the first argument. + * For example, if phase I is an interface phase between bulk + * phases A and B. Then, the XML_Node for phase I should be + * the first argument. + * The vector of %ThermoPhase objects should be consist of pointers + * to phases I, A, and B. + * + * @param phase This is an xml node containing a description + * of a phase. Within the phase is a XML element + * called reactionArray containing the location + * of the description of the reactions that make + * up the kinetics object. + * Also within the phase is an XML element called + * phaseArray containing a listing of other phases + * that participate in the kinetics mechanism. + * + * @param th This is a list of ThermoPhase pointers containing + * the phases that participate in the kinetics + * reactions. All of the phases must have already + * been initialized and formed within Cantera. + * However, their pointers should not have been + * added to the Kinetics object; this addition + * is carried out here. + * + * @param kin This is a pointer to a bare kinetics manager class + * that will be initialized with the kinetics + * mechanism. + * + * @ingroup kineticsmgr + * + */ + bool importKinetics(const XML_Node& phase, std::vector th, + Kinetics* kin); + + //!Build a single-phase ThermoPhase object with associated kinetics mechanism. + /*! + * In a single call, this routine initializes a ThermoPhase object and a + * homogenous kinetics object for a phase. + * + * @param root pointer to the XML tree which will be searched to find the + * XML phase element. + * + * @param id Name of the phase to be searched for. + * @param nm Name of the XML element. Should be "phase" + * @param th Pointer to a bare ThermoPhase object, which will be initialized + * by this operaton. + * @param k Pointer to a bare Kinetics object, which will be initialized + * by this operation to a homogeneous kinetics manager + * + * @return + * Returns true if all went well. If there are errors, it will return false. + * + * For Example + * + * @code + * ThermoPhase *th = new ThermoPhase(); + * Kinetics *k = new Kinetics(); + * XML_Node *root = get_XML_File("gri30.xml"); + * ok = buildSolutionFromXML(root, "gri30_mix", "phase", th, k) + * @endcode + * + * @ingroup inputfiles + * @see importKinetics() + */ + bool buildSolutionFromXML(XML_Node& root, std::string id, std::string nm, + ThermoPhase* th, Kinetics* k); + + //! Search an XML tree for species data. + /*! + * + * This utility routine will search the XML tree for the species + * named by the string, kname. It will return the XML_Node + * pointer. + * Failures of any kind return the null pointer. + * + * @param kname species Name + * @param phaseSpeciesData Pointer to the phase XML node pertaining to the + * species database for the phase to be found + * + * @return + * Returns a pointer to teh XML node containing the species data. + * + * @ingroup inputfiles + */ + //const XML_Node *speciesXML_Node(std::string kname, + // const XML_Node *phaseSpeciesData); + +} + +#endif + diff --git a/Cantera/src/kinetics/reaction_defs.h b/Cantera/src/kinetics/reaction_defs.h new file mode 100755 index 000000000..eb36dc544 --- /dev/null +++ b/Cantera/src/kinetics/reaction_defs.h @@ -0,0 +1,111 @@ +/** + * @file reaction_defs.h + * This file defines some constants used to specify reaction types. + */ + +/* + * $Author$ + * $Date$ + * $Revision$ + * + */ + +// Copyright 2001 California Institute of Technology + +#ifndef CT_RXN_DEFS_H +#define CT_RXN_DEFS_H + +#include "ct_defs.h" + +namespace Cantera { + + const int NONE = 0; + + /// @name Reaction Types + + //@{ + + /** + * A reaction with a rate coefficient that depends only on + * temperature. Example: O + OH <-> O2 + H + */ + const int ELEMENTARY_RXN = 1; + + /** + * A reaction that requires a third-body collision partner. Example: + * O2 + M <-> O + O + M + */ + const int THREE_BODY_RXN = 2; + + /** + * The general form for an association or dissociation reaction, with a + * pressure-dependent rate. Example: CH3 + H (+M) <-> CH4 (+M) + */ + const int FALLOFF_RXN = 4; + + /** + * A chemical activation reaction. For these reactions, the rate falls + * off as the pressure increases, due to collisional stabilization of + * a reaction intermediate. Example: Si + SiH4 (+M) <-> Si2H2 + H2 + * (+M), which competes with Si + SiH4 (+M) <-> Si2H4 (+M). + * @todo Implement chemical activation reactions. + */ + const int CHEMACT_RXN = 8; + + /** + * A reaction occurring on a surface. + */ + const int SURFACE_RXN = 20; + + /** + * A reaction occurring at a one-dimensional interface between two + * surface phases. + */ + const int EDGE_RXN = 22; + + /** + * A global reaction. These may have non-integral reaction orders, + * and are not allowed to be reversible. + */ + const int GLOBAL_RXN = 30; + + //@} + + /** @name Rate Coefficient Types + * These types define the supported rate coefficient types for + * elementary reactions. Any of these may also be used as the high and + * low-pressure limits of falloff and chemical activation reactions. + * + * Note that not all of these are currently implemented! + * @todo Finish implementing reaction rate types. + */ + //@{ + + const int ARRHENIUS = 1; + const int LANDAUTELLER = 2; + const int TSTRATE = 3; + const int SURF_ARRHENIUS = 4; + const int ARRHENIUS_SUM = 5; + + //@} + + /** @name Falloff Function Types + */ + //@{ + const int SIMPLE_FALLOFF = 100; + const int TROE3_FALLOFF = 110; + const int TROE4_FALLOFF = 111; + const int SRI3_FALLOFF = 112; + const int SRI5_FALLOFF = 113; + const int WF_FALLOFF = 114; + //@} + + // error flags + const int NO_ERROR = 0; + const int UNKNOWN_REACTION_TYPE = -100; + const int UNKNOWN_RATE_COEFF_TYPE = -200; + const int NOT_YET_IMPLEMENTED = -300; + +} + +#endif