[Kinetics] Implement addReaction from Reaction for InterfaceKinetics

This commit is contained in:
Ray Speth 2014-11-11 00:12:08 +00:00
parent b19d7342fa
commit c4e6790fda
5 changed files with 235 additions and 1 deletions

View file

@ -11,7 +11,7 @@
#include "cantera/thermo/mix_defs.h"
#include "Kinetics.h"
#include "cantera/kinetics/RxnMolChange.h"
#include "Reaction.h"
#include "cantera/base/utilities.h"
#include "RateCoeffMgr.h"
@ -210,6 +210,7 @@ public:
virtual void init();
virtual void addReaction(ReactionData& r);
virtual void addReaction(shared_ptr<Reaction> r);
virtual void finalize();
virtual bool ready() const;
//! @}
@ -389,8 +390,12 @@ public:
int phaseStability(const size_t iphase) const;
virtual void determineFwdOrdersBV(ReactionData& rdata, vector_fp& fwdFullorders);
virtual void determineFwdOrdersBV(ElectrochemicalReaction& r, vector_fp& fwdFullorders);
protected:
void addElementaryReaction(InterfaceReaction& rdata);
void addGlobalReaction(InterfaceReaction& r);
//! Temporary work vector of length m_kk
vector_fp m_grt;

View file

@ -209,6 +209,8 @@ public:
//! Forward value of the apparent Electrochemical transfer coefficient
doublereal beta;
bool exchange_current_density_formulation;
};
}

View file

@ -896,6 +896,122 @@ void InterfaceKinetics::addReaction(ReactionData& r)
}
}
void InterfaceKinetics::addReaction(shared_ptr<Reaction> r_base)
{
InterfaceReaction& r = dynamic_cast<InterfaceReaction&>(*r_base);
// Create a SurfaceArrhenius rate calculator and set the coverage dependencies
SurfaceArrhenius rate(r.rate);
// Turn on the global flag indicating surface coverage dependence
if (!r.coverage_deps.empty()) {
m_has_coverage_dependence = true;
}
for (map<string, CoverageDependency>::const_iterator iter = r.coverage_deps.begin();
iter != r.coverage_deps.end();
++iter) {
size_t k = kineticsSpeciesIndex(iter->first);
rate.addCoverageDependence(k, iter->second.a, iter->second.m, iter->second.E);
}
m_rates.install(m_ii, rate);
// Store activation energy
m_E.push_back(rate.activationEnergy_R());
ElectrochemicalReaction* re = dynamic_cast<ElectrochemicalReaction*>(&r);
if (re) {
m_has_electrochem_rxns = true;
m_beta.push_back(re->beta);
m_ctrxn.push_back(m_ii);
if (re->exchange_current_density_formulation) {
m_has_exchange_current_density_formulation = true;
m_ctrxn_ecdf.push_back(1);
} else {
m_ctrxn_ecdf.push_back(0);
}
m_ctrxn_resistivity_.push_back(re->film_resistivity);
if (r.reaction_type == BUTLERVOLMER_NOACTIVITYCOEFFS_RXN ||
r.reaction_type == BUTLERVOLMER_RXN ||
r.reaction_type == SURFACEAFFINITY_RXN ||
r.reaction_type == GLOBAL_RXN) {
// Specify alternative forms of the electrochemical reaction
if (r.reaction_type == BUTLERVOLMER_RXN) {
m_ctrxn_BVform.push_back(1);
} else if (r.reaction_type == BUTLERVOLMER_NOACTIVITYCOEFFS_RXN) {
m_ctrxn_BVform.push_back(2);
} else {
// set the default to be the normal forward / reverse calculation method
m_ctrxn_BVform.push_back(0);
}
if (!r.orders.empty()) {
vector_fp orders(nTotalSpecies(), 0.0);
for (Composition::const_iterator iter = r.orders.begin();
iter != r.orders.end();
++iter) {
orders[kineticsSpeciesIndex(iter->first)] = iter->second;
}
RxnOrders* ro = new RxnOrders();
ro->fill(orders);
m_ctrxn_ROPOrdersList_.push_back(ro);
m_ctrxn_FwdOrdersList_.push_back(0);
// Fill in the Fwd Orders dependence here for B-V reactions
if (r.reaction_type == BUTLERVOLMER_NOACTIVITYCOEFFS_RXN ||
r.reaction_type == BUTLERVOLMER_RXN) {
vector_fp fwdFullorders(m_kk, 0.0);
determineFwdOrdersBV(*re, fwdFullorders);
RxnOrders* ro = new RxnOrders();
ro->fill(fwdFullorders);
m_ctrxn_FwdOrdersList_[m_ii] = ro;
}
} else {
m_ctrxn_ROPOrdersList_.push_back(0);
m_ctrxn_FwdOrdersList_.push_back(0);
}
} else {
m_ctrxn_BVform.push_back(0);
m_ctrxn_ROPOrdersList_.push_back(0);
m_ctrxn_FwdOrdersList_.push_back(0);
if (re->film_resistivity > 0.0) {
throw CanteraError("InterfaceKinetics::addReaction()",
"film resistivity set for elementary reaction");
}
}
}
if (r.reversible) {
m_revindex.push_back(nReactions());
m_nrev++;
} else {
m_irrev.push_back(nReactions());
m_nirrev++;
}
Kinetics::addReaction(r_base);
m_rxnPhaseIsReactant.push_back(std::vector<bool>(nPhases(), false));
m_rxnPhaseIsProduct.push_back(std::vector<bool>(nPhases(), false));
size_t i = m_ii - 1;
for (Composition::const_iterator iter = r.reactants.begin();
iter != r.reactants.end();
++iter) {
size_t k = kineticsSpeciesIndex(iter->first);
size_t p = speciesPhaseIndex(k);
m_rxnPhaseIsReactant[i][p] = true;
}
for (Composition::const_iterator iter = r.products.begin();
iter != r.products.end();
++iter) {
size_t k = kineticsSpeciesIndex(iter->first);
size_t p = speciesPhaseIndex(k);
m_rxnPhaseIsProduct[i][p] = true;
}
}
void InterfaceKinetics::setIOFlag(int ioFlag)
{
m_ioFlag = ioFlag;
@ -1102,6 +1218,49 @@ void InterfaceKinetics::determineFwdOrdersBV(ReactionData& rdata, std::vector<do
}
}
void InterfaceKinetics::determineFwdOrdersBV(ElectrochemicalReaction& r, std::vector<doublereal>& fwdFullOrders)
{
// Start out with the full ROP orders vector.
// This vector will have the BV exchange current density orders in it.
fwdFullOrders.assign(nTotalSpecies(), 0.0);
for (Composition::const_iterator iter = r.orders.begin();
iter != r.orders.end();
++iter) {
fwdFullOrders[kineticsSpeciesIndex(iter->first)] = iter->second;
}
// forward and reverse beta values
double betaf = r.beta;
double betar = 1.0 - betaf;
// Loop over the reactants doing away with the BV terms.
// This should leave the reactant terms only, even if they are non-mass action.
for (Composition::const_iterator iter = r.reactants.begin();
iter != r.reactants.end();
++iter) {
size_t k = kineticsSpeciesIndex(iter->first);
fwdFullOrders[k] += betaf * iter->second;
// just to make sure roundoff doesn't leave a term that should be zero (haven't checked this out yet)
if (abs(fwdFullOrders[k]) < 0.00001) {
fwdFullOrders[k] = 0.0;
}
}
// Loop over the products doing away with the BV terms.
// This should leave the reactant terms only, even if they are non-mass action.
for (Composition::const_iterator iter = r.products.begin();
iter != r.products.end();
++iter) {
size_t k = kineticsSpeciesIndex(iter->first);
fwdFullOrders[k] -= betaf * iter->second;
// just to make sure roundoff doesn't leave a term that should be zero (haven't checked this out yet)
if (abs(fwdFullOrders[k]) < 0.00001) {
fwdFullOrders[k] = 0.0;
}
}
}
void EdgeKinetics::finalize()
{
// Note we can't call the Interface::finalize() routine because we need to check for a dimension of 1 below.

View file

@ -109,6 +109,7 @@ ElectrochemicalReaction::ElectrochemicalReaction(const Composition& reactants_,
, equilibrium_constant_power(1.0)
, affinity_power(1.0)
, beta(0.0)
, exchange_current_density_formulation(false)
{
}

View file

@ -1,7 +1,9 @@
#include "gtest/gtest.h"
#include "cantera/kinetics/importKinetics.h"
#include "cantera/thermo/IdealGasPhase.h"
#include "cantera/thermo/SurfPhase.h"
#include "cantera/kinetics/GasKinetics.h"
#include "cantera/kinetics/InterfaceKinetics.h"
#include "cantera/base/Array.h"
using namespace Cantera;
@ -158,3 +160,68 @@ TEST_F(KineticsFromScratch, add_chebyshev_reaction)
kin.finalize();
check_rates(4);
}
class InterfaceKineticsFromScratch : public testing::Test
{
public:
InterfaceKineticsFromScratch()
: gas("../data/sofc-test.xml", "gas")
, gas_ref("../data/sofc-test.xml", "gas")
, surf("../data/sofc-test.xml", "metal_surface")
, surf_ref("../data/sofc-test.xml", "metal_surface")
{
std::vector<ThermoPhase*> th;
th.push_back(&surf_ref);
th.push_back(&gas_ref);
importKinetics(surf_ref.xml(), th, &kin_ref);
kin.addPhase(surf);
kin.addPhase(gas);
kin.init();
}
IdealGasPhase gas;
IdealGasPhase gas_ref;
SurfPhase surf;
SurfPhase surf_ref;
InterfaceKinetics kin;
InterfaceKinetics kin_ref;
//! iRef is the index of the corresponding reaction in the reference mech
void check_rates(int iRef) {
ASSERT_EQ((size_t) 1, kin.nReactions());
std::string X = "H2:0.2 O2:0.5 H2O:0.1 N2:0.2";
std::string Xs = "H(m):0.1 O(m):0.2 OH(m):0.3 (m):0.4";
gas.setState_TPX(1200, 5*OneAtm, X);
gas_ref.setState_TPX(1200, 5*OneAtm, X);
surf.setState_TP(1200, 5*OneAtm);
surf_ref.setState_TP(1200, 5*OneAtm);
surf.setCoveragesByName(Xs);
surf_ref.setCoveragesByName(Xs);
vector_fp k(1), k_ref(kin_ref.nReactions());
kin.getFwdRateConstants(&k[0]);
kin_ref.getFwdRateConstants(&k_ref[0]);
EXPECT_FLOAT_EQ(k_ref[iRef], k[0]);
kin.getRevRateConstants(&k[0]);
kin_ref.getRevRateConstants(&k_ref[0]);
EXPECT_FLOAT_EQ(k_ref[iRef], k[0]);
}
};
TEST_F(InterfaceKineticsFromScratch, add_surface_reaction)
{
// Reaction 3 on the metal surface
// surface_reaction( "H(m) + O(m) <=> OH(m) + (m)",
// [5.00000E+22, 0, 100.0], id = 'metal-rxn4')
Composition reac = parseCompString("H(m):1 O(m):1");
Composition prod = parseCompString("OH(m):1 (m):1");
Arrhenius rate(5e21, 0, 100.0e6 / GasConstant); // kJ/mol -> J/kmol
shared_ptr<InterfaceReaction>R(new InterfaceReaction(reac, prod, rate));
kin.addReaction(R);
kin.finalize();
check_rates(3);
}