1 /**
2 * @file GasKinetics.cpp Homogeneous kinetics in ideal gases
3 */
4
5 // This file is part of Cantera. See License.txt in the top-level directory or
6 // at https://cantera.org/license.txt for license and copyright information.
7
8 #include "cantera/kinetics/GasKinetics.h"
9 #include "cantera/thermo/ThermoPhase.h"
10
11 using namespace std;
12
13 namespace Cantera
14 {
GasKinetics(ThermoPhase * thermo)15 GasKinetics::GasKinetics(ThermoPhase* thermo) :
16 BulkKinetics(thermo),
17 m_logp_ref(0.0),
18 m_logc_ref(0.0),
19 m_logStandConc(0.0),
20 m_pres(0.0)
21 {
22 }
23
update_rates_T()24 void GasKinetics::update_rates_T()
25 {
26 double T = thermo().temperature();
27 double P = thermo().pressure();
28 m_logStandConc = log(thermo().standardConcentration());
29 double logT = log(T);
30
31 if (T != m_temp) {
32 if (!m_rfn.empty()) {
33 m_rates.update(T, logT, m_rfn.data());
34 }
35
36 if (!m_rfn_low.empty()) {
37 m_falloff_low_rates.update(T, logT, m_rfn_low.data());
38 m_falloff_high_rates.update(T, logT, m_rfn_high.data());
39 }
40 if (!falloff_work.empty()) {
41 m_falloffn.updateTemp(T, falloff_work.data());
42 }
43 updateKc();
44 m_ROP_ok = false;
45 if (m_blowersmasel_rates.nReactions()) {
46 thermo().getPartialMolarEnthalpies(m_grt.data());
47 getReactionDelta(m_grt.data(), m_dH.data());
48 m_blowersmasel_rates.updateBlowersMasel(T, logT, m_rfn.data(), m_dH.data());
49 }
50 }
51
52 if (T != m_temp || P != m_pres) {
53
54 // loop over MultiBulkRates evaluators
55 for (auto& rates : m_bulk_rates) {
56 rates->update(thermo(), m_concm.data());
57 rates->getRateConstants(thermo(), m_rfn.data(), m_concm.data());
58 }
59
60 if (m_plog_rates.nReactions()) {
61 m_plog_rates.update(T, logT, m_rfn.data());
62 m_ROP_ok = false;
63 }
64
65 if (m_cheb_rates.nReactions()) {
66 m_cheb_rates.update(T, logT, m_rfn.data());
67 m_ROP_ok = false;
68 }
69 }
70 m_pres = P;
71 m_temp = T;
72 }
73
update_rates_C()74 void GasKinetics::update_rates_C()
75 {
76 thermo().getActivityConcentrations(m_act_conc.data());
77 thermo().getConcentrations(m_phys_conc.data());
78 doublereal ctot = thermo().molarDensity();
79
80 // 3-body reactions
81 if (!concm_3b_values.empty()) {
82 m_3b_concm.update(m_phys_conc, ctot, concm_3b_values.data());
83 }
84
85 // Falloff reactions
86 if (!concm_falloff_values.empty()) {
87 m_falloff_concm.update(m_phys_conc, ctot, concm_falloff_values.data());
88 }
89
90 // Third-body objects interacting with MultiRate evaluator
91 if (!concm_multi_values.empty()) {
92 // using pre-existing third-body handlers requires copying;
93 m_multi_concm.update(m_phys_conc, ctot, concm_multi_values.data());
94 for (size_t i = 0; i < m_multi_indices.size(); i++) {
95 m_concm[m_multi_indices[i]] = concm_multi_values[i];
96 }
97 }
98
99 // P-log reactions
100 if (m_plog_rates.nReactions()) {
101 double logP = log(thermo().pressure());
102 m_plog_rates.update_C(&logP);
103 }
104
105 // Chebyshev reactions
106 if (m_cheb_rates.nReactions()) {
107 double log10P = log10(thermo().pressure());
108 m_cheb_rates.update_C(&log10P);
109 }
110
111 m_ROP_ok = false;
112 }
113
updateKc()114 void GasKinetics::updateKc()
115 {
116 thermo().getStandardChemPotentials(m_grt.data());
117 fill(m_rkcn.begin(), m_rkcn.end(), 0.0);
118
119 // compute Delta G^0 for all reversible reactions
120 getRevReactionDelta(m_grt.data(), m_rkcn.data());
121
122 doublereal rrt = 1.0 / thermo().RT();
123 for (size_t i = 0; i < m_revindex.size(); i++) {
124 size_t irxn = m_revindex[i];
125 m_rkcn[irxn] = std::min(exp(m_rkcn[irxn]*rrt - m_dn[irxn]*m_logStandConc),
126 BigNumber);
127 }
128
129 for (size_t i = 0; i != m_irrev.size(); ++i) {
130 m_rkcn[ m_irrev[i] ] = 0.0;
131 }
132 }
133
getEquilibriumConstants(doublereal * kc)134 void GasKinetics::getEquilibriumConstants(doublereal* kc)
135 {
136 update_rates_T();
137 thermo().getStandardChemPotentials(m_grt.data());
138 fill(m_rkcn.begin(), m_rkcn.end(), 0.0);
139
140 // compute Delta G^0 for all reactions
141 getReactionDelta(m_grt.data(), m_rkcn.data());
142
143 doublereal rrt = 1.0 / thermo().RT();
144 for (size_t i = 0; i < nReactions(); i++) {
145 kc[i] = exp(-m_rkcn[i]*rrt + m_dn[i]*m_logStandConc);
146 }
147
148 // force an update of T-dependent properties, so that m_rkcn will
149 // be updated before it is used next.
150 m_temp = 0.0;
151 }
152
processFalloffReactions()153 void GasKinetics::processFalloffReactions()
154 {
155 // use m_ropr for temporary storage of reduced pressure
156 vector_fp& pr = m_ropr;
157
158 for (size_t i = 0; i < m_falloff_low_rates.nReactions(); i++) {
159 pr[i] = concm_falloff_values[i] * m_rfn_low[i] / (m_rfn_high[i] + SmallNumber);
160 AssertFinite(pr[i], "GasKinetics::processFalloffReactions",
161 "pr[{}] is not finite.", i);
162 }
163
164 m_falloffn.pr_to_falloff(pr.data(), falloff_work.data());
165
166 for (size_t i = 0; i < m_falloff_low_rates.nReactions(); i++) {
167 if (reactionTypeStr(m_fallindx[i]) == "falloff") {
168 pr[i] *= m_rfn_high[i];
169 } else { // CHEMACT_RXN
170 pr[i] *= m_rfn_low[i];
171 }
172 m_ropf[m_fallindx[i]] = pr[i];
173 }
174 }
175
updateROP()176 void GasKinetics::updateROP()
177 {
178 update_rates_C();
179 update_rates_T();
180 if (m_ROP_ok) {
181 return;
182 }
183
184 // copy rate coefficients into ropf
185 m_ropf = m_rfn;
186
187 // multiply ropf by enhanced 3b conc for all 3b rxns
188 if (!concm_3b_values.empty()) {
189 m_3b_concm.multiply(m_ropf.data(), concm_3b_values.data());
190 }
191
192 if (m_falloff_high_rates.nReactions()) {
193 processFalloffReactions();
194 }
195
196 // reactions involving third body
197 for (auto& index : m_multi_indices) {
198 m_ropf[index] *= m_concm[index];
199 }
200
201 for (size_t i = 0; i < nReactions(); i++) {
202 // Scale the forward rate coefficient by the perturbation factor
203 m_ropf[i] *= m_perturb[i];
204 // For reverse rates computed from thermochemistry, multiply the forward
205 // rate coefficients by the reciprocals of the equilibrium constants
206 m_ropr[i] = m_ropf[i] * m_rkcn[i];
207 }
208
209 // multiply ropf by concentration products
210 m_reactantStoich.multiply(m_act_conc.data(), m_ropf.data());
211
212 // for reversible reactions, multiply ropr by concentration products
213 m_revProductStoich.multiply(m_act_conc.data(), m_ropr.data());
214
215 for (size_t j = 0; j != nReactions(); ++j) {
216 m_ropnet[j] = m_ropf[j] - m_ropr[j];
217 }
218
219 for (size_t i = 0; i < m_rfn.size(); i++) {
220 AssertFinite(m_rfn[i], "GasKinetics::updateROP",
221 "m_rfn[{}] is not finite.", i);
222 AssertFinite(m_ropf[i], "GasKinetics::updateROP",
223 "m_ropf[{}] is not finite.", i);
224 AssertFinite(m_ropr[i], "GasKinetics::updateROP",
225 "m_ropr[{}] is not finite.", i);
226 }
227 m_ROP_ok = true;
228 }
229
getFwdRateConstants(double * kfwd)230 void GasKinetics::getFwdRateConstants(double* kfwd)
231 {
232 update_rates_C();
233 update_rates_T();
234
235 // copy rate coefficients into ropf
236 m_ropf = m_rfn;
237
238 if (legacy_rate_constants_used()) {
239 warn_deprecated("GasKinetics::getFwdRateConstants",
240 "Behavior to change after Cantera 2.6;\nresults will no longer include "
241 "third-body concentrations for three-body reactions.\nTo switch to new "
242 "behavior, use 'cantera.use_legacy_rate_constants(False)' (Python),\n"
243 "'useLegacyRateConstants(0)' (MATLAB), 'Cantera::use_legacy_rate_constants"
244 "(false)' (C++),\nor 'ct_use_legacy_rate_constants(0)' (clib).");
245
246 // multiply ropf by enhanced 3b conc for all 3b rxns
247 if (!concm_3b_values.empty()) {
248 m_3b_concm.multiply(m_ropf.data(), concm_3b_values.data());
249 }
250
251 // reactions involving third body
252 for (auto& index : m_multi_indices) {
253 m_ropf[index] *= m_concm[index];
254 }
255 }
256
257 if (m_falloff_high_rates.nReactions()) {
258 processFalloffReactions();
259 }
260
261 for (size_t i = 0; i < nReactions(); i++) {
262 // multiply by perturbation factor
263 kfwd[i] = m_ropf[i] * m_perturb[i];
264 }
265 }
266
addReaction(shared_ptr<Reaction> r,bool resize)267 bool GasKinetics::addReaction(shared_ptr<Reaction> r, bool resize)
268 {
269 // operations common to all reaction types
270 bool added = BulkKinetics::addReaction(r, resize);
271 if (!added) {
272 return false;
273 } else if (!(r->usesLegacy())) {
274 // Rate object already added in BulkKinetics::addReaction
275 return true;
276 }
277
278 if (r->type() == "elementary-legacy") {
279 addElementaryReaction(dynamic_cast<ElementaryReaction2&>(*r));
280 } else if (r->type() == "three-body-legacy") {
281 addThreeBodyReaction(dynamic_cast<ThreeBodyReaction2&>(*r));
282 } else if (r->type() == "falloff") {
283 addFalloffReaction(dynamic_cast<FalloffReaction&>(*r));
284 } else if (r->type() == "chemically-activated") {
285 addFalloffReaction(dynamic_cast<FalloffReaction&>(*r));
286 } else if (r->type() == "pressure-dependent-Arrhenius-legacy") {
287 addPlogReaction(dynamic_cast<PlogReaction2&>(*r));
288 } else if (r->type() == "Chebyshev-legacy") {
289 addChebyshevReaction(dynamic_cast<ChebyshevReaction2&>(*r));
290 } else if (r->type() == "Blowers-Masel") {
291 addBlowersMaselReaction(dynamic_cast<BlowersMaselReaction&>(*r));
292 } else {
293 throw CanteraError("GasKinetics::addReaction",
294 "Unknown reaction type specified: '{}'", r->type());
295 }
296 return true;
297 }
298
addFalloffReaction(FalloffReaction & r)299 void GasKinetics::addFalloffReaction(FalloffReaction& r)
300 {
301 // install high and low rate coeff calculators and extend the high and low
302 // rate coeff value vectors
303 size_t nfall = m_falloff_high_rates.nReactions();
304 m_falloff_high_rates.install(nfall, r.high_rate);
305 m_rfn_high.push_back(0.0);
306 m_falloff_low_rates.install(nfall, r.low_rate);
307 m_rfn_low.push_back(0.0);
308
309 // add this reaction number to the list of falloff reactions
310 m_fallindx.push_back(nReactions()-1);
311 m_rfallindx[nReactions()-1] = nfall;
312
313 // install the enhanced third-body concentration calculator
314 map<size_t, double> efficiencies;
315 for (const auto& eff : r.third_body.efficiencies) {
316 size_t k = kineticsSpeciesIndex(eff.first);
317 if (k != npos) {
318 efficiencies[k] = eff.second;
319 }
320 }
321 m_falloff_concm.install(nfall, efficiencies,
322 r.third_body.default_efficiency);
323 concm_falloff_values.resize(m_falloff_concm.workSize());
324
325 // install the falloff function calculator for this reaction
326 m_falloffn.install(nfall, r.type(), r.falloff);
327 falloff_work.resize(m_falloffn.workSize());
328 }
329
addThreeBodyReaction(ThreeBodyReaction2 & r)330 void GasKinetics::addThreeBodyReaction(ThreeBodyReaction2& r)
331 {
332 m_rates.install(nReactions()-1, r.rate);
333 map<size_t, double> efficiencies;
334 for (const auto& eff : r.third_body.efficiencies) {
335 size_t k = kineticsSpeciesIndex(eff.first);
336 if (k != npos) {
337 efficiencies[k] = eff.second;
338 }
339 }
340 m_3b_concm.install(nReactions()-1, efficiencies,
341 r.third_body.default_efficiency);
342 concm_3b_values.resize(m_3b_concm.workSize());
343 }
344
addPlogReaction(PlogReaction2 & r)345 void GasKinetics::addPlogReaction(PlogReaction2& r)
346 {
347 m_plog_rates.install(nReactions()-1, r.rate);
348 }
349
addChebyshevReaction(ChebyshevReaction2 & r)350 void GasKinetics::addChebyshevReaction(ChebyshevReaction2& r)
351 {
352 m_cheb_rates.install(nReactions()-1, r.rate);
353 }
354
addBlowersMaselReaction(BlowersMaselReaction & r)355 void GasKinetics::addBlowersMaselReaction(BlowersMaselReaction& r)
356 {
357 m_blowersmasel_rates.install(nReactions()-1, r.rate);
358 }
359
modifyReaction(size_t i,shared_ptr<Reaction> rNew)360 void GasKinetics::modifyReaction(size_t i, shared_ptr<Reaction> rNew)
361 {
362 // operations common to all bulk reaction types
363 BulkKinetics::modifyReaction(i, rNew);
364
365 if (!(rNew->usesLegacy())) {
366 // Rate object already modified in BulkKinetics::modifyReaction
367 return;
368 }
369
370 if (rNew->type() == "elementary-legacy") {
371 modifyElementaryReaction(i, dynamic_cast<ElementaryReaction2&>(*rNew));
372 } else if (rNew->type() == "three-body-legacy") {
373 modifyThreeBodyReaction(i, dynamic_cast<ThreeBodyReaction2&>(*rNew));
374 } else if (rNew->type() == "falloff") {
375 modifyFalloffReaction(i, dynamic_cast<FalloffReaction&>(*rNew));
376 } else if (rNew->type() == "chemically-activated") {
377 modifyFalloffReaction(i, dynamic_cast<FalloffReaction&>(*rNew));
378 } else if (rNew->type() == "pressure-dependent-Arrhenius-legacy") {
379 modifyPlogReaction(i, dynamic_cast<PlogReaction2&>(*rNew));
380 } else if (rNew->type() == "Chebyshev-legacy") {
381 modifyChebyshevReaction(i, dynamic_cast<ChebyshevReaction2&>(*rNew));
382 } else if (rNew->type() == "Blowers-Masel") {
383 modifyBlowersMaselReaction(i, dynamic_cast<BlowersMaselReaction&>(*rNew));
384 } else {
385 throw CanteraError("GasKinetics::modifyReaction",
386 "Unknown reaction type specified: '{}'", rNew->type());
387 }
388
389 // invalidate all cached data
390 m_ROP_ok = false;
391 m_temp += 0.1234;
392 m_pres += 0.1234;
393 }
394
modifyThreeBodyReaction(size_t i,ThreeBodyReaction2 & r)395 void GasKinetics::modifyThreeBodyReaction(size_t i, ThreeBodyReaction2& r)
396 {
397 m_rates.replace(i, r.rate);
398 }
399
modifyFalloffReaction(size_t i,FalloffReaction & r)400 void GasKinetics::modifyFalloffReaction(size_t i, FalloffReaction& r)
401 {
402 size_t iFall = m_rfallindx[i];
403 m_falloff_high_rates.replace(iFall, r.high_rate);
404 m_falloff_low_rates.replace(iFall, r.low_rate);
405 m_falloffn.replace(iFall, r.falloff);
406 }
407
modifyPlogReaction(size_t i,PlogReaction2 & r)408 void GasKinetics::modifyPlogReaction(size_t i, PlogReaction2& r)
409 {
410 m_plog_rates.replace(i, r.rate);
411 }
412
modifyChebyshevReaction(size_t i,ChebyshevReaction2 & r)413 void GasKinetics::modifyChebyshevReaction(size_t i, ChebyshevReaction2& r)
414 {
415 m_cheb_rates.replace(i, r.rate);
416 }
417
modifyBlowersMaselReaction(size_t i,BlowersMaselReaction & r)418 void GasKinetics::modifyBlowersMaselReaction(size_t i, BlowersMaselReaction& r)
419 {
420 m_blowersmasel_rates.replace(i, r.rate);
421 }
422
init()423 void GasKinetics::init()
424 {
425 BulkKinetics::init();
426 m_logp_ref = log(thermo().refPressure()) - log(GasConstant);
427 }
428
invalidateCache()429 void GasKinetics::invalidateCache()
430 {
431 BulkKinetics::invalidateCache();
432 m_pres += 0.13579;
433 }
434
435 }
436