Cantera  3.0.0
Loading...
Searching...
No Matches
InterfaceRate.cpp
Go to the documentation of this file.
1//! @file InterfaceRate.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
11#include "cantera/base/AnyMap.h"
13
14namespace Cantera
15{
16
18{
19 throw CanteraError("InterfaceData::update",
20 "Missing state information: 'InterfaceData' requires species coverages.");
21}
22
23void InterfaceData::update(double T, const vector<double>& values)
24{
25 warn_user("InterfaceData::update",
26 "This method does not update the site density.");
28 sqrtT = sqrt(T);
29 if (coverages.size() == 0) {
30 coverages = values;
31 logCoverages.resize(values.size());
32 } else if (values.size() == coverages.size()) {
33 std::copy(values.begin(), values.end(), coverages.begin());
34 } else {
35 throw CanteraError("InterfaceData::update",
36 "Incompatible lengths of coverage arrays: received {} elements while "
37 "{} are required.", values.size(), coverages.size());
38 }
39 for (size_t n = 0; n < coverages.size(); n++) {
40 logCoverages[n] = std::log(std::max(coverages[n], Tiny));
41 }
42}
43
44bool InterfaceData::update(const ThermoPhase& phase, const Kinetics& kin)
45{
46 int mf = 0;
47 for (size_t n = 0; n < kin.nPhases(); n++) {
48 mf += kin.thermo(n).stateMFNumber();
49 }
50
51 double T = phase.temperature();
52 bool changed = false;
53 const auto& surf = dynamic_cast<const SurfPhase&>(
54 kin.thermo(kin.reactionPhaseIndex()));
55 double site_density = surf.siteDensity();
56 if (density != site_density) {
57 density = surf.siteDensity();
58 changed = true;
59 }
60 if (T != temperature) {
62 sqrtT = sqrt(T);
63 changed = true;
64 }
65 if (changed || mf != m_state_mf_number) {
66 surf.getCoverages(coverages.data());
67 for (size_t n = 0; n < coverages.size(); n++) {
68 logCoverages[n] = std::log(std::max(coverages[n], Tiny));
69 }
70 for (size_t n = 0; n < kin.nPhases(); n++) {
71 size_t start = kin.kineticsSpeciesIndex(0, n);
72 const auto& ph = kin.thermo(n);
73 electricPotentials[n] = ph.electricPotential();
74 ph.getPartialMolarEnthalpies(partialMolarEnthalpies.data() + start);
75 ph.getStandardChemPotentials(standardChemPotentials.data() + start);
76 size_t nsp = ph.nSpecies();
77 for (size_t k = 0; k < nsp; k++) {
78 // only used for exchange current density formulation
79 standardConcentrations[k + start] = ph.standardConcentration(k);
80 }
81 }
83 changed = true;
84 }
85 return changed;
86}
87
88void InterfaceData::perturbTemperature(double deltaT)
89{
90 throw NotImplementedError("InterfaceData::perturbTemperature");
91}
92
93InterfaceRateBase::InterfaceRateBase()
94 : m_siteDensity(NAN)
95 , m_acov(0.)
96 , m_ecov(0.)
97 , m_mcov(0.)
98 , m_chargeTransfer(false)
99 , m_exchangeCurrentDensityFormulation(false)
100 , m_beta(0.5)
101 , m_deltaPotential_RT(NAN)
102 , m_deltaGibbs0_RT(NAN)
103 , m_prodStandardConcentrations(NAN)
104{
105}
106
108{
109 if (node.hasKey("coverage-dependencies")) {
111 node["coverage-dependencies"].as<AnyMap>(), node.units());
112 }
113 if (node.hasKey("beta")) {
114 m_beta = node["beta"].asDouble();
115 }
116 m_exchangeCurrentDensityFormulation = node.getBool(
117 "exchange-current-density-formulation", false);
118}
119
121{
122 if (!m_cov.empty()) {
123 AnyMap deps;
125 node["coverage-dependencies"] = std::move(deps);
126 }
127 if (m_chargeTransfer) {
128 if (m_beta != 0.5) {
129 node["beta"] = m_beta;
130 }
131 if (m_exchangeCurrentDensityFormulation) {
132 node["exchange-current-density-formulation"] = true;
133 }
134 }
135}
136
138 const UnitSystem& units)
139{
140 m_cov.clear();
141 m_ac.clear();
142 m_ec.clear();
143 m_mc.clear();
144 m_lindep.clear();
145 for (const auto& [species, coeffs] : dependencies) {
146 double a, m;
147 vector<double> E(5, 0.0);
148 if (coeffs.is<AnyMap>()) {
149 auto& cov_map = coeffs.as<AnyMap>();
150 a = cov_map["a"].asDouble();
151 m = cov_map["m"].asDouble();
152 if (cov_map["E"].isScalar()) {
153 m_lindep.push_back(true);
154 E[1] = units.convertActivationEnergy(cov_map["E"], "K");
155 } else {
156 m_lindep.push_back(false);
157 auto& E_temp = cov_map["E"].asVector<AnyValue>(1, 4);
158 for (size_t i = 0; i < E_temp.size(); i++) {
159 E[i+1] = units.convertActivationEnergy(E_temp[i], "K");
160 }
161 }
162 } else {
163 auto& cov_vec = coeffs.asVector<AnyValue>(3);
164 a = cov_vec[0].asDouble();
165 m = cov_vec[1].asDouble();
166 if (cov_vec[2].isScalar()) {
167 m_lindep.push_back(true);
168 E[1] = units.convertActivationEnergy(cov_vec[2], "K");
169 } else {
170 m_lindep.push_back(false);
171 auto& E_temp = cov_vec[2].asVector<AnyValue>(1, 4);
172 for (size_t i = 0; i < E_temp.size(); i++) {
173 E[i+1] = units.convertActivationEnergy(E_temp[i], "K");
174 }
175 }
176 }
177 addCoverageDependence(species, a, m, E);
178 }
179}
180
182 bool asVector) const
183{
184 for (size_t k = 0; k < m_cov.size(); k++) {
185 if (asVector) {
186 // this preserves the previous 'coverage_deps' units
187 warn_deprecated("InterfaceRateBase::getCoverageDependencies",
188 "To be changed after Cantera 3.0: second argument will be removed.");
189 vector<double> dep(3);
190 if (m_lindep[k]) {
191 dep[2] = m_ec[k][1];
192 } else {
193 throw NotImplementedError("InterfaceRateBase::getCoverageDependencies",
194 "Polynomial dependency not implemented for asVector.");
195 }
196 dep[0] = m_ac[k];
197 dep[1] = m_mc[k];
198 dependencies[m_cov[k]] = std::move(dep);
199 } else {
200 AnyMap dep;
201 dep["a"] = m_ac[k];
202 dep["m"] = m_mc[k];
203 if (m_lindep[k]) {
204 dep["E"].setQuantity(m_ec[k][1], "K", true);
205 } else {
206 vector<AnyValue> E_temp(4);
207 for (size_t i = 0; i < m_ec[k].size() - 1; i++) {
208 E_temp[i].setQuantity(m_ec[k][i+1], "K", true);
209 }
210 dep["E"] = E_temp;
211 }
212 dependencies[m_cov[k]] = std::move(dep);
213 }
214 }
215}
216
217void InterfaceRateBase::addCoverageDependence(const string& sp, double a, double m,
218 const vector<double>& e)
219{
220 if (std::find(m_cov.begin(), m_cov.end(), sp) == m_cov.end()) {
221 m_cov.push_back(sp);
222 m_ac.push_back(a);
223 m_ec.push_back(e);
224 m_mc.push_back(m);
225 m_indices.clear();
226 } else {
227 throw CanteraError("InterfaceRateBase::addCoverageDependence",
228 "Coverage for species '{}' is already specified.", sp);
229 }
230}
231
232void InterfaceRateBase::setSpecies(const vector<string>& species)
233{
234 m_indices.clear();
235 for (size_t k = 0; k < m_cov.size(); k++) {
236 auto it = find(species.begin(), species.end(), m_cov[k]);
237 if (it != species.end()) {
238 m_indices.emplace(k, it - species.begin());
239 } else {
240 throw CanteraError("InterfaceRateBase:setSpeciesIndices",
241 "Species list does not contain '{}'.", m_cov[k]);
242 }
243 }
244}
245
247 if (shared_data.ready) {
248 m_siteDensity = shared_data.density;
249 }
250
251 if (m_indices.size() != m_cov.size()) {
252 // object is not set up correctly (setSpecies needs to be run)
253 m_acov = NAN;
254 m_ecov = NAN;
255 m_mcov = NAN;
256 return;
257 }
258 m_acov = 0.0;
259 m_ecov = 0.0;
260 m_mcov = 0.0;
261 for (auto& [iCov, iKin] : m_indices) {
262 m_acov += m_ac[iCov] * shared_data.coverages[iKin];
263 if (m_lindep[iCov]) {
264 m_ecov += m_ec[iCov][1] * shared_data.coverages[iKin];
265 } else {
266 m_ecov += poly4(shared_data.coverages[iKin], m_ec[iCov].data());
267 }
268 m_mcov += m_mc[iCov] * shared_data.logCoverages[iKin];
269 }
270
271 // Update change in electrical potential energy
272 if (m_chargeTransfer) {
274 for (const auto& [iPhase, netCharge] : m_netCharges) {
276 shared_data.electricPotentials[iPhase] * netCharge;
277 }
279 }
280
281 // Update quantities used for exchange current density formulation
282 if (m_exchangeCurrentDensityFormulation) {
283 m_deltaGibbs0_RT = 0.;
285 for (const auto& [k, stoich] : m_stoichCoeffs) {
287 shared_data.standardChemPotentials[k] * stoich;
288 if (stoich > 0.) {
290 shared_data.standardConcentrations[k];
291 }
292 }
294 }
295}
296
298{
300
302 if (!m_chargeTransfer) {
303 return;
304 }
305
306 m_stoichCoeffs.clear();
307 for (const auto& [name, stoich] : rxn.reactants) {
308 m_stoichCoeffs.emplace_back(kin.kineticsSpeciesIndex(name), -stoich);
309 }
310 for (const auto& [name, stoich] : rxn.products) {
311 m_stoichCoeffs.emplace_back(kin.kineticsSpeciesIndex(name), stoich);
312 }
313
314 m_netCharges.clear();
315 for (const auto& [k, stoich] : m_stoichCoeffs) {
316 size_t n = kin.speciesPhaseIndex(k);
317 size_t start = kin.kineticsSpeciesIndex(0, n);
318 double charge = kin.thermo(n).charge(k - start);
319 m_netCharges.emplace_back(n, Faraday * charge * stoich);
320 }
321}
322
323StickingCoverage::StickingCoverage()
324 : m_motzWise(false)
325 , m_explicitMotzWise(false)
326 , m_stickingSpecies("")
327 , m_explicitSpecies(false)
328 , m_surfaceOrder(NAN)
329 , m_multiplier(NAN)
330 , m_factor(NAN)
331{
332}
333
335{
336 m_motzWise = node.getBool("Motz-Wise", false);
337 m_explicitMotzWise = node.hasKey("Motz-Wise");
338 m_stickingSpecies = node.getString("sticking-species", "");
339 m_explicitSpecies = node.hasKey("sticking-species");
340}
341
343{
344 if (m_explicitMotzWise) {
345 node["Motz-Wise"] = m_motzWise;
346 }
347 if (m_explicitSpecies) {
348 node["sticking-species"] = m_stickingSpecies;
349 }
350}
351
353{
354 // Ensure that site density is initialized
355 const ThermoPhase& phase = kin.thermo(kin.reactionPhaseIndex());
356 const auto& surf = dynamic_cast<const SurfPhase&>(phase);
357 m_siteDensity = surf.siteDensity();
358 if (!m_explicitMotzWise) {
359 m_motzWise = kin.thermo().input().getBool("Motz-Wise", false);
360 }
361
362 // Identify the interface phase
363 size_t iInterface = kin.reactionPhaseIndex();
364
365 string sticking_species = m_stickingSpecies;
366 if (sticking_species == "") {
367 // Identify the sticking species if not explicitly given
368 vector<string> gasSpecies;
369 vector<string> anySpecies;
370 for (const auto& [name, stoich] : rxn.reactants) {
371 size_t iPhase = kin.speciesPhaseIndex(kin.kineticsSpeciesIndex(name));
372 if (iPhase != iInterface) {
373 // Non-interface species. There should be exactly one of these
374 // (either in gas phase or other phase)
375 if (kin.thermo(iPhase).phaseOfMatter() == "gas") {
376 gasSpecies.push_back(name);
377 }
378 anySpecies.push_back(name);
379 }
380 }
381 if (gasSpecies.size() == 1) {
382 // single sticking species in gas phase
383 sticking_species = gasSpecies[0];
384 } else if (anySpecies.size() == 1) {
385 // single sticking species in any phase
386 sticking_species = anySpecies[0];
387 } else if (anySpecies.size() == 0) {
388 throw InputFileError("StickingCoverage::setContext",
389 rxn.input, "No non-interface species found "
390 "in sticking reaction: '{}'", rxn.equation());
391 } else {
392 throw InputFileError("StickingCoverage::setContext",
393 rxn.input, "Multiple non-interface species ({})\nfound in sticking "
394 "reaction: '{}'.\nSticking species must be explicitly specified.",
395 fmt::format("'{}'", fmt::join(anySpecies, "', '")), rxn.equation());
396 }
397 }
398 m_stickingSpecies = sticking_species;
399
400 double surface_order = 0.0;
401 double multiplier = 1.0;
402 // Adjust the A-factor
403 for (const auto& [name, stoich] : rxn.reactants) {
404 size_t iPhase = kin.speciesPhaseIndex(kin.kineticsSpeciesIndex(name));
405 const ThermoPhase& p = kin.thermo(iPhase);
406 size_t k = p.speciesIndex(name);
407 if (name == sticking_species) {
408 multiplier *= sqrt(GasConstant / (2 * Pi * p.molecularWeight(k)));
409 } else {
410 // Non-sticking species. Convert from coverages used in the
411 // sticking probability expression to the concentration units
412 // used in the mass action rate expression. For surface phases,
413 // the dependence on the site density is incorporated when the
414 // rate constant is evaluated, since we don't assume that the
415 // site density is known at this time.
416 double order = getValue(rxn.orders, name, stoich);
417 if (&p == &surf) {
418 multiplier *= pow(surf.size(k), order);
419 surface_order += order;
420 } else {
421 multiplier *= pow(p.standardConcentration(k), -order);
422 }
423 }
424 }
425 m_surfaceOrder = surface_order;
426 m_multiplier = multiplier;
427}
428
429}
Header for reaction rates that occur at interfaces.
Base class for kinetics managers and also contains the kineticsmgr module documentation (see Kinetics...
Header for a simple thermodynamics model of a surface phase derived from ThermoPhase,...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:427
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition AnyMap.cpp:1423
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
Definition AnyMap.h:630
bool getBool(const string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
Definition AnyMap.cpp:1515
const string & getString(const string &key, const string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition AnyMap.cpp:1530
A wrapper for a variable whose type is determined at runtime.
Definition AnyMap.h:86
double & asDouble()
Return the held value as a double, if it is a double or a long int.
Definition AnyMap.cpp:824
Base class for exceptions thrown by Cantera classes.
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition AnyMap.h:738
double m_mcov
Coverage term in reaction rate.
void setCoverageDependencies(const AnyMap &dependencies, const UnitSystem &units=UnitSystem())
Set coverage dependencies based on AnyMap node information.
double m_beta
Electrochemistry only.
double m_ecov
Coverage contribution to activation energy.
vector< pair< size_t, double > > m_stoichCoeffs
Pairs of species index and multipliers to calculate enthalpy change.
vector< double > m_ac
Vector holding coverage-specific exponential dependence.
void setParameters(const AnyMap &node)
Perform object setup based on AnyMap node information.
void setSpecies(const vector< string > &species)
Set association with an ordered list of all species associated with a given Kinetics object.
vector< string > m_cov
Vector holding names of coverage species.
double m_prodStandardConcentrations
Products of standard concentrations.
double m_deltaGibbs0_RT
Normalized standard state Gibbs free energy change.
virtual void addCoverageDependence(const string &sp, double a, double m, const vector< double > &e)
Add a coverage dependency for species sp, with exponential dependence a, power-law exponent m,...
bool m_chargeTransfer
Boolean indicating use of electrochemistry.
void getCoverageDependencies(AnyMap &dependencies, bool asVector=false) const
Store parameters needed to reconstruct coverage dependencies.
vector< double > m_mc
Vector holding coverage-specific power-law exponents.
map< size_t, size_t > m_indices
Map from coverage dependencies stored in this object to the index of the coverage species in the Kine...
vector< pair< size_t, double > > m_netCharges
Pairs of phase index and net electric charges (same order as m_stoichCoeffs)
double m_acov
Coverage contribution to pre-exponential factor.
vector< bool > m_lindep
Vector holding boolean for linear dependence.
void getParameters(AnyMap &node) const
Store parameters needed to reconstruct an identical object.
vector< vector< double > > m_ec
Vector holding coverage-specific activation energy dependence as a 5-membered array of polynomial coe...
double m_siteDensity
Site density [kmol/m^2].
void setContext(const Reaction &rxn, const Kinetics &kin)
Build rate-specific parameters based on Reaction and Kinetics context.
void updateFromStruct(const InterfaceData &shared_data)
Update reaction rate parameters.
double m_deltaPotential_RT
Normalized electric potential energy change.
Public interface for kinetics managers.
Definition Kinetics.h:126
size_t reactionPhaseIndex() const
Phase where the reactions occur.
Definition Kinetics.h:235
ThermoPhase & thermo(size_t n=0)
This method returns a reference to the nth ThermoPhase object defined in this kinetics mechanism.
Definition Kinetics.h:254
size_t nPhases() const
The number of phases participating in the reaction mechanism.
Definition Kinetics.h:185
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition Kinetics.h:288
size_t speciesPhaseIndex(size_t k) const
This function takes as an argument the kineticsSpecies index (that is, the list index in the list of ...
Definition Kinetics.cpp:323
An error indicating that an unimplemented function has been called.
double temperature() const
Temperature (K).
Definition Phase.h:662
size_t speciesIndex(const string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:138
int stateMFNumber() const
Return the State Mole Fraction Number.
Definition Phase.h:866
const vector< string > & speciesNames() const
Return a const reference to the vector of species names.
Definition Phase.cpp:157
double molecularWeight(size_t k) const
Molecular weight of species k.
Definition Phase.cpp:482
double charge(size_t k) const
Dimensionless electrical charge of a single molecule of species k The charge is normalized by the the...
Definition Phase.h:638
Abstract base class which stores data about a reaction and its rate parameterization so that it can b...
Definition Reaction.h:27
Composition orders
Forward reaction order with respect to specific species.
Definition Reaction.h:140
string equation() const
The chemical equation for this reaction.
Definition Reaction.cpp:347
bool usesElectrochemistry(const Kinetics &kin) const
Check whether reaction uses electrochemistry.
Definition Reaction.cpp:690
Composition products
Product species and stoichiometric coefficients.
Definition Reaction.h:135
Composition reactants
Reactant species and stoichiometric coefficients.
Definition Reaction.h:132
AnyMap input
Input data used for specific models.
Definition Reaction.h:160
void setStickingParameters(const AnyMap &node)
Perform object setup based on AnyMap node information.
bool m_explicitSpecies
Boolean flag.
string m_stickingSpecies
string identifying sticking species
bool m_explicitMotzWise
Correction cannot be overriden by default.
void getStickingParameters(AnyMap &node) const
Store parameters needed to reconstruct an identical object.
double m_multiplier
multiplicative factor in rate expression
bool m_motzWise
boolean indicating whether Motz & Wise correction is used
double m_surfaceOrder
exponent applied to site density term
void setContext(const Reaction &rxn, const Kinetics &kin)
Build rate-specific parameters based on Reaction and Kinetics context.
A simple thermodynamic model for a surface phase, assuming an ideal solution model.
Definition SurfPhase.h:98
double siteDensity() const
Returns the site density.
Definition SurfPhase.h:221
Base class for a phase with thermodynamic properties.
virtual double standardConcentration(size_t k=0) const
Return the standard concentration for the kth species.
virtual string phaseOfMatter() const
String indicating the mechanical phase of the matter in this Phase.
const AnyMap & input() const
Access input data associated with the phase description.
Unit conversion utility.
Definition Units.h:169
double convertActivationEnergy(double value, const string &src, const string &dest) const
Convert value from the units of src to the units of dest, allowing for the different dimensions that ...
Definition Units.cpp:673
R poly4(D x, R *c)
Evaluates a polynomial of order 4.
Definition utilities.h:153
const double Faraday
Faraday constant [C/kmol].
Definition ct_defs.h:131
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:120
const double Pi
Pi.
Definition ct_defs.h:68
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition global.h:267
Namespace for the Cantera kernel.
Definition AnyMap.cpp:564
const double Tiny
Small number to compare differences of mole fractions against.
Definition ct_defs.h:173
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition AnyMap.cpp:1926
const U & getValue(const map< T, U > &m, const T &key, const U &default_val)
Const accessor for a value in a map.
Definition utilities.h:190
int m_state_mf_number
integer that is incremented when composition changes
vector< double > partialMolarEnthalpies
partial molar enthalpies
bool ready
boolean indicating whether vectors are accessible
double density
used to determine if updates are needed
Data container holding shared data for reaction rate specification with interfaces.
vector< double > logCoverages
logarithm of surface coverages
vector< double > electricPotentials
electric potentials of phases
bool update(const ThermoPhase &bulk, const Kinetics &kin) override
Update data container based on thermodynamic phase state.
vector< double > coverages
surface coverages
vector< double > standardChemPotentials
standard state chemical potentials
vector< double > standardConcentrations
standard state concentrations
double sqrtT
square root of temperature
virtual void update(double T)
Update data container based on temperature T
double temperature
temperature
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...