Open speth opened 4 years ago
Just a brief update: we have started some work on this, and have working modules in place for three different charge transfer mechanisms: Butler-Volmer, Marcus theory, and Marcus-Hush-Chidsey.
They are in a pretty basic state, at the moment: concentration dependence is only partially implemented, and setting other properties like reaction orders is yet-to-be-implemented. But it is probably a good time to write up a basic test suite and submit an initial PR, before adding in the "next layer" of features.
As a concrete example, some of this has been implemented in a branch (https://github.com/CSM-Offenburg/cantera/blob/a91ed333a0f6448f0ab99fa76c9be0f1368ce03a/src/kinetics/InterfaceKinetics.cpp#L349-L389). The following is a part of InterfaceKinetics::updateROP()
:
for (size_t i = 0; i < m_beta.size(); i++) {
// Get the reaction index, relative to the array of all interface
// reactions:
size_t irxn = m_ctrxn[i];
// Update electrochemical potentials. This is equal to the
// overpotential, multiplied by the elementary charge transfered and
// the Farday constant, for each reaction:
vector_fp echemPotentials(nReactions(), 0.0);
getDeltaElectrochemPotentials(&echemPotentials[0]);
// Reciprocal of RT:
double rrt = 1.0 / thermo(reactionPhaseIndex()).RT();
// Calculate the BV-type form directly, if specified:
if (m_ctrxn_form[i] > 0 && m_ctrxn_form[i] < 4) {
// Scale the concentration dependence by the refeence
// concentrations:
m_ropf[irxn] *= m_ctrxn_concScaleFactor[i];
double beta = m_beta[i];
if (m_ctrxn_form[i] == 3){ // marcus theory
beta += echemPotentials[irxn]/4/m_lambda[i];
}
// Calculate rop_f and rop_r:
m_ropf[irxn] *= exp(-beta*echemPotentials[irxn]*rrt)/Faraday;
m_ropr[irxn] = m_ropf[irxn]*exp(echemPotentials[irxn]*rrt);
} else if (m_ctrxn_form[i] == 4) {
// Scale the concentration dependence by the reference
// concentrations:
double i_o = m_ropf[irxn] * m_ctrxn_concScaleFactor[i];
double eta_RT = echemPotentials[irxn]*rrt;
double lambda_RT = m_lambda[i]*rrt;
double erfc_term = erfc((lambda_RT - sqrt(1.0 + sqrt(lambda_RT)
+ eta_RT*eta_RT))/2/sqrt(lambda_RT));
m_ropf[irxn] = i_o*sqrt(Pi*lambda_RT)
* erfc_term/(1. + exp(eta_RT))/Faraday;
m_ropr[irxn] = i_o*sqrt(Pi*lambda_RT)
* erfc_term/(1. + exp(-eta_RT))/Faraday;
}
}
Okay, so moving discussion from #cantera1416 over here.
@korffdm has an implementation that works with the current ReactionRateFactory
format. It is not the cleanest implementation, in terms of making the theory easy to trace and debug, but it is accurate (it passes local tests we have developed and will make part of the PR, if we stick with the current development), and appears to be the shortest development path to getting these capabilities into Cantera.
One note, w/r/t the theory described above.
All that aside, I would like to discuss if an implementation closer to the code snippets in @speth's comment on Feb 15 is possible. From the refactorization, it looks as though all production rates currently go through updateROP
, where the procedure is:
k_fwd/K_eq
Before I speculate further, let me make sure I understand the situation correctly: am I correct that all reactions go through this procedure?
Abstract
There are several ways of specifying the rate constant for electrochemical reactions which are used in practice. The aim of this enhancement is to support input of rate constants using those formulations that are convenient for users, while keeping the underlying implementation reasonably clean & efficient.
Motivation
Cantera currently has had an incomplete and not fully-implemented set of options for defining electrochemical reaction rates. The non-functional options (namely, those related to the Butler-Volmer formulation) were deprecated in Cantera 2.5 (See Cantera/cantera#749 and Cantera/cantera#750).
In the discussion of that issue, @decaluwe provided a summary of the theoretical bases for electrochemical reaction types, which can be found here: Charge.Transfer.in.Cantera.pdf.
Some additional context provided by @wbessler in the above-linked issue is also worth highlighting: https://github.com/Cantera/cantera/issues/749#issuecomment-559727736
Possible Solutions
To summarize the suggestions from both @decaluwe and @wbessler, it makes sense to support two general types of electrochemical reactions, one corresponding to Marcus theory / elementary reactions, and one for implementing different variants of the Butler-Volmer equation. In more detail:
Marcus theory:
k_fwd
or pass ini0
and have it converted to an equivalentk_fwd
beta
, contains a specification ofi0
, or, in the absence of either of these, the reaction equation is used to identify a charge-transfer reaction.Butler-Volmer:
Inputs: The user should be able to:
k_fwd
and have it converted to an equivalenti0
.i0
(likely via Arrhenius parameters)i*0
for a known reference state (known species concentrations), along with a list of these reference concentrations[X*_k]
, which is then used to calculatei0
.i0
Outputs: The user should also be able to query, for a given state:
i0
eta
.@ischoegl has also raised concerns about the fact that checking whether a reaction involves charge transfer currently involves parsing the reaction equation from its string form twice. If desired, this duplicate parsing could be eliminated by making parsing of the reaction equation a separate step from instantiation of a
Reaction
object.