Cantera  4.0.0a1
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, span<const 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.assign(values.begin(), values.end());
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&>(kin.thermo(0));
54 double site_density = surf.siteDensity();
55 if (density != site_density) {
56 density = surf.siteDensity();
57 changed = true;
58 }
59 if (T != temperature) {
61 sqrtT = sqrt(T);
62 changed = true;
63 }
64 if (changed || mf != m_state_mf_number) {
65 surf.getCoverages(coverages);
66 for (size_t n = 0; n < coverages.size(); n++) {
67 logCoverages[n] = std::log(std::max(coverages[n], Tiny));
68 }
69 for (size_t n = 0; n < kin.nPhases(); n++) {
70 size_t start = kin.kineticsSpeciesIndex(0, n);
71 const auto& ph = kin.thermo(n);
72 electricPotentials[n] = ph.electricPotential();
73 ph.getPartialMolarEnthalpies(
74 span<double>(partialMolarEnthalpies).subspan(start, ph.nSpecies()));
75 ph.getStandardChemPotentials(
76 span<double>(standardChemPotentials).subspan(start, ph.nSpecies()));
77 size_t nsp = ph.nSpecies();
78 for (size_t k = 0; k < nsp; k++) {
79 // only used for exchange current density formulation
80 standardConcentrations[k + start] = ph.standardConcentration(k);
81 }
82 }
84 changed = true;
85 }
86 return changed;
87}
88
89void InterfaceData::perturbTemperature(double deltaT)
90{
91 throw NotImplementedError("InterfaceData::perturbTemperature");
92}
93
95 coverages.resize(kin.thermo().nSpecies(), 0.);
96 logCoverages.resize(kin.thermo().nSpecies(), 0.);
97 partialMolarEnthalpies.resize(kin.nTotalSpecies(), 0.);
98 electricPotentials.resize(kin.nPhases(), 0.);
99 standardChemPotentials.resize(kin.nTotalSpecies(), 0.);
100 standardConcentrations.resize(kin.nTotalSpecies(), 0.);
101 ready = true;
102}
103
104InterfaceRateBase::InterfaceRateBase()
105 : m_siteDensity(NAN)
106 , m_acov(0.)
107 , m_ecov(0.)
108 , m_mcov(0.)
109 , m_chargeTransfer(false)
110 , m_exchangeCurrentDensityFormulation(false)
111 , m_beta(0.5)
112 , m_deltaPotential_RT(NAN)
113 , m_deltaGibbs0_RT(NAN)
114 , m_prodStandardConcentrations(NAN)
115{
116}
117
119{
120 if (node.hasKey("coverage-dependencies")) {
122 node["coverage-dependencies"].as<AnyMap>(), node.units());
123 }
124 if (node.hasKey("beta")) {
125 m_beta = node["beta"].asDouble();
126 }
127 m_exchangeCurrentDensityFormulation = node.getBool(
128 "exchange-current-density-formulation", false);
129}
130
132{
133 if (!m_cov.empty()) {
134 AnyMap deps;
136 node["coverage-dependencies"] = std::move(deps);
137 }
138 if (m_chargeTransfer) {
139 if (m_beta != 0.5) {
140 node["beta"] = m_beta;
141 }
142 if (m_exchangeCurrentDensityFormulation) {
143 node["exchange-current-density-formulation"] = true;
144 }
145 }
146}
147
149 const UnitSystem& units)
150{
151 m_cov.clear();
152 m_ac.clear();
153 m_ec.clear();
154 m_mc.clear();
155 m_lindep.clear();
156 for (const auto& [species, coeffs] : dependencies) {
157 double a, m;
158 vector<double> E(5, 0.0);
159 if (coeffs.is<AnyMap>()) {
160 auto& cov_map = coeffs.as<AnyMap>();
161 a = cov_map["a"].asDouble();
162 m = cov_map["m"].asDouble();
163 if (cov_map["E"].isScalar()) {
164 m_lindep.push_back(true);
165 E[1] = units.convertActivationEnergy(cov_map["E"], "K");
166 } else {
167 m_lindep.push_back(false);
168 auto& E_temp = cov_map["E"].asVector<AnyValue>(1, 4);
169 for (size_t i = 0; i < E_temp.size(); i++) {
170 E[i+1] = units.convertActivationEnergy(E_temp[i], "K");
171 }
172 }
173 } else {
174 auto& cov_vec = coeffs.asVector<AnyValue>(3);
175 a = cov_vec[0].asDouble();
176 m = cov_vec[1].asDouble();
177 if (cov_vec[2].isScalar()) {
178 m_lindep.push_back(true);
179 E[1] = units.convertActivationEnergy(cov_vec[2], "K");
180 } else {
181 m_lindep.push_back(false);
182 auto& E_temp = cov_vec[2].asVector<AnyValue>(1, 4);
183 for (size_t i = 0; i < E_temp.size(); i++) {
184 E[i+1] = units.convertActivationEnergy(E_temp[i], "K");
185 }
186 }
187 }
188 addCoverageDependence(species, a, m, E);
189 }
190}
191
193{
194 for (size_t k = 0; k < m_cov.size(); k++) {
195 AnyMap dep;
196 dep["a"] = m_ac[k];
197 dep["m"] = m_mc[k];
198 if (m_lindep[k]) {
199 dep["E"].setQuantity(m_ec[k][1], "K", true);
200 } else {
201 vector<AnyValue> E_temp(4);
202 for (size_t i = 0; i < m_ec[k].size() - 1; i++) {
203 E_temp[i].setQuantity(m_ec[k][i+1], "K", true);
204 }
205 dep["E"] = E_temp;
206 }
207 dependencies[m_cov[k]] = std::move(dep);
208 }
209}
210
211void InterfaceRateBase::addCoverageDependence(const string& sp, double a, double m,
212 span<const double> e)
213{
214 if (std::find(m_cov.begin(), m_cov.end(), sp) == m_cov.end()) {
215 m_cov.push_back(sp);
216 m_ac.push_back(a);
217 m_ec.emplace_back(e.begin(), e.end());
218 m_mc.push_back(m);
219 m_indices.clear();
220 } else {
221 throw CanteraError("InterfaceRateBase::addCoverageDependence",
222 "Coverage for species '{}' is already specified.", sp);
223 }
224}
225
226void InterfaceRateBase::setSpecies(span<const string> species)
227{
228 m_indices.clear();
229 for (size_t k = 0; k < m_cov.size(); k++) {
230 auto it = find(species.begin(), species.end(), m_cov[k]);
231 if (it != species.end()) {
232 m_indices.emplace(k, it - species.begin());
233 } else {
234 throw CanteraError("InterfaceRateBase:setSpeciesIndices",
235 "Species list does not contain '{}'.", m_cov[k]);
236 }
237 }
238}
239
241 if (shared_data.ready) {
242 m_siteDensity = shared_data.density;
243 }
244
245 if (m_indices.size() != m_cov.size()) {
246 // object is not set up correctly (setSpecies needs to be run)
247 m_acov = NAN;
248 m_ecov = NAN;
249 m_mcov = NAN;
250 return;
251 }
252 m_acov = 0.0;
253 m_ecov = 0.0;
254 m_mcov = 0.0;
255 for (auto& [iCov, iKin] : m_indices) {
256 m_acov += m_ac[iCov] * shared_data.coverages[iKin];
257 if (m_lindep[iCov]) {
258 m_ecov += m_ec[iCov][1] * shared_data.coverages[iKin];
259 } else {
260 m_ecov += poly4(shared_data.coverages[iKin], m_ec[iCov]);
261 }
262 m_mcov += m_mc[iCov] * shared_data.logCoverages[iKin];
263 }
264
265 // Update change in electrical potential energy
266 if (m_chargeTransfer) {
268 for (const auto& [iPhase, netCharge] : m_netCharges) {
270 shared_data.electricPotentials[iPhase] * netCharge;
271 }
273 }
274
275 // Update quantities used for exchange current density formulation
276 if (m_exchangeCurrentDensityFormulation) {
277 m_deltaGibbs0_RT = 0.;
279 for (const auto& [k, stoich] : m_stoichCoeffs) {
281 shared_data.standardChemPotentials[k] * stoich;
282 if (stoich > 0.) {
284 shared_data.standardConcentrations[k];
285 }
286 }
288 }
289}
290
292{
294
296 if (!m_chargeTransfer) {
297 return;
298 }
299
300 m_stoichCoeffs.clear();
301 for (const auto& [name, stoich] : rxn.reactants) {
302 m_stoichCoeffs.emplace_back(kin.kineticsSpeciesIndex(name), -stoich);
303 }
304 for (const auto& [name, stoich] : rxn.products) {
305 m_stoichCoeffs.emplace_back(kin.kineticsSpeciesIndex(name), stoich);
306 }
307
308 m_netCharges.clear();
309 for (const auto& [k, stoich] : m_stoichCoeffs) {
310 size_t n = kin.speciesPhaseIndex(k);
311 size_t start = kin.kineticsSpeciesIndex(0, n);
312 double charge = kin.thermo(n).charge(k - start);
313 m_netCharges.emplace_back(n, Faraday * charge * stoich);
314 }
315}
316
317StickingCoverage::StickingCoverage()
318 : m_motzWise(false)
319 , m_explicitMotzWise(false)
320 , m_stickingSpecies("")
321 , m_explicitSpecies(false)
322 , m_surfaceOrder(NAN)
323 , m_multiplier(NAN)
324 , m_factor(NAN)
325{
326}
327
329{
330 m_motzWise = node.getBool("Motz-Wise", false);
331 m_explicitMotzWise = node.hasKey("Motz-Wise");
332 m_stickingSpecies = node.getString("sticking-species", "");
333 m_explicitSpecies = node.hasKey("sticking-species");
334}
335
337{
338 if (m_explicitMotzWise) {
339 node["Motz-Wise"] = m_motzWise;
340 }
341 if (m_explicitSpecies) {
342 node["sticking-species"] = m_stickingSpecies;
343 }
344}
345
347{
348 // Ensure that site density is initialized
349 const ThermoPhase& phase = kin.thermo(0);
350 const auto& surf = dynamic_cast<const SurfPhase&>(phase);
351 m_siteDensity = surf.siteDensity();
352 if (!m_explicitMotzWise) {
353 m_motzWise = kin.thermo().input().getBool("Motz-Wise", false);
354 }
355
356 string sticking_species = m_stickingSpecies;
357 if (sticking_species == "") {
358 // Identify the sticking species if not explicitly given
359 vector<string> gasSpecies;
360 vector<string> anySpecies;
361 for (const auto& [name, stoich] : rxn.reactants) {
362 size_t iPhase = kin.speciesPhaseIndex(kin.kineticsSpeciesIndex(name));
363 if (iPhase != 0) {
364 // Non-interface species. There should be exactly one of these
365 // (either in gas phase or other phase)
366 if (kin.thermo(iPhase).phaseOfMatter() == "gas") {
367 gasSpecies.push_back(name);
368 }
369 anySpecies.push_back(name);
370 }
371 }
372 if (gasSpecies.size() == 1) {
373 // single sticking species in gas phase
374 sticking_species = gasSpecies[0];
375 } else if (anySpecies.size() == 1) {
376 // single sticking species in any phase
377 sticking_species = anySpecies[0];
378 } else if (anySpecies.size() == 0) {
379 throw InputFileError("StickingCoverage::setContext",
380 rxn.input, "No non-interface species found "
381 "in sticking reaction: '{}'", rxn.equation());
382 } else {
383 throw InputFileError("StickingCoverage::setContext",
384 rxn.input, "Multiple non-interface species ({})\nfound in sticking "
385 "reaction: '{}'.\nSticking species must be explicitly specified.",
386 fmt::format("'{}'", fmt::join(anySpecies, "', '")), rxn.equation());
387 }
388 }
389 m_stickingSpecies = sticking_species;
390
391 double surface_order = 0.0;
392 double multiplier = 1.0;
393 // Adjust the A-factor
394 for (const auto& [name, stoich] : rxn.reactants) {
395 size_t iPhase = kin.speciesPhaseIndex(kin.kineticsSpeciesIndex(name));
396 const ThermoPhase& p = kin.thermo(iPhase);
397 size_t k = p.speciesIndex(name, true);
398 if (name == sticking_species) {
399 multiplier *= sqrt(GasConstant / (2 * Pi * p.molecularWeight(k)));
400 } else {
401 // Non-sticking species. Convert from coverages used in the
402 // sticking probability expression to the concentration units
403 // used in the mass action rate expression. For surface phases,
404 // the dependence on the site density is incorporated when the
405 // rate constant is evaluated, since we don't assume that the
406 // site density is known at this time.
407 double order = getValue(rxn.orders, name, stoich);
408 if (&p == &surf) {
409 multiplier *= pow(surf.size(k), order);
410 surface_order += order;
411 } else {
412 multiplier *= pow(p.standardConcentration(k), -order);
413 }
414 }
415 }
416 m_surfaceOrder = surface_order;
417 m_multiplier = multiplier;
418}
419
420}
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:431
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition AnyMap.cpp:1477
const UnitSystem & units() const
Return the default units that should be used to convert stored values.
Definition AnyMap.h:640
bool getBool(const string &key, bool default_) const
If key exists, return it as a bool, otherwise return default_.
Definition AnyMap.cpp:1575
const string & getString(const string &key, const string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition AnyMap.cpp:1590
A wrapper for a variable whose type is determined at runtime.
Definition AnyMap.h:88
double & asDouble()
Return the held value as a double, if it is a double or a long int.
Definition AnyMap.cpp:867
Base class for exceptions thrown by Cantera classes.
Error thrown for problems processing information contained in an AnyMap or AnyValue.
Definition AnyMap.h:749
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 setSpecies(span< const string > species)
Set association with an ordered list of all species associated with a given Kinetics object.
void setParameters(const AnyMap &node)
Perform object setup based on AnyMap node information.
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.
void getCoverageDependencies(AnyMap &dependencies) const
Store parameters needed to reconstruct coverage dependencies.
bool m_chargeTransfer
Boolean indicating use of electrochemistry.
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)
virtual void addCoverageDependence(const string &sp, double a, double m, span< const double > e)
Add a coverage dependency for species sp, with exponential dependence a, power-law exponent m,...
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
ThermoPhase & thermo(size_t n=0)
This method returns a reference to the nth ThermoPhase object defined in this kinetics mechanism.
Definition Kinetics.h:239
size_t nPhases() const
The number of phases participating in the reaction mechanism.
Definition Kinetics.h:189
size_t kineticsSpeciesIndex(size_t k, size_t n) const
The location of species k of phase n in species arrays.
Definition Kinetics.h:273
size_t nTotalSpecies() const
The total number of species in all phases participating in the kinetics mechanism.
Definition Kinetics.h:251
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:354
An error indicating that an unimplemented function has been called.
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:246
size_t speciesIndex(const string &name, bool raise=true) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:127
double temperature() const
Temperature (K).
Definition Phase.h:585
int stateMFNumber() const
Return the State Mole Fraction Number.
Definition Phase.h:794
const vector< string > & speciesNames() const
Return a const reference to the vector of species names.
Definition Phase.cpp:151
double molecularWeight(size_t k) const
Molecular weight of species k.
Definition Phase.cpp:388
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:561
Abstract base class which stores data about a reaction and its rate parameterization so that it can b...
Definition Reaction.h:25
Composition orders
Forward reaction order with respect to specific species.
Definition Reaction.h:119
string equation() const
The chemical equation for this reaction.
Definition Reaction.cpp:380
bool usesElectrochemistry(const Kinetics &kin) const
Check whether reaction uses electrochemistry.
Definition Reaction.cpp:740
Composition products
Product species and stoichiometric coefficients.
Definition Reaction.h:114
Composition reactants
Reactant species and stoichiometric coefficients.
Definition Reaction.h:111
AnyMap input
Input data used for specific models.
Definition Reaction.h:139
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:114
double siteDensity() const
Returns the site density.
Definition SurfPhase.h:232
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:674
auto poly4(D x, const C &c)
Evaluates a polynomial of order 4.
Definition utilities.h:179
const double Faraday
Faraday constant [C/kmol].
Definition ct_defs.h:134
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:123
const double Pi
Pi.
Definition ct_defs.h:71
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition global.h:263
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const double Tiny
Small number to compare differences of mole fractions against.
Definition ct_defs.h:176
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:223
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
void resize(Kinetics &kin) override
Update array sizes that depend on number of species, reactions and phases.
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...