Cantera 2.6.0
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"
12
13namespace Cantera
14{
15
16InterfaceData::InterfaceData()
17 : sqrtT(NAN)
18{
19}
20
21void InterfaceData::update(double T)
22{
23 throw CanteraError("InterfaceData::update",
24 "Missing state information: 'InterfaceData' requires species coverages.");
25}
26
27void InterfaceData::update(double T, const vector_fp& values)
28{
29 warn_user("InterfaceData::update",
30 "This method does not update the site density.");
31 ReactionData::update(T);
32 sqrtT = sqrt(T);
33 if (coverages.size() == 0) {
34 coverages = values;
35 logCoverages.resize(values.size());
36 } else if (values.size() == coverages.size()) {
37 std::copy(values.begin(), values.end(), coverages.begin());
38 } else {
39 throw CanteraError("InterfaceData::update",
40 "Incompatible lengths of coverage arrays: received {} elements while "
41 "{} are required.", values.size(), coverages.size());
42 }
43 for (size_t n = 0; n < coverages.size(); n++) {
44 logCoverages[n] = std::log(std::max(coverages[n], Tiny));
45 }
46}
47
48bool InterfaceData::update(const ThermoPhase& phase, const Kinetics& kin)
49{
50 int mf = 0;
51 for (size_t n = 0; n < kin.nPhases(); n++) {
52 mf += kin.thermo(n).stateMFNumber();
53 }
54
55 double T = phase.temperature();
56 bool changed = false;
57 const auto& surf = dynamic_cast<const SurfPhase&>(
58 kin.thermo(kin.surfacePhaseIndex()));
59 double site_density = surf.siteDensity();
60 if (density != site_density) {
61 density = surf.siteDensity();
62 changed = true;
63 }
64 if (T != temperature) {
65 ReactionData::update(T);
66 sqrtT = sqrt(T);
67 changed = true;
68 }
69 if (changed || mf != m_state_mf_number) {
70 surf.getCoverages(coverages.data());
71 for (size_t n = 0; n < coverages.size(); n++) {
72 logCoverages[n] = std::log(std::max(coverages[n], Tiny));
73 }
74 for (size_t n = 0; n < kin.nPhases(); n++) {
75 size_t start = kin.kineticsSpeciesIndex(0, n);
76 const auto& ph = kin.thermo(n);
77 electricPotentials[n] = ph.electricPotential();
78 ph.getPartialMolarEnthalpies(partialMolarEnthalpies.data() + start);
79 ph.getStandardChemPotentials(standardChemPotentials.data() + start);
80 size_t nsp = ph.nSpecies();
81 for (size_t k = 0; k < nsp; k++) {
82 // only used for exchange current density formulation
83 standardConcentrations[k + start] = ph.standardConcentration(k);
84 }
85 }
86 m_state_mf_number = mf;
87 changed = true;
88 }
89 return changed;
90}
91
92void InterfaceData::perturbTemperature(double deltaT)
93{
94 throw NotImplementedError("InterfaceData::perturbTemperature");
95}
96
97InterfaceRateBase::InterfaceRateBase()
98 : m_siteDensity(NAN)
99 , m_acov(0.)
100 , m_ecov(0.)
101 , m_mcov(0.)
102 , m_chargeTransfer(false)
103 , m_exchangeCurrentDensityFormulation(false)
104 , m_beta(0.5)
105 , m_deltaPotential_RT(NAN)
106 , m_deltaGibbs0_RT(NAN)
107 , m_prodStandardConcentrations(NAN)
108{
109}
110
112{
113 if (node.hasKey("coverage-dependencies")) {
115 node["coverage-dependencies"].as<AnyMap>(), node.units());
116 }
117 if (node.hasKey("beta")) {
118 m_beta = node["beta"].asDouble();
119 }
120 m_exchangeCurrentDensityFormulation = node.getBool(
121 "exchange-current-density-formulation", false);
122}
123
125{
126 if (!m_cov.empty()) {
127 AnyMap deps;
129 node["coverage-dependencies"] = std::move(deps);
130 }
131 if (m_chargeTransfer) {
132 if (m_beta != 0.5) {
133 node["beta"] = m_beta;
134 }
135 if (m_exchangeCurrentDensityFormulation) {
136 node["exchange-current-density-formulation"] = true;
137 }
138 }
139}
140
142 const UnitSystem& units)
143{
144 m_cov.clear();
145 m_ac.clear();
146 m_ec.clear();
147 m_mc.clear();
148 for (const auto& item : dependencies) {
149 double a, E, m;
150 if (item.second.is<AnyMap>()) {
151 auto& cov_map = item.second.as<AnyMap>();
152 a = cov_map["a"].asDouble();
153 m = cov_map["m"].asDouble();
154 E = units.convertActivationEnergy(cov_map["E"], "K");
155 } else {
156 auto& cov_vec = item.second.asVector<AnyValue>(3);
157 a = cov_vec[0].asDouble();
158 m = cov_vec[1].asDouble();
159 E = units.convertActivationEnergy(cov_vec[2], "K");
160 }
161 addCoverageDependence(item.first, a, m, E);
162 }
163}
164
166 bool asVector) const
167{
168 for (size_t k = 0; k < m_cov.size(); k++) {
169 if (asVector) {
170 // this preserves the previous 'coverage_deps' units
171 vector_fp dep(3);
172 dep[0] = m_ac[k];
173 dep[1] = m_mc[k];
174 dep[2] = m_ec[k];
175 dependencies[m_cov[k]] = std::move(dep);
176 } else {
177 AnyMap dep;
178 dep["a"] = m_ac[k];
179 dep["m"] = m_mc[k];
180 dep["E"].setQuantity(m_ec[k], "K", true);
181 dependencies[m_cov[k]] = std::move(dep);
182 }
183 }
184}
185
187 double a, double m, double e)
188{
189 if (std::find(m_cov.begin(), m_cov.end(), sp) == m_cov.end()) {
190 m_cov.push_back(sp);
191 m_ac.push_back(a);
192 m_ec.push_back(e);
193 m_mc.push_back(m);
194 m_indices.clear();
195 } else {
196 throw CanteraError("InterfaceRateBase::addCoverageDependence",
197 "Coverage for species '{}' is already specified.", sp);
198 }
199}
200
201void InterfaceRateBase::setSpecies(const std::vector<std::string>& species)
202{
203 m_indices.clear();
204 for (size_t k = 0; k < m_cov.size(); k++) {
205 auto it = find(species.begin(), species.end(), m_cov[k]);
206 if (it != species.end()) {
207 m_indices.emplace(k, it - species.begin());
208 } else {
209 throw CanteraError("InterfaceRateBase:setSpeciesIndices",
210 "Species list does not contain '{}'.", m_cov[k]);
211 }
212 }
213}
214
216 if (shared_data.ready) {
217 m_siteDensity = shared_data.density;
218 }
219
220 if (m_indices.size() != m_cov.size()) {
221 // object is not set up correctly (setSpecies needs to be run)
222 m_acov = NAN;
223 m_ecov = NAN;
224 m_mcov = NAN;
225 return;
226 }
227 m_acov = 0.0;
228 m_ecov = 0.0;
229 m_mcov = 0.0;
230 for (auto& item : m_indices) {
231 m_acov += m_ac[item.first] * shared_data.coverages[item.second];
232 m_ecov += m_ec[item.first] * shared_data.coverages[item.second];
233 m_mcov += m_mc[item.first] * shared_data.logCoverages[item.second];
234 }
235
236 // Update change in electrical potential energy
237 if (m_chargeTransfer) {
239 for (const auto& ch : m_netCharges) {
241 shared_data.electricPotentials[ch.first] * ch.second;
242 }
244 }
245
246 // Update quantities used for exchange current density formulation
247 if (m_exchangeCurrentDensityFormulation) {
248 m_deltaGibbs0_RT = 0.;
250 for (const auto& item : m_stoichCoeffs) {
252 shared_data.standardChemPotentials[item.first] * item.second;
253 if (item.second > 0.) {
255 shared_data.standardConcentrations[item.first];
256 }
257 }
259 }
260}
261
263{
265
267 if (!m_chargeTransfer) {
268 return;
269 }
270
271 m_stoichCoeffs.clear();
272 for (const auto& sp : rxn.reactants) {
273 m_stoichCoeffs.emplace_back(kin.kineticsSpeciesIndex(sp.first), -sp.second);
274 }
275 for (const auto& sp : rxn.products) {
276 m_stoichCoeffs.emplace_back(kin.kineticsSpeciesIndex(sp.first), sp.second);
277 }
278
279 m_netCharges.clear();
280 for (const auto& sp : m_stoichCoeffs) {
281 size_t n = kin.speciesPhaseIndex(sp.first);
282 size_t start = kin.kineticsSpeciesIndex(0, n);
283 double charge = kin.thermo(n).charge(sp.first - start);
284 m_netCharges.emplace_back(n, Faraday * charge * sp.second);
285 }
286}
287
288StickingCoverage::StickingCoverage()
289 : m_motzWise(false)
290 , m_explicitMotzWise(false)
291 , m_stickingSpecies("")
292 , m_explicitSpecies(false)
293 , m_surfaceOrder(NAN)
294 , m_multiplier(NAN)
295 , m_factor(NAN)
296{
297}
298
300{
301 m_motzWise = node.getBool("Motz-Wise", false);
302 m_explicitMotzWise = node.hasKey("Motz-Wise");
303 m_stickingSpecies = node.getString("sticking-species", "");
304 m_explicitSpecies = node.hasKey("sticking-species");
305}
306
308{
309 if (m_explicitMotzWise) {
310 node["Motz-Wise"] = m_motzWise;
311 }
312 if (m_explicitSpecies) {
313 node["sticking-species"] = m_stickingSpecies;
314 }
315}
316
318{
319 // Ensure that site density is initialized
320 const ThermoPhase& phase = kin.thermo(kin.surfacePhaseIndex());
321 const auto& surf = dynamic_cast<const SurfPhase&>(phase);
322 m_siteDensity = surf.siteDensity();
323 if (!m_explicitMotzWise) {
324 m_motzWise = kin.thermo().input().getBool("Motz-Wise", false);
325 }
326
327 // Identify the interface phase
328 size_t iInterface = kin.reactionPhaseIndex();
329
330 std::string sticking_species = m_stickingSpecies;
331 if (sticking_species == "") {
332 // Identify the sticking species if not explicitly given
333 std::vector<std::string> gasSpecies;
334 std::vector<std::string> anySpecies;
335 for (const auto& sp : rxn.reactants) {
336 size_t iPhase = kin.speciesPhaseIndex(kin.kineticsSpeciesIndex(sp.first));
337 if (iPhase != iInterface) {
338 // Non-interface species. There should be exactly one of these
339 // (either in gas phase or other phase)
340 if (kin.thermo(iPhase).phaseOfMatter() == "gas") {
341 gasSpecies.push_back(sp.first);
342 }
343 anySpecies.push_back(sp.first);
344 }
345 }
346 if (gasSpecies.size() == 1) {
347 // single sticking species in gas phase
348 sticking_species = gasSpecies[0];
349 } else if (anySpecies.size() == 1) {
350 // single sticking species in any phase
351 sticking_species = anySpecies[0];
352 } else if (anySpecies.size() == 0) {
353 throw InputFileError("StickingCoverage::setContext",
354 rxn.input, "No non-interface species found "
355 "in sticking reaction: '{}'", rxn.equation());
356 } else {
357 throw InputFileError("StickingCoverage::setContext",
358 rxn.input, "Multiple non-interface species ({})\nfound in sticking "
359 "reaction: '{}'.\nSticking species must be explicitly specified.",
360 fmt::format("'{}'", fmt::join(anySpecies, "', '")), rxn.equation());
361 }
362 }
363 m_stickingSpecies = sticking_species;
364
365 double surface_order = 0.0;
366 double multiplier = 1.0;
367 // Adjust the A-factor
368 for (const auto& sp : rxn.reactants) {
369 size_t iPhase = kin.speciesPhaseIndex(kin.kineticsSpeciesIndex(sp.first));
370 const ThermoPhase& p = kin.thermo(iPhase);
371 size_t k = p.speciesIndex(sp.first);
372 if (sp.first == sticking_species) {
373 multiplier *= sqrt(GasConstant / (2 * Pi * p.molecularWeight(k)));
374 } else {
375 // Non-sticking species. Convert from coverages used in the
376 // sticking probability expression to the concentration units
377 // used in the mass action rate expression. For surface phases,
378 // the dependence on the site density is incorporated when the
379 // rate constant is evaluated, since we don't assume that the
380 // site density is known at this time.
381 double order = getValue(rxn.orders, sp.first, sp.second);
382 if (&p == &surf) {
383 multiplier *= pow(surf.size(k), order);
384 surface_order += order;
385 } else {
386 multiplier *= pow(p.standardConcentration(k), -order);
387 }
388 }
389 }
390 m_surfaceOrder = surface_order;
391 m_multiplier = multiplier;
392}
393
394}
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:399
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
Definition: AnyMap.h:598
const std::string & getString(const std::string &key, const std::string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition: AnyMap.cpp:1502
bool getBool(const std::string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
Definition: AnyMap.cpp:1487
bool hasKey(const std::string &key) const
Returns true if the map contains an item named key.
Definition: AnyMap.cpp:1406
A wrapper for a variable whose type is determined at runtime.
Definition: AnyMap.h:84
double & asDouble()
Return the held value as a double, if it is a double or a long int.
Definition: AnyMap.cpp:801
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition: AnyMap.h:702
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.
void addCoverageDependence(const std::string &sp, double a, double m, double e)
Add a coverage dependency for species sp, with exponential dependence a, power-law exponent m,...
void setParameters(const AnyMap &node)
Perform object setup based on AnyMap node information.
double m_prodStandardConcentrations
Products of standard concentrations.
std::vector< std::pair< size_t, double > > m_netCharges
Pairs of phase index and net electric charges (same order as m_stoichCoeffs)
double m_deltaGibbs0_RT
Normalized standard state Gibbs free energy change.
vector_fp m_mc
Vector holding coverage-specific power-law exponents.
std::map< size_t, size_t > m_indices
Map holding indices of coverage species.
vector_fp m_ec
Vector holding coverage-specific activation energy dependence.
vector_fp m_ac
Vector holding coverage-specific exponential dependence.
bool m_chargeTransfer
Boolean indicating use of electrochemistry.
void getCoverageDependencies(AnyMap &dependencies, bool asVector=false) const
Store parameters needed to reconstruct coverage dependencies.
double m_acov
Coverage contribution to pre-exponential factor.
std::vector< std::pair< size_t, double > > m_stoichCoeffs
Pairs of species index and multiplers to calculate enthalpy change.
void setSpecies(const std::vector< std::string > &species)
Set association with an ordered list of all species associated with a given Kinetics object.
void getParameters(AnyMap &node) const
Store parameters needed to reconstruct an identical object.
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.
std::vector< std::string > m_cov
Vector holding names of coverage species.
double m_deltaPotential_RT
Normalized electric potential energy change.
Public interface for kinetics managers.
Definition: Kinetics.h:114
size_t reactionPhaseIndex() const
Phase where the reactions occur.
Definition: Kinetics.h:218
ThermoPhase & thermo(size_t n=0)
This method returns a reference to the nth ThermoPhase object defined in this kinetics mechanism.
Definition: Kinetics.h:231
size_t nPhases() const
The number of phases participating in the reaction mechanism.
Definition: Kinetics.h:171
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition: Kinetics.h:265
size_t surfacePhaseIndex() const
This returns the integer index of the phase which has ThermoPhase type cSurf.
Definition: Kinetics.h:206
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:373
An error indicating that an unimplemented function has been called.
Definition: ctexceptions.h:187
doublereal 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:630
doublereal molecularWeight(size_t k) const
Molecular weight of species k.
Definition: Phase.cpp:492
int stateMFNumber() const
Return the State Mole Fraction Number.
Definition: Phase.h:858
doublereal temperature() const
Temperature (K).
Definition: Phase.h:654
const std::vector< std::string > & speciesNames() const
Return a const reference to the vector of species names.
Definition: Phase.cpp:206
size_t speciesIndex(const std::string &name) const
Returns the index of a species named 'name' within the Phase object.
Definition: Phase.cpp:187
Abstract base class which stores data about a reaction and its rate parameterization so that it can b...
Definition: Reaction.h:33
Composition orders
Forward reaction order with respect to specific species.
Definition: Reaction.h:143
bool usesElectrochemistry(const Kinetics &kin) const
Check whether reaction uses electrochemistry.
Definition: Reaction.cpp:540
Composition products
Product species and stoichiometric coefficients.
Definition: Reaction.h:138
Composition reactants
Reactant species and stoichiometric coefficients.
Definition: Reaction.h:135
AnyMap input
Input data used for specific models.
Definition: Reaction.h:163
std::string equation() const
The chemical equation for this reaction.
Definition: Reaction.cpp:296
void setStickingParameters(const AnyMap &node)
Perform object setup based on AnyMap node information.
bool m_explicitSpecies
Boolean flag.
std::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:125
double siteDensity() const
Returns the site density.
Definition: SurfPhase.h:308
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:102
virtual std::string phaseOfMatter() const
String indicating the mechanical phase of the matter in this Phase.
Definition: ThermoPhase.h:142
virtual doublereal standardConcentration(size_t k=0) const
Return the standard concentration for the kth species.
Definition: ThermoPhase.h:425
doublereal electricPotential() const
Returns the electric potential of this phase (V).
Definition: ThermoPhase.h:326
const AnyMap & input() const
Access input data associated with the phase description.
Unit conversion utility.
Definition: Units.h:161
double convertActivationEnergy(double value, const std::string &src, const std::string &dest) const
Convert value from the units of src to the units of dest, allowing for the different dimensions that ...
Definition: Units.cpp:658
const double Pi
Pi.
Definition: ct_defs.h:52
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const double Tiny
Small number to compare differences of mole fractions against.
Definition: ct_defs.h:170
const double Faraday
Faraday constant [C/kmol].
Definition: ct_defs.h:128
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:184
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:113
void warn_user(const std::string &method, const std::string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition: global.h:245
const U & getValue(const std::map< T, U > &m, const T &key, const U &default_val)
Const accessor for a value in a std::map.
Definition: utilities.h:184
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.
Definition: InterfaceRate.h:40
vector_fp electricPotentials
electric potentials of phases
Definition: InterfaceRate.h:67
vector_fp coverages
surface coverages
Definition: InterfaceRate.h:65
vector_fp logCoverages
logarithm of surface coverages
Definition: InterfaceRate.h:66
vector_fp standardChemPotentials
standard state chemical potentials
Definition: InterfaceRate.h:68
vector_fp standardConcentrations
standard state concentrations
Definition: InterfaceRate.h:69
double temperature
temperature
Definition: ReactionData.h:109