Cantera  2.0
AqueousKinetics.cpp
Go to the documentation of this file.
1 /**
2  * @file AqueousKinetics.cpp
3  *
4  * Homogeneous kinetics in an aqueous phase, either condensed
5  * or dilute in salts
6  *
7  */
8 /*
9  * Copyright (2006) Sandia Corporation. Under the terms of
10  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
11  * U.S. Government retains certain rights in this software.
12  */
13 
18 
19 #include <iostream>
20 using namespace std;
21 
22 namespace Cantera
23 {
24 //====================================================================================================================
25 /**
26  * Construct an empty reaction mechanism.
27  */
28 AqueousKinetics::AqueousKinetics(thermo_t* thermo) :
29  Kinetics(),
30  m_nfall(0),
31  m_nirrev(0),
32  m_nrev(0),
33  m_ROP_ok(false),
34  m_temp(0.0),
35  m_finalized(false)
36 {
37  if (thermo != 0) {
38  addPhase(*thermo);
39  }
40 }
41 //====================================================================================================================
43  Kinetics(),
44  m_nfall(0),
45  m_nirrev(0),
46  m_nrev(0),
47  m_ROP_ok(false),
48  m_temp(0.0),
49  m_finalized(false)
50 {
51  *this = right;
52 }
53 //====================================================================================================================
55 {
56 }
57 //====================================================================================================================
58 AqueousKinetics& AqueousKinetics::operator=(const AqueousKinetics& right)
59 {
60  if (this == &right) {
61  return *this;
62  }
63 
64  Kinetics::operator=(right);
65 
66  m_nfall = right.m_nfall;
67  m_rates = right.m_rates;
68  m_index = right.m_index;
69  m_irrev = right.m_irrev;
70 
71  m_rxnstoich = right.m_rxnstoich;
72 
73  m_fwdOrder = right.m_fwdOrder;
74  m_nirrev = right.m_nirrev;
75  m_nrev = right.m_nrev;
76  m_rgroups = right.m_rgroups;
77  m_pgroups = right.m_pgroups;
78  m_rxntype = right.m_rxntype;
79  m_rrxn = right.m_rrxn;
80  m_prxn = right.m_prxn;
81  m_dn = right.m_dn;
82  m_revindex = right.m_revindex;
83  m_rxneqn = right.m_rxneqn;
84 
85  m_ropf = right.m_ropf;
86  m_ropr = right.m_ropr;
87  m_ropnet = right.m_ropnet;
88  m_ROP_ok = right.m_ROP_ok;
89  m_temp = right.m_temp;
90  m_rfn = right.m_rfn;
91  m_rkcn = right.m_rkcn;
92 
93  m_conc = right.m_conc;
94  m_grt = right.m_grt;
95  m_finalized = right.m_finalized;
96 
97  throw CanteraError("GasKinetics::operator=()",
98  "Unfinished implementation");
99 
100  return *this;
101 
102 }
103 //====================================================================================================================
104 Kinetics* AqueousKinetics::duplMyselfAsKinetics(const std::vector<thermo_t*> & tpVector) const
105 {
106  AqueousKinetics* gK = new AqueousKinetics(*this);
107  gK->assignShallowPointers(tpVector);
108  return dynamic_cast<Kinetics*>(gK);
109 }
110 
111 //====================================================================================================================
112 /**
113  * Update temperature-dependent portions of reaction rates and
114  * falloff functions.
115  */
118 
119 void AqueousKinetics::
120 update_C() {}
121 
122 void AqueousKinetics::_update_rates_T()
123 {
124  doublereal T = thermo().temperature();
125  doublereal logT = log(T);
126  m_rates.update(T, logT, &m_rfn[0]);
127 
128  m_temp = T;
129  updateKc();
130  m_ROP_ok = false;
131 };
132 
133 
134 /**
135  * Update properties that depend on concentrations. Currently only
136  * the enhanced collision partner concentrations are updated here.
137  */
140 {
141  thermo().getActivityConcentrations(&m_conc[0]);
142 
143  m_ROP_ok = false;
144 }
145 
146 /**
147  * Update the equilibrium constants in molar units.
148  */
150 {
151  doublereal rt = GasConstant * m_temp;
152 
153  thermo().getStandardChemPotentials(&m_grt[0]);
154  fill(m_rkcn.begin(), m_rkcn.end(), 0.0);
155  for (size_t k = 0; k < thermo().nSpecies(); k++) {
156  doublereal logStandConc_k = thermo().logStandardConc(k);
157  m_grt[k] -= rt * logStandConc_k;
158  }
159 
160  // compute Delta G^0 for all reversible reactions
161  m_rxnstoich.getRevReactionDelta(m_ii, &m_grt[0], &m_rkcn[0]);
162 
163  //doublereal logStandConc = m_kdata->m_logStandConc;
164  doublereal rrt = 1.0/(GasConstant * thermo().temperature());
165  for (size_t i = 0; i < m_nrev; i++) {
166  size_t irxn = m_revindex[i];
167  m_rkcn[irxn] = exp(m_rkcn[irxn]*rrt);
168  }
169 
170  for (size_t i = 0; i != m_nirrev; ++i) {
171  m_rkcn[ m_irrev[i] ] = 0.0;
172  }
173 }
174 
175 /**
176  * Get the equilibrium constants of all reactions, whether
177  * reversible or not.
178  */
180 {
181  _update_rates_T();
182 
183  thermo().getStandardChemPotentials(&m_grt[0]);
184  fill(m_rkcn.begin(), m_rkcn.end(), 0.0);
185  doublereal rt = GasConstant * m_temp;
186  for (size_t k = 0; k < thermo().nSpecies(); k++) {
187  doublereal logStandConc_k = thermo().logStandardConc(k);
188  m_grt[k] -= rt * logStandConc_k;
189  }
190 
191  // compute Delta G^0 for all reactions
192  m_rxnstoich.getReactionDelta(m_ii, &m_grt[0], &m_rkcn[0]);
193 
194  doublereal rrt = 1.0/(GasConstant * thermo().temperature());
195  for (size_t i = 0; i < m_ii; i++) {
196  kc[i] = exp(-m_rkcn[i]*rrt);
197  }
198 
199  // force an update of T-dependent properties, so that m_rkcn will
200  // be updated before it is used next.
201  m_temp = 0.0;
202 }
203 
204 /**
205  *
206  * getDeltaGibbs():
207  *
208  * Return the vector of values for the reaction gibbs free energy
209  * change
210  * These values depend upon the concentration
211  * of the ideal gas.
212  *
213  * units = J kmol-1
214  */
215 void AqueousKinetics::getDeltaGibbs(doublereal* deltaG)
216 {
217  /*
218  * Get the chemical potentials of the species in the
219  * ideal gas solution.
220  */
221  thermo().getChemPotentials(&m_grt[0]);
222  /*
223  * Use the stoichiometric manager to find deltaG for each
224  * reaction.
225  */
226  m_rxnstoich.getReactionDelta(m_ii, &m_grt[0], deltaG);
227 }
228 
229 /**
230  *
231  * getDeltaEnthalpy():
232  *
233  * Return the vector of values for the reactions change in
234  * enthalpy.
235  * These values depend upon the concentration
236  * of the solution.
237  *
238  * units = J kmol-1
239  */
240 void AqueousKinetics::getDeltaEnthalpy(doublereal* deltaH)
241 {
242  /*
243  * Get the partial molar enthalpy of all species in the
244  * ideal gas.
245  */
246  thermo().getPartialMolarEnthalpies(&m_grt[0]);
247  /*
248  * Use the stoichiometric manager to find deltaG for each
249  * reaction.
250  */
251  m_rxnstoich.getReactionDelta(m_ii, &m_grt[0], deltaH);
252 }
253 
254 /*
255  *
256  * getDeltaEntropy():
257  *
258  * Return the vector of values for the reactions change in
259  * entropy.
260  * These values depend upon the concentration
261  * of the solution.
262  *
263  * units = J kmol-1 Kelvin-1
264  */
265 void AqueousKinetics::getDeltaEntropy(doublereal* deltaS)
266 {
267  /*
268  * Get the partial molar entropy of all species in the
269  * solid solution.
270  */
271  thermo().getPartialMolarEntropies(&m_grt[0]);
272  /*
273  * Use the stoichiometric manager to find deltaS for each
274  * reaction.
275  */
276  m_rxnstoich.getReactionDelta(m_ii, &m_grt[0], deltaS);
277 }
278 
279 /**
280  *
281  * getDeltaSSGibbs():
282  *
283  * Return the vector of values for the reaction
284  * standard state gibbs free energy change.
285  * These values don't depend upon the concentration
286  * of the solution.
287  *
288  * units = J kmol-1
289  */
290 void AqueousKinetics::getDeltaSSGibbs(doublereal* deltaG)
291 {
292  /*
293  * Get the standard state chemical potentials of the species.
294  * This is the array of chemical potentials at unit activity
295  * We define these here as the chemical potentials of the pure
296  * species at the temperature and pressure of the solution.
297  */
298  thermo().getStandardChemPotentials(&m_grt[0]);
299  /*
300  * Use the stoichiometric manager to find deltaG for each
301  * reaction.
302  */
303  m_rxnstoich.getReactionDelta(m_ii, &m_grt[0], deltaG);
304 }
305 
306 /**
307  *
308  * getDeltaSSEnthalpy():
309  *
310  * Return the vector of values for the change in the
311  * standard state enthalpies of reaction.
312  * These values don't depend upon the concentration
313  * of the solution.
314  *
315  * units = J kmol-1
316  */
317 void AqueousKinetics::getDeltaSSEnthalpy(doublereal* deltaH)
318 {
319  /*
320  * Get the standard state enthalpies of the species.
321  * This is the array of chemical potentials at unit activity
322  * We define these here as the enthalpies of the pure
323  * species at the temperature and pressure of the solution.
324  */
325  thermo().getEnthalpy_RT(&m_grt[0]);
326  doublereal RT = thermo().temperature() * GasConstant;
327  for (size_t k = 0; k < m_kk; k++) {
328  m_grt[k] *= RT;
329  }
330  /*
331  * Use the stoichiometric manager to find deltaG for each
332  * reaction.
333  */
334  m_rxnstoich.getReactionDelta(m_ii, &m_grt[0], deltaH);
335 }
336 
337 /*
338  *
339  * getDeltaSSEntropy():
340  *
341  * Return the vector of values for the change in the
342  * standard state entropies for each reaction.
343  * These values don't depend upon the concentration
344  * of the solution.
345  *
346  * units = J kmol-1 Kelvin-1
347  */
348 void AqueousKinetics::getDeltaSSEntropy(doublereal* deltaS)
349 {
350  /*
351  * Get the standard state entropy of the species.
352  * We define these here as the entropies of the pure
353  * species at the temperature and pressure of the solution.
354  */
355  thermo().getEntropy_R(&m_grt[0]);
356  doublereal R = GasConstant;
357  for (size_t k = 0; k < m_kk; k++) {
358  m_grt[k] *= R;
359  }
360  /*
361  * Use the stoichiometric manager to find deltaS for each
362  * reaction.
363  */
364  m_rxnstoich.getReactionDelta(m_ii, &m_grt[0], deltaS);
365 }
366 
367 
368 
369 void AqueousKinetics::updateROP()
370 {
371  _update_rates_T();
372  _update_rates_C();
373 
374  if (m_ROP_ok) {
375  return;
376  }
377 
378  // copy rate coefficients into ropf
379  copy(m_rfn.begin(), m_rfn.end(), m_ropf.begin());
380 
381  // multiply by perturbation factor
382  multiply_each(m_ropf.begin(), m_ropf.end(), m_perturb.begin());
383 
384  // copy the forward rates to the reverse rates
385  copy(m_ropf.begin(), m_ropf.end(), m_ropr.begin());
386 
387  // for reverse rates computed from thermochemistry, multiply
388  // the forward rates copied into m_ropr by the reciprocals of
389  // the equilibrium constants
390  multiply_each(m_ropr.begin(), m_ropr.end(), m_rkcn.begin());
391 
392  // multiply ropf by concentration products
393  m_rxnstoich.multiplyReactants(&m_conc[0], &m_ropf[0]);
394  //m_reactantStoich.multiply(m_conc.begin(), ropf.begin());
395 
396  // for reversible reactions, multiply ropr by concentration
397  // products
398  m_rxnstoich.multiplyRevProducts(&m_conc[0], &m_ropr[0]);
399  //m_revProductStoich.multiply(m_conc.begin(), ropr.begin());
400 
401  for (size_t j = 0; j != m_ii; ++j) {
402  m_ropnet[j] = m_ropf[j] - m_ropr[j];
403  }
404 
405  m_ROP_ok = true;
406 }
407 
408 /**
409  *
410  * getFwdRateConstants():
411  *
412  * Update the rate of progress for the reactions.
413  * This key routine makes sure that the rate of progress vectors
414  * located in the solid kinetics data class are up to date.
415  */
417 getFwdRateConstants(doublereal* kfwd)
418 {
419  _update_rates_T();
420  _update_rates_C();
421 
422  // copy rate coefficients into ropf
423  copy(m_rfn.begin(), m_rfn.end(), m_ropf.begin());
424 
425  // multiply by perturbation factor
426  multiply_each(m_ropf.begin(), m_ropf.end(), m_perturb.begin());
427 
428  for (size_t i = 0; i < m_ii; i++) {
429  kfwd[i] = m_ropf[i];
430  }
431 }
432 
433 /**
434  *
435  * getRevRateConstants():
436  *
437  * Return a vector of the reverse reaction rate constants
438  *
439  * Length is the number of reactions. units depends
440  * on many issues. Note, this routine will return rate constants
441  * for irreversible reactions if the default for
442  * doIrreversible is overridden.
443  */
445 getRevRateConstants(doublereal* krev, bool doIrreversible)
446 {
447  /*
448  * go get the forward rate constants. -> note, we don't
449  * really care about speed or redundancy in these
450  * informational routines.
451  */
452  getFwdRateConstants(krev);
453 
454  if (doIrreversible) {
455  getEquilibriumConstants(&m_ropnet[0]);
456  for (size_t i = 0; i < m_ii; i++) {
457  krev[i] /= m_ropnet[i];
458  }
459  } else {
460  /*
461  * m_rkcn[] is zero for irreversible reactions
462  */
463  for (size_t i = 0; i < m_ii; i++) {
464  krev[i] *= m_rkcn[i];
465  }
466  }
467 }
468 
469 void AqueousKinetics::addReaction(ReactionData& r)
470 {
471 
472  if (r.reactionType == ELEMENTARY_RXN) {
473  addElementaryReaction(r);
474  }
475 
476  // operations common to all reaction types
477  installReagents(r);
478  installGroups(reactionNumber(), r.rgroups, r.pgroups);
480  m_rxneqn.push_back(r.equation);
481 }
482 
483 
484 
485 
486 void AqueousKinetics::addElementaryReaction(ReactionData& r)
487 {
488  size_t iloc;
489 
490  // install rate coeff calculator
491  iloc = m_rates.install(reactionNumber(), r);
492 
493  // add constant term to rate coeff value vector
494  m_rfn.push_back(r.rateCoeffParameters[0]);
495 
496  // forward rxn order equals number of reactants
497  m_fwdOrder.push_back(r.reactants.size());
498  registerReaction(reactionNumber(), ELEMENTARY_RXN, iloc);
499 }
500 
501 
502 
503 
504 void AqueousKinetics::installReagents(const ReactionData& r)
505 {
506 
507  m_ropf.push_back(0.0); // extend by one for new rxn
508  m_ropr.push_back(0.0);
509  m_ropnet.push_back(0.0);
510  size_t n, ns, m;
511  doublereal nsFlt;
512  doublereal reactantGlobalOrder = 0.0;
513  doublereal productGlobalOrder = 0.0;
514  size_t rnum = reactionNumber();
515 
516  std::vector<size_t> rk;
517  size_t nr = r.reactants.size();
518  for (n = 0; n < nr; n++) {
519  nsFlt = r.rstoich[n];
520  reactantGlobalOrder += nsFlt;
521  ns = (size_t) nsFlt;
522  if ((doublereal) ns != nsFlt) {
523  if (ns < 1) {
524  ns = 1;
525  }
526  }
527  if (r.rstoich[n] != 0.0) {
528  m_rrxn[r.reactants[n]][rnum] += r.rstoich[n];
529  }
530  for (m = 0; m < ns; m++) {
531  rk.push_back(r.reactants[n]);
532  }
533  }
534  m_reactants.push_back(rk);
535 
536  std::vector<size_t> pk;
537  size_t np = r.products.size();
538  for (n = 0; n < np; n++) {
539  nsFlt = r.pstoich[n];
540  productGlobalOrder += nsFlt;
541  ns = (size_t) nsFlt;
542  if ((double) ns != nsFlt) {
543  if (ns < 1) {
544  ns = 1;
545  }
546  }
547  if (r.pstoich[n] != 0.0) {
548  m_prxn[r.products[n]][rnum] += r.pstoich[n];
549  }
550  for (m = 0; m < ns; m++) {
551  pk.push_back(r.products[n]);
552  }
553  }
554  m_products.push_back(pk);
555 
556  m_rkcn.push_back(0.0);
557 
558  m_rxnstoich.add(reactionNumber(), r);
559 
560  if (r.reversible) {
561  m_dn.push_back(productGlobalOrder - reactantGlobalOrder);
562  m_revindex.push_back(reactionNumber());
563  m_nrev++;
564  } else {
565  m_dn.push_back(productGlobalOrder - reactantGlobalOrder);
566  m_irrev.push_back(reactionNumber());
567  m_nirrev++;
568  }
569 }
570 
571 
572 void AqueousKinetics::installGroups(size_t irxn,
573  const vector<grouplist_t>& r,
574  const vector<grouplist_t>& p)
575 {
576  if (!r.empty()) {
577  writelog("installing groups for reaction "+int2str(reactionNumber()));
578  m_rgroups[reactionNumber()] = r;
579  m_pgroups[reactionNumber()] = p;
580  }
581 }
582 
583 
585 {
586  m_kk = thermo().nSpecies();
587  m_rrxn.resize(m_kk);
588  m_prxn.resize(m_kk);
589  m_conc.resize(m_kk);
590  m_grt.resize(m_kk);
591 }
592 
594 {
595  if (!m_finalized) {
596  m_finalized = true;
597 
598  // Guarantee that these arrays can be converted to double* even in the
599  // special case where there are no reactions defined.
600  if (!m_ii) {
601  m_perturb.resize(1, 1.0);
602  m_ropf.resize(1, 0.0);
603  m_ropr.resize(1, 0.0);
604  m_ropnet.resize(1, 0.0);
605  m_rkcn.resize(1, 0.0);
606  }
607  }
608 }
609 
611 {
612  return (m_finalized);
613 }
614 
615 }