bool BulkKinetics::addReaction(shared_ptr<Reaction> r) { bool added = Kinetics::addReaction(r); if (!added) { return false; } double dn = 0.0; for (Composition::const_iterator iter = r->products.begin(); iter != r->products.end(); ++iter) { dn += iter->second; } for (Composition::const_iterator iter = r->reactants.begin(); iter != r->reactants.end(); ++iter) { dn -= iter->second; } m_dn.push_back(dn); if (r->reversible) { m_revindex.push_back(nReactions()-1); } else { m_irrev.push_back(nReactions()-1); } return true; }
void InterpKinetics::update_rates_T() { if (!m_ntemps) { rebuildInterpData(200, 250, 3000); } double Tnow = thermo().temperature(); if (Tnow >= m_tmax) { rebuildInterpData(m_ntemps + 10, m_tmin, Tnow + 100.0); } else if (Tnow <= m_tmin) { rebuildInterpData(m_ntemps + 2, std::max(Tnow - 20.0, 10.0), m_tmax); } size_t n = static_cast<size_t>(floor((Tnow-m_tmin)/(m_tmax-m_tmin)*(m_ntemps-1))); double dT = Tnow - n * (m_tmax - m_tmin) / (m_ntemps - 1) - m_tmin; assert(dT >= 0 && dT <= (m_tmax - m_tmin) / (m_ntemps - 1)); assert(n < m_ntemps); size_t nFall = m_falloff_low_rates.nReactions(); vec_map(&m_rfn[0], nReactions()) = m_rfn_const.col(n) + m_rfn_slope.col(n) * dT; vec_map(&m_rkcn[0], nReactions()) = m_rkcn_const.col(n) + m_rkcn_slope.col(n) * dT; vec_map(&m_rfn_low[0], nFall) = m_rfn_low_const.col(n) + m_rfn_low_slope.col(n) * dT; vec_map(&m_rfn_high[0], nFall) = m_rfn_high_const.col(n) + m_rfn_high_slope.col(n) * dT; vec_map(&falloff_work[0], nFall) = m_falloff_work_const.col(n) + m_falloff_work_slope.col(n) * dT; m_logStandConc = log(thermo().standardConcentration()); m_temp = Tnow; m_ROP_ok = false; }
void BulkKinetics::addReaction(ReactionData& r) { m_dn.push_back(accumulate(r.pstoich.begin(), r.pstoich.end(), 0.0) - accumulate(r.rstoich.begin(), r.rstoich.end(), 0.0)); if (r.reversible) { m_revindex.push_back(nReactions()); } else { m_irrev.push_back(nReactions()); } Kinetics::addReaction(r); }
void AqueousKinetics::updateROP() { _update_rates_T(); _update_rates_C(); if (m_ROP_ok) { return; } // copy rate coefficients into ropf m_ropf = m_rfn; // multiply by perturbation factor multiply_each(m_ropf.begin(), m_ropf.end(), m_perturb.begin()); // copy the forward rates to the reverse rates m_ropr = m_ropf; // for reverse rates computed from thermochemistry, multiply the forward // rates copied into m_ropr by the reciprocals of the equilibrium constants multiply_each(m_ropr.begin(), m_ropr.end(), m_rkcn.begin()); // multiply ropf by concentration products m_reactantStoich.multiply(m_conc.data(), m_ropf.data()); // for reversible reactions, multiply ropr by concentration products m_revProductStoich.multiply(m_conc.data(), m_ropr.data()); for (size_t j = 0; j != nReactions(); ++j) { m_ropnet[j] = m_ropf[j] - m_ropr[j]; } m_ROP_ok = true; }
void Kinetics::getRevReactionDelta(const double* prop, double* deltaProp) { fill(deltaProp, deltaProp + nReactions(), 0.0); // products add m_revProductStoich.incrementReactions(prop, deltaProp); // reactants subtract m_reactantStoich.decrementReactions(prop, deltaProp); }
void GasKinetics::addThreeBodyReaction(ThreeBodyReaction& r) { m_rates.install(nReactions()-1, r.rate); map<size_t, double> efficiencies; for (Composition::const_iterator iter = r.third_body.efficiencies.begin(); iter != r.third_body.efficiencies.end(); ++iter) { size_t k = kineticsSpeciesIndex(iter->first); if (k != npos) { efficiencies[k] = iter->second; } else if (!m_skipUndeclaredThirdBodies) { throw CanteraError("GasKinetics::addThreeBodyReaction", "Found " "third-body efficiency for undefined species '" + iter->first + "' while adding reaction '" + r.equation() + "'"); } } m_3b_concm.install(nReactions()-1, efficiencies, r.third_body.default_efficiency); }
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])); } } }
/** * 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 = reactionPhaseIndex(); if (ks < 0) throw CanteraError("InterfaceKinetics::finalize", "no surface phase is present."); m_surf = (SurfPhase*)&thermo(ks); if (m_surf->nDim() != 2) throw CanteraError("InterfaceKinetics::finalize", "expected interface dimension = 2, but got dimension = " +int2str(m_surf->nDim())); m_finalized = true; }
void BulkKinetics::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) { getEquilibriumConstants(&m_ropnet[0]); for (size_t i = 0; i < nReactions(); i++) { krev[i] /= m_ropnet[i]; } } else { // m_rkcn[] is zero for irreversible reactions for (size_t i = 0; i < nReactions(); i++) { krev[i] *= m_rkcn[i]; } } }
void GasKinetics::updateROP() { update_rates_C(); update_rates_T(); if (m_ROP_ok) { return; } // copy rate coefficients into ropf copy(m_rfn.begin(), m_rfn.end(), m_ropf.begin()); // multiply ropf by enhanced 3b conc for all 3b rxns if (!concm_3b_values.empty()) { m_3b_concm.multiply(&m_ropf[0], &concm_3b_values[0]); } if (m_nfall) { processFalloffReactions(); } // multiply by perturbation factor multiply_each(m_ropf.begin(), m_ropf.end(), m_perturb.begin()); // copy the forward rates to the reverse rates copy(m_ropf.begin(), m_ropf.end(), m_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(m_ropr.begin(), m_ropr.end(), m_rkcn.begin()); // multiply ropf by concentration products m_reactantStoich.multiply(&m_conc[0], &m_ropf[0]); // for reversible reactions, multiply ropr by concentration products m_revProductStoich.multiply(&m_conc[0], &m_ropr[0]); for (size_t j = 0; j != nReactions(); ++j) { m_ropnet[j] = m_ropf[j] - m_ropr[j]; } for (size_t i = 0; i < m_rfn.size(); i++) { AssertFinite(m_rfn[i], "GasKinetics::updateROP", "m_rfn[" + int2str(i) + "] is not finite."); AssertFinite(m_ropf[i], "GasKinetics::updateROP", "m_ropf[" + int2str(i) + "] is not finite."); AssertFinite(m_ropr[i], "GasKinetics::updateROP", "m_ropr[" + int2str(i) + "] is not finite."); } m_ROP_ok = true; }
/** * Compute the forward rate constants. */ void InterfaceKinetics::getFwdRateConstants(doublereal* kfwd) { updateROP(); 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 BulkKinetics::finalize() { m_finalized = true; // Guarantee that these arrays can be converted to double* even in the // special case where there are no reactions defined. if (!nReactions()) { m_perturb.resize(1, 1.0); m_ropf.resize(1, 0.0); m_ropr.resize(1, 0.0); m_ropnet.resize(1, 0.0); m_rkcn.resize(1, 0.0); } }
void GasKinetics::addFalloffReaction(FalloffReaction& r) { // install high and low rate coeff calculators // and extend the high and low rate coeff value vectors m_falloff_high_rates.install(m_nfall, r.high_rate); m_rfn_high.push_back(0.0); m_falloff_low_rates.install(m_nfall, r.low_rate); m_rfn_low.push_back(0.0); // add this reaction number to the list of falloff reactions m_fallindx.push_back(nReactions()-1); m_rfallindx[nReactions()-1] = m_nfall; // install the enhanced third-body concentration calculator map<size_t, double> efficiencies; for (Composition::const_iterator iter = r.third_body.efficiencies.begin(); iter != r.third_body.efficiencies.end(); ++iter) { size_t k = kineticsSpeciesIndex(iter->first); if (k != npos) { efficiencies[k] = iter->second; } else if (!m_skipUndeclaredThirdBodies) { throw CanteraError("GasKinetics::addTFalloffReaction", "Found " "third-body efficiency for undefined species '" + iter->first + "' while adding reaction '" + r.equation() + "'"); } } m_falloff_concm.install(m_nfall, efficiencies, r.third_body.default_efficiency); // install the falloff function calculator for this reaction m_falloffn.install(m_nfall, r.reaction_type, r.falloff); // increment the falloff reaction counter ++m_nfall; }
void AqueousKinetics::getFwdRateConstants(doublereal* kfwd) { _update_rates_T(); _update_rates_C(); // copy rate coefficients into ropf m_ropf = m_rfn; // multiply by perturbation factor multiply_each(m_ropf.begin(), m_ropf.end(), m_perturb.begin()); for (size_t i = 0; i < nReactions(); i++) { kfwd[i] = m_ropf[i]; } }
void GasKinetics::getEquilibriumConstants(doublereal* kc) { update_rates_T(); thermo().getStandardChemPotentials(&m_grt[0]); fill(m_rkcn.begin(), m_rkcn.end(), 0.0); // compute Delta G^0 for all reactions getReactionDelta(&m_grt[0], &m_rkcn[0]); doublereal rrt = 1.0/(GasConstant * thermo().temperature()); for (size_t i = 0; i < nReactions(); i++) { kc[i] = exp(-m_rkcn[i]*rrt + m_dn[i]*m_logStandConc); } // force an update of T-dependent properties, so that m_rkcn will // be updated before it is used next. m_temp = 0.0; }
/** * Update the equilibrium constants in molar units for all * reversible reactions. Irreversible reactions have their * equilibrium constant set to zero. * For reactions involving charged species the equilibrium * constant is adjusted according to the electrostatis potential. */ 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) { /* * Get the vector of electrochemical potentials and store it in m_mu0 */ 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_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 AqueousKinetics::getEquilibriumConstants(doublereal* kc) { _update_rates_T(); thermo().getStandardChemPotentials(m_grt.data()); fill(m_rkcn.begin(), m_rkcn.end(), 0.0); for (size_t k = 0; k < thermo().nSpecies(); k++) { doublereal logStandConc_k = thermo().logStandardConc(k); m_grt[k] -= GasConstant * m_temp * logStandConc_k; } // compute Delta G^0 for all reactions getReactionDelta(m_grt.data(), m_rkcn.data()); doublereal rrt = 1.0 / thermo().RT(); for (size_t i = 0; i < nReactions(); i++) { kc[i] = exp(-m_rkcn[i]*rrt); } // force an update of T-dependent properties, so that m_rkcn will // be updated before it is used next. m_temp = 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() { Kinetics::finalize(); m_rwork.resize(nReactions()); int ks = reactionPhaseIndex(); if (ks < 0) throw CanteraError("InterfaceKinetics::finalize", "no surface phase is present."); m_surf = (SurfPhase*)&thermo(ks); if (m_surf->nDim() != 2) throw CanteraError("InterfaceKinetics::finalize", "expected interface dimension = 2, but got dimension = " +int2str(m_surf->nDim())); m_StandardConc.resize(m_nTotalSpecies, 0.0); m_deltaG0.resize(m_ii, 0.0); m_ProdStanConcReac.resize(m_ii, 0.0); if (m_thermo.size() != m_phaseExists.size()) { throw CanteraError("InterfaceKinetics::finalize", "internal error"); } m_finalized = true; }
void GasKinetics::getFwdRateConstants(doublereal* kfwd) { update_rates_C(); update_rates_T(); // copy rate coefficients into ropf copy(m_rfn.begin(), m_rfn.end(), m_ropf.begin()); // multiply ropf by enhanced 3b conc for all 3b rxns if (!concm_3b_values.empty()) { m_3b_concm.multiply(&m_ropf[0], &concm_3b_values[0]); } if (m_nfall) { processFalloffReactions(); } // multiply by perturbation factor multiply_each(m_ropf.begin(), m_ropf.end(), m_perturb.begin()); for (size_t i = 0; i < nReactions(); i++) { kfwd[i] = m_ropf[i]; } }
void InterpKinetics::rebuildInterpData(size_t nTemps, double Tmin, double Tmax) { m_ntemps = nTemps; m_tmin = Tmin; m_tmax = Tmax; m_rfn_const = dmatrix::Zero(nReactions(), m_ntemps); m_rfn_slope = dmatrix::Zero(nReactions(), m_ntemps); m_rkcn_const = dmatrix::Zero(nReactions(), m_ntemps); m_rkcn_slope = dmatrix::Zero(nReactions(), m_ntemps); size_t nFall = m_falloff_low_rates.nReactions(); m_rfn_low_const = dmatrix::Zero(nFall, m_ntemps); m_rfn_low_slope = dmatrix::Zero(nFall, m_ntemps); m_rfn_high_const = dmatrix::Zero(nFall, m_ntemps); m_rfn_high_slope = dmatrix::Zero(nFall, m_ntemps); m_falloff_work_const = dmatrix::Zero(nFall, m_ntemps); m_falloff_work_slope = dmatrix::Zero(nFall, m_ntemps); double T_save = thermo().temperature(); double rho_save = thermo().density(); for (size_t n = 0; n < m_ntemps; n++) { thermo().setState_TR(Tmin + ((double) n)/(m_ntemps - 1) * (Tmax - Tmin), rho_save); GasKinetics::update_rates_T(); m_rfn_const.col(n) = vec_map(&m_rfn[0], nReactions()); m_rkcn_const.col(n) = vec_map(&m_rkcn[0], nReactions()); m_rfn_low_const.col(n) = vec_map(&m_rfn_low[0], nFall); m_rfn_high_const.col(n) = vec_map(&m_rfn_high[0], nFall); m_falloff_work_const.col(n) = vec_map(&falloff_work[0], nFall); } double dT = (Tmax - Tmin) / (m_ntemps - 1); for (size_t n = 0; n < m_ntemps - 1; n++) { m_rfn_slope.col(n) = (m_rfn_const.col(n+1) - m_rfn_const.col(n)) / dT; m_rkcn_slope.col(n) = (m_rkcn_const.col(n+1) - m_rkcn_const.col(n)) / dT; m_rfn_low_slope.col(n) = (m_rfn_low_const.col(n+1) - m_rfn_low_const.col(n)) / dT; m_rfn_high_slope.col(n) = (m_rfn_high_const.col(n+1) - m_rfn_high_const.col(n)) / dT; m_falloff_work_slope.col(n) = (m_falloff_work_const.col(n+1) - m_falloff_work_const.col(n)) / dT; } thermo().setState_TR(T_save, rho_save); }
void GasKinetics::addPlogReaction(PlogReaction& r) { m_plog_rates.install(nReactions()-1, r.rate); }
void GasKinetics::addChebyshevReaction(ChebyshevReaction& r) { m_cheb_rates.install(nReactions()-1, r.rate); }
void BulkKinetics::addElementaryReaction(ElementaryReaction& r) { m_rates.install(nReactions()-1, r.rate); }
void Kinetics::checkReactionIndex(size_t i) const { if (i >= nReactions()) { throw IndexError("checkReactionIndex", "reactions", i, nReactions()-1); } }
bool Kinetics::addReaction(shared_ptr<Reaction> r) { r->validate(); if (m_kk == 0) { init(); } resizeSpecies(); // 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. if (r->reversible && !r->orders.empty()) { throw CanteraError("Kinetics::addReaction", "Reaction orders may only " "be given for irreversible reactions"); } // Check for undeclared species for (const auto& sp : r->reactants) { if (kineticsSpeciesIndex(sp.first) == npos) { if (m_skipUndeclaredSpecies) { return false; } else { throw CanteraError("Kinetics::addReaction", "Reaction '" + r->equation() + "' contains the undeclared species '" + sp.first + "'"); } } } for (const auto& sp : r->products) { if (kineticsSpeciesIndex(sp.first) == npos) { if (m_skipUndeclaredSpecies) { return false; } else { throw CanteraError("Kinetics::addReaction", "Reaction '" + r->equation() + "' contains the undeclared species '" + sp.first + "'"); } } } checkReactionBalance(*r); size_t irxn = nReactions(); // index of the new reaction // indices of reactant and product species within this Kinetics object std::vector<size_t> rk, pk; // Reactant and product stoichiometric coefficients, such that rstoich[i] is // the coefficient for species rk[i] vector_fp rstoich, pstoich; for (const auto& sp : r->reactants) { size_t k = kineticsSpeciesIndex(sp.first); rk.push_back(k); rstoich.push_back(sp.second); } for (const auto& sp : r->products) { size_t k = kineticsSpeciesIndex(sp.first); pk.push_back(k); pstoich.push_back(sp.second); } // The default order for each reactant is its stoichiometric coefficient, // which can be overridden by entries in the Reaction.orders map. rorder[i] // is the order for species rk[i]. vector_fp rorder = rstoich; for (const auto& sp : r->orders) { size_t k = kineticsSpeciesIndex(sp.first); // Find the index of species k within rk auto rloc = std::find(rk.begin(), rk.end(), k); if (rloc != rk.end()) { rorder[rloc - rk.begin()] = sp.second; } else { // If the reaction order involves a non-reactant species, add an // extra term to the reactants with zero stoichiometry so that the // stoichiometry manager can be used to compute the global forward // reaction rate. rk.push_back(k); rstoich.push_back(0.0); rorder.push_back(sp.second); } } m_reactantStoich.add(irxn, rk, rorder, rstoich); // product orders = product stoichiometric coefficients if (r->reversible) { m_revProductStoich.add(irxn, pk, pstoich, pstoich); } else { m_irrevProductStoich.add(irxn, pk, pstoich, pstoich); } m_reactions.push_back(r); m_rfn.push_back(0.0); m_rkcn.push_back(0.0); m_ropf.push_back(0.0); m_ropr.push_back(0.0); m_ropnet.push_back(0.0); m_perturb.push_back(1.0); return true; }
void BulkKinetics::addElementaryReaction(ReactionData& r) { m_rates.install(nReactions(), r); }
void Kinetics::checkReactionArraySize(size_t ii) const { if (nReactions() > ii) { throw ArraySizeError("checkReactionArraySize", ii, nReactions()); } }