Cantera  4.0.0a1
Loading...
Searching...
No Matches
StoichManager.h
Go to the documentation of this file.
1/**
2 * @file StoichManager.h
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#ifndef CT_STOICH_MGR_H
9#define CT_STOICH_MGR_H
10
11#include "cantera/numerics/eigen_sparse.h"
13
14namespace Cantera
15{
16
17/**
18 * @defgroup Stoichiometry Stoichiometry
19 *
20 * The classes defined here implement simple operations that are used by class
21 * Kinetics to compute things like rates of progress, species
22 * production rates, etc. In general, a reaction mechanism may involve many
23 * species and many reactions, but any given reaction typically only involves
24 * a few species as reactants, and a few as products. Therefore, the matrix of
25 * stoichiometric coefficients is very sparse. Not only is it sparse, but the
26 * non-zero matrix elements often have the value 1, and in many cases no more
27 * than three coefficients are non-zero for the reactants and/or the products.
28 *
29 * For the present purposes, we will consider each direction of a reversible
30 * reaction to be a separate reaction. We often need to compute quantities
31 * that can formally be written as a matrix product of a stoichiometric
32 * coefficient matrix and a vector of reaction rates. For example, the species
33 * creation rates are given by
34 *
35 * @f[
36 * \dot C_k = \sum_k \nu^{(p)}_{k,i} R_i
37 * @f]
38 *
39 * where @f$ \nu^{(p)_{k,i}} @f$ is the product-side stoichiometric
40 * coefficient of species @e k in reaction @e i. This could be done by
41 * straightforward matrix multiplication, but would be inefficient, since most
42 * of the matrix elements of @f$ \nu^{(p)}_{k,i} @f$ are zero. We could do
43 * better by using sparse-matrix algorithms to compute this product.
44 *
45 * If the reactions are general ones, with non-integral stoichiometric
46 * coefficients, this is about as good as we can do. But we are particularly
47 * concerned here with the performance for very large reaction mechanisms,
48 * which are usually composed of elementary reactions, which have integral
49 * stoichiometric coefficients. Furthermore, very few elementary reactions
50 * involve more than 3 product or reactant molecules.
51 *
52 * But we can do even better if we take account of the special structure of
53 * this matrix for elementary reactions involving three or fewer product
54 * molecules (or reactant molecules).
55 *
56 * To take advantage of this structure, reactions are divided into four groups.
57 * These classes are designed to take advantage of this sparse structure when
58 * computing quantities that can be written as matrix multiplies.
59 *
60 * They are designed to explicitly unroll loops over species or reactions for
61 * operations on reactions that require knowing the reaction stoichiometry.
62 *
63 * This module consists of class StoichManagerN, and classes C1, C2, and C3.
64 * Classes C1, C2, and C3 handle operations involving one, two, or three
65 * species, respectively, in a reaction. Instances are instantiated with a
66 * reaction number, and n species numbers (n = 1 for C1, etc.). All three
67 * classes have the same interface.
68 *
69 * These classes are designed for use by StoichManagerN, and the operations
70 * implemented are those needed to efficiently compute quantities such as
71 * rates of progress, species production rates, reaction thermochemistry, etc.
72 * The compiler will inline these methods into the body of the corresponding
73 * StoichManagerN method, and so there is no performance penalty (unless
74 * inlining is turned off).
75 *
76 * To describe the methods, consider class C3 and suppose an instance is
77 * created with reaction number irxn and species numbers k0, k1, and k2.
78 *
79 * - multiply(in, out) : out[irxn] is multiplied by
80 * in[k0] * in[k1] * in[k2]
81 *
82 * - incrementReaction(in, out) : out[irxn] is incremented by
83 * in[k0] + in[k1] + in[k2]
84 *
85 * - decrementReaction(in, out) : out[irxn] is decremented by
86 * in[k0] + in[k1] + in[k2]
87 *
88 * - incrementSpecies(in, out) : out[k0], out[k1], and out[k2]
89 * are all incremented by in[irxn]
90 *
91 * - decrementSpecies(in, out) : out[k0], out[k1], and out[k2]
92 * are all decremented by in[irxn]
93 *
94 * The function multiply() is usually used when evaluating the forward and
95 * reverse rates of progress of reactions. The rate constants are usually
96 * loaded into out[]. Then multiply() is called to add in the dependence of
97 * the species concentrations to yield a forward and reverse rop.
98 *
99 * The function incrementSpecies() and its cousin decrementSpecies() is used
100 * to translate from rates of progress to species production rates. The vector
101 * in[] is preloaded with the rates of progress of all reactions. Then
102 * incrementSpecies() is called to increment the species production vector,
103 * out[], with the rates of progress.
104 *
105 * The functions incrementReaction() and decrementReaction() are used to find
106 * the standard state equilibrium constant for a reaction. Here, output[] is a
107 * vector of length number of reactions, usually the standard Gibbs free
108 * energies of reaction, while input, usually the standard state Gibbs free
109 * energies of species, is a vector of length number of species.
110 *
111 * Note the stoichiometric coefficient for a species in a reaction is handled
112 * by always assuming it is equal to one and then treating reactants and
113 * products for a reaction separately. Bimolecular reactions involving the
114 * identical species are treated as involving separate species.
115 *
116 * The methods resizeCoeffs(), derivatives() and scale() are used for the calculation
117 * of derivatives with respect to species mole fractions. In this context,
118 * resizeCoeffs() is used to establish a mapping between a reaction and corresponding
119 * non-zero entries of the sparse derivative matrix of the reaction rate-of-progress
120 * vector, which itself is evaluated by the derivatives() method. The scale() method is
121 * used to multiply rop entries by reaction order and a user-supplied factor.
122 *
123 * @ingroup rateEvaluators
124 */
125
126/**
127 * Handles one species in a reaction.
128 * See @ref Stoichiometry
129 * @ingroup Stoichiometry
130 */
131class C1
132{
133public:
134 C1(size_t rxn = 0, size_t ic0 = 0) :
135 m_rxn(rxn),
136 m_ic0(ic0) {
137 }
138
139 void incrementSpecies(span<const double> R, span<double> S) const {
140 S[m_ic0] += R[m_rxn];
141 }
142
143 void decrementSpecies(span<const double> R, span<double> S) const {
144 S[m_ic0] -= R[m_rxn];
145 }
146
147 void multiply(span<const double> S, span<double> R) const {
148 R[m_rxn] *= S[m_ic0];
149 }
150
151 void incrementReaction(span<const double> S, span<double> R) const {
152 R[m_rxn] += S[m_ic0];
153 }
154
155 void decrementReaction(span<const double> S, span<double> R) const {
156 R[m_rxn] -= S[m_ic0];
157 }
158
159 void resizeCoeffs(const map<pair<size_t, size_t>, size_t>& indices)
160 {
161 m_jc0 = indices.at({m_rxn, m_ic0});
162 }
163
164 void derivatives(span<const double> S, span<const double> R,
165 span<double> jac) const
166 {
167 jac[m_jc0] += R[m_rxn]; // index (m_ic0, m_rxn)
168 }
169
170
171 void scale(span<const double> R, span<double> out, double factor) const {
172 out[m_rxn] = R[m_rxn] * factor;
173 }
174
175private:
176 //! Reaction number
177 size_t m_rxn;
178 //! Species number
179 size_t m_ic0;
180
181 size_t m_jc0; //!< Index in derivative triplet vector
182};
183
184/**
185 * Handles two species in a single reaction.
186 * See @ref Stoichiometry
187 * @ingroup Stoichiometry
188 */
189class C2
190{
191public:
192 C2(size_t rxn = 0, size_t ic0 = 0, size_t ic1 = 0)
193 : m_rxn(rxn), m_ic0(ic0), m_ic1(ic1) {}
194
195 void incrementSpecies(span<const double> R, span<double> S) const {
196 S[m_ic0] += R[m_rxn];
197 S[m_ic1] += R[m_rxn];
198 }
199
200 void decrementSpecies(span<const double> R, span<double> S) const {
201 S[m_ic0] -= R[m_rxn];
202 S[m_ic1] -= R[m_rxn];
203 }
204
205 void multiply(span<const double> S, span<double> R) const {
206 if (S[m_ic0] < 0 && S[m_ic1] < 0) {
207 R[m_rxn] = 0;
208 } else {
209 R[m_rxn] *= S[m_ic0] * S[m_ic1];
210 }
211 }
212
213 void incrementReaction(span<const double> S, span<double> R) const {
214 R[m_rxn] += S[m_ic0] + S[m_ic1];
215 }
216
217 void decrementReaction(span<const double> S, span<double> R) const {
218 R[m_rxn] -= (S[m_ic0] + S[m_ic1]);
219 }
220
221 void resizeCoeffs(const map<pair<size_t, size_t>, size_t>& indices)
222 {
223 m_jc0 = indices.at({m_rxn, m_ic0});
224 m_jc1 = indices.at({m_rxn, m_ic1});
225 }
226
227 void derivatives(span<const double> S, span<const double> R,
228 span<double> jac) const
229 {
230 if (S[m_ic1] > 0) {
231 jac[m_jc0] += R[m_rxn] * S[m_ic1]; // index (m_ic0, m_rxn)
232 }
233 if (S[m_ic0] > 0) {
234 jac[m_jc1] += R[m_rxn] * S[m_ic0]; // index (m_ic1, m_rxn)
235 }
236 }
237
238 void scale(span<const double> R, span<double> out, double factor) const
239 {
240 out[m_rxn] = 2 * R[m_rxn] * factor;
241 }
242
243private:
244 //! Reaction index -> index into the ROP vector
245 size_t m_rxn;
246
247 //! Species index -> index into the species vector for the two species.
248 size_t m_ic0, m_ic1;
249
250 size_t m_jc0, m_jc1; //!< Indices in derivative triplet vector
251};
252
253/**
254 * Handles three species in a reaction.
255 * See @ref Stoichiometry
256 * @ingroup Stoichiometry
257 */
258class C3
259{
260public:
261 C3(size_t rxn = 0, size_t ic0 = 0, size_t ic1 = 0, size_t ic2 = 0)
262 : m_rxn(rxn), m_ic0(ic0), m_ic1(ic1), m_ic2(ic2) {}
263
264 void incrementSpecies(span<const double> R, span<double> S) const {
265 S[m_ic0] += R[m_rxn];
266 S[m_ic1] += R[m_rxn];
267 S[m_ic2] += R[m_rxn];
268 }
269
270 void decrementSpecies(span<const double> R, span<double> S) const {
271 S[m_ic0] -= R[m_rxn];
272 S[m_ic1] -= R[m_rxn];
273 S[m_ic2] -= R[m_rxn];
274 }
275
276 void multiply(span<const double> S, span<double> R) const {
277 if ((S[m_ic0] < 0 && (S[m_ic1] < 0 || S[m_ic2] < 0)) ||
278 (S[m_ic1] < 0 && S[m_ic2] < 0)) {
279 R[m_rxn] = 0;
280 } else {
281 R[m_rxn] *= S[m_ic0] * S[m_ic1] * S[m_ic2];
282 }
283 }
284
285 void incrementReaction(span<const double> S, span<double> R) const {
286 R[m_rxn] += S[m_ic0] + S[m_ic1] + S[m_ic2];
287 }
288
289 void decrementReaction(span<const double> S, span<double> R) const {
290 R[m_rxn] -= (S[m_ic0] + S[m_ic1] + S[m_ic2]);
291 }
292
293 void resizeCoeffs(const map<pair<size_t, size_t>, size_t>& indices)
294 {
295 m_jc0 = indices.at({m_rxn, m_ic0});
296 m_jc1 = indices.at({m_rxn, m_ic1});
297 m_jc2 = indices.at({m_rxn, m_ic2});
298 }
299
300 void derivatives(span<const double> S, span<const double> R,
301 span<double> jac) const
302 {
303 if (S[m_ic1] > 0 && S[m_ic2] > 0) {
304 jac[m_jc0] += R[m_rxn] * S[m_ic1] * S[m_ic2];; // index (m_ic0, m_rxn)
305 }
306 if (S[m_ic0] > 0 && S[m_ic2] > 0) {
307 jac[m_jc1] += R[m_rxn] * S[m_ic0] * S[m_ic2]; // index (m_ic1, m_ic1)
308 }
309 if (S[m_ic0] > 0 && S[m_ic1] > 0) {
310 jac[m_jc2] += R[m_rxn] * S[m_ic0] * S[m_ic1]; // index (m_ic2, m_ic2)
311 }
312 }
313
314 void scale(span<const double> R, span<double> out, double factor) const
315 {
316 out[m_rxn] = 3 * R[m_rxn] * factor;
317 }
318
319private:
320 size_t m_rxn;
321 size_t m_ic0;
322 size_t m_ic1;
323 size_t m_ic2;
324
325 size_t m_jc0, m_jc1, m_jc2; //!< Indices in derivative triplet vector
326};
327
328/**
329 * Handles any number of species in a reaction, including fractional
330 * stoichiometric coefficients, and arbitrary reaction orders.
331 * See @ref Stoichiometry
332 * @ingroup Stoichiometry
333 */
335{
336public:
337 C_AnyN() = default;
338
339 C_AnyN(size_t rxn, span<const size_t> ic,
340 span<const double> order_, span<const double> stoich_) :
341 m_n(ic.size()),
342 m_rxn(rxn) {
343 m_ic.resize(m_n);
344 m_jc.resize(m_n, 0);
345 m_order.resize(m_n);
346 m_stoich.resize(m_n);
347 for (size_t n = 0; n < m_n; n++) {
348 m_ic[n] = ic[n];
349 m_order[n] = order_[n];
350 m_stoich[n] = stoich_[n];
351 }
352 }
353
354 void multiply(span<const double> input, span<double> output) const {
355 for (size_t n = 0; n < m_n; n++) {
356 double order = m_order[n];
357 if (order != 0.0) {
358 double c = input[m_ic[n]];
359 if (c > 0.0) {
360 output[m_rxn] *= std::pow(c, order);
361 } else {
362 output[m_rxn] = 0.0;
363 }
364 }
365 }
366 }
367
368 void incrementSpecies(span<const double> input, span<double> output) const {
369 double x = input[m_rxn];
370 for (size_t n = 0; n < m_n; n++) {
371 output[m_ic[n]] += m_stoich[n]*x;
372 }
373 }
374
375 void decrementSpecies(span<const double> input, span<double> output) const {
376 double x = input[m_rxn];
377 for (size_t n = 0; n < m_n; n++) {
378 output[m_ic[n]] -= m_stoich[n]*x;
379 }
380 }
381
382 void incrementReaction(span<const double> input, span<double> output) const {
383 for (size_t n = 0; n < m_n; n++) {
384 output[m_rxn] += m_stoich[n]*input[m_ic[n]];
385 }
386 }
387
388 void decrementReaction(span<const double> input, span<double> output) const {
389 for (size_t n = 0; n < m_n; n++) {
390 output[m_rxn] -= m_stoich[n]*input[m_ic[n]];
391 }
392 }
393
394 void resizeCoeffs(const map<pair<size_t, size_t>, size_t>& indices)
395 {
396 for (size_t i = 0; i < m_n; i++) {
397 m_jc[i] = indices.at({m_rxn, m_ic[i]});
398 }
399
400 m_sum_order = 0.;
401 for (size_t n = 0; n < m_n; ++n) {
402 m_sum_order += m_order[n];
403 }
404 }
405
406 void derivatives(span<const double> S, span<const double> R,
407 span<double> jac) const
408 {
409 for (size_t i = 0; i < m_n; i++) {
410 // calculate derivative
411 double prod = R[m_rxn];
412 double order_i = m_order[i];
413 if (S[m_ic[i]] > 0. && order_i != 0.) {
414 prod *= order_i * std::pow(S[m_ic[i]], order_i - 1);
415 for (size_t j = 0; j < m_n; j++) {
416 if (i != j) {
417 if (S[m_ic[j]] > 0) {
418 prod *= std::pow(S[m_ic[j]], m_order[j]);
419 } else {
420 prod = 0.;
421 break;
422 }
423 }
424 }
425 } else {
426 prod = 0.;
427 }
428 jac[m_jc[i]] += prod;
429 }
430 }
431
432 void scale(span<const double> R, span<double> out, double factor) const
433 {
434 out[m_rxn] = m_sum_order * R[m_rxn] * factor;
435 }
436
437private:
438 //! Length of the m_ic vector
439 /*!
440 * This is the number of species which participate in the reaction order
441 * and stoichiometric coefficient vectors for the reactant or product
442 * description of the reaction.
443 */
444 size_t m_n = 0;
445
446 //! ID of the reaction corresponding to this stoichiometric manager
447 /*!
448 * This is used within the interface to select the array position to read
449 * and write to Normally this is associated with the reaction number in an
450 * array of quantities indexed by the reaction number, for example, ROP[irxn].
451 */
452 size_t m_rxn = npos;
453
454 //! Vector of species which are involved with this stoichiometric manager
455 //! calculations
456 /*!
457 * This is an integer list of species which participate in either the
458 * reaction order matrix or the stoichiometric order matrix for this
459 * reaction, m_rxn.
460 */
461 vector<size_t> m_ic;
462
463 //! Reaction orders for the reaction
464 /*!
465 * This is either for the reactants or products. Length = m_n. Species
466 * number, m_ic[n], has a reaction order of m_order[n].
467 */
468 vector<double> m_order;
469
470 //! Stoichiometric coefficients for the reaction, reactant or product side.
471 /*!
472 * This is either for the reactants or products. Length = m_n. Species
473 * number m_ic[m], has a stoichiometric coefficient of m_stoich[n].
474 */
475 vector<double> m_stoich;
476
477 vector<size_t> m_jc; //!< Indices in derivative triplet vector
478
479 double m_sum_order; //!< Sum of reaction order vector
480};
481
482template<class InputIter, class Vec1, class Vec2>
483inline static void _multiply(InputIter begin, InputIter end,
484 const Vec1& input, Vec2& output)
485{
486 for (; begin != end; ++begin) {
487 begin->multiply(input, output);
488 }
489}
490
491template<class InputIter, class Vec1, class Vec2>
492inline static void _incrementSpecies(InputIter begin,
493 InputIter end, const Vec1& input, Vec2& output)
494{
495 for (; begin != end; ++begin) {
496 begin->incrementSpecies(input, output);
497 }
498}
499
500template<class InputIter, class Vec1, class Vec2>
501inline static void _decrementSpecies(InputIter begin,
502 InputIter end, const Vec1& input, Vec2& output)
503{
504 for (; begin != end; ++begin) {
505 begin->decrementSpecies(input, output);
506 }
507}
508
509template<class InputIter, class Vec1, class Vec2>
510inline static void _incrementReactions(InputIter begin,
511 InputIter end, const Vec1& input, Vec2& output)
512{
513 for (; begin != end; ++begin) {
514 begin->incrementReaction(input, output);
515 }
516}
517
518template<class InputIter, class Vec1, class Vec2>
519inline static void _decrementReactions(InputIter begin,
520 InputIter end, const Vec1& input, Vec2& output)
521{
522 for (; begin != end; ++begin) {
523 begin->decrementReaction(input, output);
524 }
525}
526
527template<class InputIter, class Indices>
528inline static void _resizeCoeffs(InputIter begin, InputIter end, Indices& ix)
529{
530 for (; begin != end; ++begin) {
531 begin->resizeCoeffs(ix);
532 }
533}
534
535template<class InputIter, class Vec1, class Vec2, class Vec3>
536inline static void _derivatives(InputIter begin, InputIter end,
537 const Vec1& S, const Vec2& R, Vec3& jac)
538{
539 for (; begin != end; ++begin) {
540 begin->derivatives(S, R, jac);
541 }
542}
543
544template<class InputIter, class Vec1, class Vec2>
545inline static void _scale(InputIter begin, InputIter end,
546 const Vec1& in, Vec2& out, double factor)
547{
548 for (; begin != end; ++begin) {
549 begin->scale(in, out, factor);
550 }
551}
552
553/**
554 * This class handles operations involving the stoichiometric coefficients on
555 * one side of a reaction (reactant or product) for a set of reactions
556 * comprising a reaction mechanism. This class is used by class Kinetics, which
557 * contains three instances of this class (one to handle operations on the
558 * reactions, one for the products of reversible reactions, and one for the
559 * products of irreversible reactions).
560 *
561 * This class is designed for use with elementary reactions, or at least ones
562 * with integral stoichiometric coefficients. Let @f$ M(i) @f$ be the number of
563 * molecules on the product or reactant side of reaction number i.
564 * @f[
565 * r_i = \sum_m^{M_i} s_{k_{m,i}}
566 * @f]
567 * To understand the operations performed by this class, let @f$ N_{k,i} @f$
568 * denote the stoichiometric coefficient of species k on one side (reactant or
569 * product) in reaction i. Then @b N is a sparse K by I matrix of stoichiometric
570 * coefficients.
571 *
572 * The following matrix operations may be carried out with a vector S of length
573 * K, and a vector R of length I:
574 *
575 * - @f$ S = S + N R @f$ (incrementSpecies)
576 * - @f$ S = S - N R @f$ (decrementSpecies)
577 * - @f$ R = R + N^T S @f$ (incrementReaction)
578 * - @f$ R = R - N^T S @f$ (decrementReaction)
579 *
580 * The actual implementation, however, does not compute these quantities by
581 * matrix multiplication. A faster algorithm is used that makes use of the fact
582 * that the @b integer-valued N matrix is very sparse, and the non-zero terms
583 * are small positive integers.
584 * @f[
585 * S_k = R_{i1} + \dots + R_{iM}
586 * @f]
587 * where M is the number of molecules, and @f$ i(m) @f$ is the
588 * See @ref Stoichiometry
589 * @ingroup Stoichiometry
590 */
592{
593public:
594 /**
595 * Constructor for the StoichManagerN class.
596 */
598 m_stoichCoeffs.setZero();
599 m_stoichCoeffs.resize(0, 0);
600 }
601
602 //! Resize the sparse coefficient matrix
603 void resizeCoeffs(size_t nSpc, size_t nRxn)
604 {
605 size_t nCoeffs = m_coeffList.size();
606
607 // Stoichiometric coefficient matrix
608 m_stoichCoeffs.resize(nSpc, nRxn);
609 m_stoichCoeffs.reserve(nCoeffs);
610 m_stoichCoeffs.setFromTriplets(m_coeffList.begin(), m_coeffList.end());
611
612 // Set up outer/inner indices for mapped derivative output
613 Eigen::SparseMatrix<double> tmp = m_stoichCoeffs.transpose();
614 m_outerIndices.resize(nSpc + 1); // number of columns + 1
615 for (int i = 0; i < tmp.outerSize() + 1; i++) {
616 m_outerIndices[i] = tmp.outerIndexPtr()[i];
617 }
618 m_innerIndices.resize(nCoeffs);
619 for (size_t n = 0; n < nCoeffs; n++) {
620 m_innerIndices[n] = tmp.innerIndexPtr()[n];
621 }
622 m_values.resize(nCoeffs, 0.);
623
624 // Set up index pairs for derivatives
625 map<pair<size_t, size_t>, size_t> indices;
626 size_t n = 0;
627 for (int i = 0; i < tmp.outerSize(); i++) {
628 for (Eigen::SparseMatrix<double>::InnerIterator it(tmp, i); it; ++it) {
629 indices[{static_cast<size_t>(it.row()),
630 static_cast<size_t>(it.col())}] = n++;
631 }
632 }
633 // update reaction setup
634 _resizeCoeffs(m_c1_list.begin(), m_c1_list.end(), indices);
635 _resizeCoeffs(m_c2_list.begin(), m_c2_list.end(), indices);
636 _resizeCoeffs(m_c3_list.begin(), m_c3_list.end(), indices);
637 _resizeCoeffs(m_cn_list.begin(), m_cn_list.end(), indices);
638
639 m_ready = true;
640 }
641
642 /**
643 * Add a single reaction to the list of reactions that this stoichiometric
644 * manager object handles.
645 *
646 * This function is the same as the add() function below. However, the order
647 * of each species in the power list expression is set to one automatically.
648 */
649 void add(size_t rxn, span<const size_t> k) {
650 vector<double> order(k.size(), 1.0);
651 vector<double> stoich(k.size(), 1.0);
652 add(rxn, k, order, stoich);
653 }
654
655 void add(size_t rxn, span<const size_t> k, span<const double> order) {
656 vector<double> stoich(k.size(), 1.0);
657 add(rxn, k, order, stoich);
658 }
659
660 //! Add a single reaction to the list of reactions that this
661 //! stoichiometric manager object handles.
662 /*!
663 * @param rxn Reaction index of the current reaction. This is used as an
664 * index into vectors which have length n_total_rxn.
665 * @param k This is a vector of integer values specifying the species
666 * indices. The length of this vector species the number of different
667 * species in the description. The value of the entries are the species
668 * indices. These are used as indexes into vectors which have length
669 * n_total_species.
670 * @param order This is a vector of the same length as vector k. The order
671 * is used for the routine power(), which produces a power law
672 * expression involving the species vector.
673 * @param stoich This is used to handle fractional stoichiometric
674 * coefficients on the product side of irreversible reactions.
675 */
676 void add(size_t rxn, span<const size_t> k, span<const double> order,
677 span<const double> stoich) {
678 if (order.size() != k.size()) {
679 throw CanteraError(
680 "StoichManagerN::add()", "size of order and species arrays differ");
681 }
682 if (stoich.size() != k.size()) {
683 throw CanteraError(
684 "StoichManagerN::add()", "size of stoich and species arrays differ");
685 }
686 bool frac = false;
687 for (size_t n = 0; n < stoich.size(); n++) {
688 m_coeffList.emplace_back(
689 static_cast<int>(k[n]), static_cast<int>(rxn), stoich[n]);
690 if (fmod(stoich[n], 1.0) || stoich[n] != order[n]) {
691 frac = true;
692 }
693 }
694 if (frac || k.size() > 3) {
695 m_cn_list.emplace_back(rxn, k, order, stoich);
696 } else {
697 // Try to express the reaction with unity stoichiometric
698 // coefficients (by repeating species when necessary) so that the
699 // simpler 'multiply' function can be used to compute the rate
700 // instead of 'power'.
701 vector<size_t> kRep;
702 for (size_t n = 0; n < k.size(); n++) {
703 for (size_t i = 0; i < stoich[n]; i++) {
704 kRep.push_back(k[n]);
705 }
706 }
707
708 switch (kRep.size()) {
709 case 1:
710 m_c1_list.emplace_back(rxn, kRep[0]);
711 break;
712 case 2:
713 m_c2_list.emplace_back(rxn, kRep[0], kRep[1]);
714 break;
715 case 3:
716 m_c3_list.emplace_back(rxn, kRep[0], kRep[1], kRep[2]);
717 break;
718 default:
719 m_cn_list.emplace_back(rxn, k, order, stoich);
720 }
721 }
722 m_ready = false;
723 }
724
725 void multiply(span<const double> input, span<double> output) const {
726 _multiply(m_c1_list.begin(), m_c1_list.end(), input, output);
727 _multiply(m_c2_list.begin(), m_c2_list.end(), input, output);
728 _multiply(m_c3_list.begin(), m_c3_list.end(), input, output);
729 _multiply(m_cn_list.begin(), m_cn_list.end(), input, output);
730 }
731
732 void incrementSpecies(span<const double> input, span<double> output) const {
733 _incrementSpecies(m_c1_list.begin(), m_c1_list.end(), input, output);
734 _incrementSpecies(m_c2_list.begin(), m_c2_list.end(), input, output);
735 _incrementSpecies(m_c3_list.begin(), m_c3_list.end(), input, output);
736 _incrementSpecies(m_cn_list.begin(), m_cn_list.end(), input, output);
737 }
738
739 void decrementSpecies(span<const double> input, span<double> output) const {
740 _decrementSpecies(m_c1_list.begin(), m_c1_list.end(), input, output);
741 _decrementSpecies(m_c2_list.begin(), m_c2_list.end(), input, output);
742 _decrementSpecies(m_c3_list.begin(), m_c3_list.end(), input, output);
743 _decrementSpecies(m_cn_list.begin(), m_cn_list.end(), input, output);
744 }
745
746 void incrementReactions(span<const double> input, span<double> output) const {
747 _incrementReactions(m_c1_list.begin(), m_c1_list.end(), input, output);
748 _incrementReactions(m_c2_list.begin(), m_c2_list.end(), input, output);
749 _incrementReactions(m_c3_list.begin(), m_c3_list.end(), input, output);
750 _incrementReactions(m_cn_list.begin(), m_cn_list.end(), input, output);
751 }
752
753 void decrementReactions(span<const double> input, span<double> output) const {
754 _decrementReactions(m_c1_list.begin(), m_c1_list.end(), input, output);
755 _decrementReactions(m_c2_list.begin(), m_c2_list.end(), input, output);
756 _decrementReactions(m_c3_list.begin(), m_c3_list.end(), input, output);
757 _decrementReactions(m_cn_list.begin(), m_cn_list.end(), input, output);
758 }
759
760 //! Return matrix containing stoichiometric coefficients
761 const Eigen::SparseMatrix<double>& stoichCoeffs() const
762 {
763 if (!m_ready) {
764 // This can happen if a user overrides default behavior:
765 // Kinetics::resizeReactions is not called after adding reactions via
766 // Kinetics::addReaction with the 'resize' flag set to 'false'
767 throw CanteraError("StoichManagerN::stoichCoeffs", "The object "
768 "is not fully configured; make sure to call resizeCoeffs().");
769 }
770 return m_stoichCoeffs;
771 }
772
773 //! Calculate derivatives with respect to species concentrations.
774 /*!
775 * The species derivative is the term of the Jacobian that accounts for
776 * species products in the law of mass action. As StoichManagerN does not account
777 * for third bodies or rate constants that depend on species concentrations,
778 * corresponding derivatives are not included.
779 *
780 * @param conc Species concentration.
781 * @param rates Rates-of-progress.
782 */
783 Eigen::SparseMatrix<double> derivatives(span<const double> conc,
784 span<const double> rates)
785 {
786 // calculate derivative entries using known sparse storage order
787 std::fill(m_values.begin(), m_values.end(), 0.);
788 _derivatives(m_c1_list.begin(), m_c1_list.end(), conc, rates, m_values);
789 _derivatives(m_c2_list.begin(), m_c2_list.end(), conc, rates, m_values);
790 _derivatives(m_c3_list.begin(), m_c3_list.end(), conc, rates, m_values);
791 _derivatives(m_cn_list.begin(), m_cn_list.end(), conc, rates, m_values);
792
793 return Eigen::Map<Eigen::SparseMatrix<double>>(
794 m_stoichCoeffs.cols(), m_stoichCoeffs.rows(), m_values.size(),
795 m_outerIndices.data(), m_innerIndices.data(), m_values.data());
796 }
797
798 //! Scale input by reaction order and factor
799 void scale(span<const double> in, span<double> out, double factor) const
800 {
801 _scale(m_c1_list.begin(), m_c1_list.end(), in, out, factor);
802 _scale(m_c2_list.begin(), m_c2_list.end(), in, out, factor);
803 _scale(m_c3_list.begin(), m_c3_list.end(), in, out, factor);
804 _scale(m_cn_list.begin(), m_cn_list.end(), in, out, factor);
805 }
806
807private:
808 bool m_ready; //!< Boolean flag indicating whether object is fully configured
809
810 vector<C1> m_c1_list;
811 vector<C2> m_c2_list;
812 vector<C3> m_c3_list;
813 vector<C_AnyN> m_cn_list;
814
815 //! Sparse matrices for stoichiometric coefficients
816 SparseTriplets m_coeffList;
817 Eigen::SparseMatrix<double> m_stoichCoeffs;
818
819 //! Storage indicies used to build derivatives
820 vector<int> m_outerIndices;
821 vector<int> m_innerIndices;
822 vector<double> m_values;
823};
824
825}
826
827#endif
Handles one species in a reaction.
size_t m_rxn
Reaction number.
size_t m_jc0
Index in derivative triplet vector.
size_t m_ic0
Species number.
Handles two species in a single reaction.
size_t m_rxn
Reaction index -> index into the ROP vector.
size_t m_ic0
Species index -> index into the species vector for the two species.
size_t m_jc1
Indices in derivative triplet vector.
Handles three species in a reaction.
size_t m_jc2
Indices in derivative triplet vector.
Handles any number of species in a reaction, including fractional stoichiometric coefficients,...
size_t m_rxn
ID of the reaction corresponding to this stoichiometric manager.
vector< double > m_stoich
Stoichiometric coefficients for the reaction, reactant or product side.
double m_sum_order
Sum of reaction order vector.
vector< size_t > m_ic
Vector of species which are involved with this stoichiometric manager calculations.
vector< double > m_order
Reaction orders for the reaction.
vector< size_t > m_jc
Indices in derivative triplet vector.
size_t m_n
Length of the m_ic vector.
Base class for exceptions thrown by Cantera classes.
This class handles operations involving the stoichiometric coefficients on one side of a reaction (re...
vector< int > m_outerIndices
Storage indicies used to build derivatives.
bool m_ready
Boolean flag indicating whether object is fully configured.
Eigen::SparseMatrix< double > derivatives(span< const double > conc, span< const double > rates)
Calculate derivatives with respect to species concentrations.
SparseTriplets m_coeffList
Sparse matrices for stoichiometric coefficients.
void scale(span< const double > in, span< double > out, double factor) const
Scale input by reaction order and factor.
void resizeCoeffs(size_t nSpc, size_t nRxn)
Resize the sparse coefficient matrix.
void add(size_t rxn, span< const size_t > k)
Add a single reaction to the list of reactions that this stoichiometric manager object handles.
void add(size_t rxn, span< const size_t > k, span< const double > order, span< const double > stoich)
Add a single reaction to the list of reactions that this stoichiometric manager object handles.
const Eigen::SparseMatrix< double > & stoichCoeffs() const
Return matrix containing stoichiometric coefficients.
StoichManagerN()
Constructor for the StoichManagerN class.
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:183