Cantera 2.6.0
ReactionPath.cpp
Go to the documentation of this file.
1/**
2 * @file ReactionPath.cpp
3 * Implementation file for classes used in reaction path analysis.
4 */
5
6// This file is part of Cantera. See License.txt in the top-level directory or
7// at https://cantera.org/license.txt for license and copyright information.
8
12
13#include <boost/algorithm/string.hpp>
14
15namespace ba = boost::algorithm;
16
17using namespace std;
18
19namespace Cantera
20{
21
22void SpeciesNode::addPath(Path* path)
23{
24 m_paths.push_back(path);
25 if (path->begin() == this) {
26 m_out += path->flow();
27 } else if (path->end() == this) {
28 m_in += path->flow();
29 } else {
30 throw CanteraError("SpeciesNode::addPath", "path added to wrong node");
31 }
32}
33
34void SpeciesNode::printPaths()
35{
36 for (size_t i = 0; i < m_paths.size(); i++) {
37 cout << m_paths[i]->begin()->name << " --> "
38 << m_paths[i]->end()->name << ": "
39 << m_paths[i]->flow() << endl;
40 }
41}
42
43Path::Path(SpeciesNode* begin, SpeciesNode* end)
44 : m_a(begin), m_b(end), m_total(0.0)
45{
46 begin->addPath(this);
47 end->addPath(this);
48}
49
50void Path::addReaction(size_t rxnNumber, doublereal value,
51 const string& label)
52{
53 m_rxn[rxnNumber] += value;
54 m_total += value;
55 if (label != "") {
56 m_label[label] += value;
57 }
58}
59
60void Path::writeLabel(ostream& s, doublereal threshold)
61{
62 if (m_label.size() == 0) {
63 return;
64 }
65 doublereal v;
66 for (const auto& label : m_label) {
67 v = label.second/m_total;
68 if (m_label.size() == 1) {
69 s << label.first << "\\l";
70 } else if (v > threshold) {
71 s << label.first;
72 int percent = int(100*v + 0.5);
73 if (percent < 100) {
74 s << " (" << percent << "%)\\l";
75 } else {
76 s << "\\l";
77 }
78 }
79 }
80}
81
82ReactionPathDiagram::ReactionPathDiagram()
83{
84 name = "reaction_paths";
85 m_flxmax = 0.0;
86 bold_color = "blue";
87 normal_color = "steelblue";
88 dashed_color = "gray";
89 dot_options = "center=1;";
90 m_font = "Helvetica";
91 bold_min = 0.2;
92 dashed_max = 0.0;
93 label_min = 0.0;
94 threshold = 0.005;
95 flow_type = NetFlow;
96 scale = -1;
97 x_size = -1.0;
98 y_size = -1.0;
99 arrow_width = -5.0;
100 show_details = false;
101 arrow_hue = 0.6666;
102 title = "";
103 m_local = npos;
104}
105
107{
108 // delete the nodes
109 for (const auto& node : m_nodes) {
110 delete node.second;
111 }
112
113 // delete the paths
114 size_t nn = nPaths();
115 for (size_t n = 0; n < nn; n++) {
116 delete m_pathlist[n];
117 }
118}
119
120vector_int ReactionPathDiagram::reactions()
121{
122 doublereal flmax = 0.0;
123 for (size_t i = 0; i < nPaths(); i++) {
124 Path* p = path(i);
125 flmax = std::max(p->flow(), flmax);
126 }
127 m_rxns.clear();
128 for (size_t i = 0; i < nPaths(); i++) {
129 for (const auto& rxn : path(i)->reactionMap()) {
130 double flxratio = rxn.second/flmax;
131 if (flxratio > threshold) {
132 m_rxns[rxn.first] = 1;
133 }
134 }
135 }
136 vector_int r;
137 for (const auto& rxn : m_rxns) {
138 r.push_back(int(rxn.first));
139 }
140 return r;
141}
142
143void ReactionPathDiagram::add(ReactionPathDiagram& d)
144{
145 for (size_t n = 0; n < nPaths(); n++) {
146 Path* p = path(n);
147 size_t k1 = p->begin()->number;
148 size_t k2 = p->end()->number;
149 p->setFlow(p->flow() + d.flow(k1,k2));
150 }
151}
152
153void ReactionPathDiagram::findMajorPaths(doublereal athreshold, size_t lda,
154 doublereal* a)
155{
156 double netmax = 0.0;
157 for (size_t n = 0; n < nNodes(); n++) {
158 for (size_t m = n+1; m < nNodes(); m++) {
159 size_t k1 = m_speciesNumber[n];
160 size_t k2 = m_speciesNumber[m];
161 double fl = fabs(netFlow(k1,k2));
162 netmax = std::max(fl, netmax);
163 }
164 }
165 for (size_t n = 0; n < nNodes(); n++) {
166 for (size_t m = n+1; m < nNodes(); m++) {
167 size_t k1 = m_speciesNumber[n];
168 size_t k2 = m_speciesNumber[m];
169 double fl = fabs(netFlow(k1,k2));
170 if (fl > athreshold*netmax) {
171 a[lda*k1 + k2] = 1;
172 }
173 }
174 }
175}
176
177void ReactionPathDiagram::writeData(ostream& s)
178{
179 s << title << endl;
180 for (size_t i1 = 0; i1 < nNodes(); i1++) {
181 size_t k1 = m_speciesNumber[i1];
182 s << m_nodes[k1]->name << " ";
183 }
184 s << endl;
185 for (size_t i1 = 0; i1 < nNodes(); i1++) {
186 size_t k1 = m_speciesNumber[i1];
187 for (size_t i2 = i1+1; i2 < nNodes(); i2++) {
188 size_t k2 = m_speciesNumber[i2];
189 double f1 = flow(k1, k2);
190 double f2 = flow(k2, k1);
191 s << m_nodes[k1]->name << " " << m_nodes[k2]->name
192 << " " << f1 << " " << -f2 << endl;
193 }
194 }
195}
196
198{
199 doublereal flmax = 0.0;
200 s.precision(3);
201
202 // a directed graph
203 s << "digraph " << name << " {" << endl;
204
205 // the graph will be no larger than x_size, y_size
206 if (x_size > 0.0) {
207 if (y_size < 0.0) {
208 y_size = x_size;
209 }
210 s << "size = \""
211 << x_size << ","
212 << y_size << "\";"
213 << endl;
214 }
215
216 if (dot_options != "") {
217 s << dot_options << endl;
218 }
219
220 // draw paths representing net flows
221 if (flow_type == NetFlow) {
222 // if no scale was specified, normalize net flows by the maximum net
223 // flow
224 if (scale <= 0.0) {
225 for (size_t i1 = 0; i1 < nNodes(); i1++) {
226 size_t k1 = m_speciesNumber[i1];
227 node(k1)->visible = false;
228 for (size_t i2 = i1+1; i2 < nNodes(); i2++) {
229 size_t k2 = m_speciesNumber[i2];
230 double flx = netFlow(k1, k2);
231 if (flx < 0.0) {
232 flx = -flx;
233 }
234 flmax = std::max(flx, flmax);
235 }
236 }
237 } else {
238 flmax = scale;
239 }
240 flmax = std::max(flmax, 1e-10);
241
242 // loop over all unique pairs of nodes
243 for (size_t i1 = 0; i1 < nNodes(); i1++) {
244 size_t k1 = m_speciesNumber[i1];
245 for (size_t i2 = i1+1; i2 < nNodes(); i2++) {
246 size_t k2 = m_speciesNumber[i2];
247 double flx = netFlow(k1, k2);
248 if (m_local != npos && k1 != m_local && k2 != m_local) {
249 flx = 0.0;
250 }
251 if (flx != 0.0) {
252 double flxratio;
253 size_t kbegin, kend;
254 // set beginning and end of the path based on the sign of
255 // the net flow
256 if (flx > 0.0) {
257 kbegin = k1;
258 kend = k2;
259 flxratio = flx/flmax;
260 } else {
261 kbegin = k2;
262 kend = k1;
263 flxratio = -flx/flmax;
264 }
265
266 // write out path specification if the net flow is greater
267 // than the threshold
268 if (flxratio >= threshold) {
269 // make nodes visible
270 node(kbegin)->visible = true;
271 node(kend)->visible = true;
272
273 s << "s" << kbegin << " -> s" << kend;
274 s << "[fontname=\""+m_font+"\", penwidth=";
275
276 if (arrow_width < 0) {
277 double lwidth = 1.0 - 4.0
278 * log10(flxratio/threshold)/log10(threshold) + 1.0;
279 s << lwidth;
280 s << ", arrowsize="
281 << std::min(6.0, 0.5*lwidth);
282 } else {
283 s << arrow_width;
284 s << ", arrowsize=" << flxratio + 1;
285 }
286
287 doublereal hue = 0.7;
288 doublereal bright = 0.9;
289 s << ", color=" << "\"" << hue << ", "
290 << flxratio + 0.5
291 << ", " << bright << "\"" << endl;
292
293 if (flxratio > label_min) {
294 s << ", label=\" " << flxratio;
295 if (show_details) {
296 if (flow(kbegin, kend) > 0.0) {
297 s << "\\l fwd: "
298 << flow(kbegin, kend)/flmax << "\\l";
299 path(kbegin, kend)->writeLabel(s);
300 }
301 if (flow(kend, kbegin) > 0.0) {
302 s << " \\l rev: "
303 << flow(kend,kbegin)/flmax << "\\l";
304 path(kend, kbegin)->writeLabel(s);
305 }
306 }
307 s << "\"";
308 }
309 s << "];" << endl;
310 }
311 }
312 }
313 }
314 } else {
315 if (scale < 0) {
316 for (size_t i = 0; i < nPaths(); i++) {
317 flmax = std::max(path(i)->flow(), flmax);
318 }
319 } else {
320 flmax = scale;
321 }
322
323 for (size_t i = 0; i < nPaths(); i++) {
324 Path* p = path(i);
325 double flxratio = p->flow()/flmax;
326 if (m_local != npos) {
327 if (p->begin()->number != m_local
328 && p->end()->number != m_local) {
329 flxratio = 0.0;
330 }
331 }
332 if (flxratio > threshold) {
333 p->begin()->visible = true;
334 p->end()->visible = true;
335 s << "s" << p->begin()->number
336 << " -> s" << p->end()->number;
337
338 if (arrow_width < 0) {
339 double lwidth = 1.0 - 4.0 * log10(flxratio/threshold)/log10(threshold)
340 + 1.0;
341 s << "[fontname=\""+m_font+"\", penwidth="
342 << lwidth;
343 s << ", arrowsize="
344 << std::min(6.0, 0.5*lwidth);
345 } else {
346 s << ", penwidth="
347 << arrow_width;
348 s << ", arrowsize=" << flxratio + 1;
349 }
350 doublereal hue = 0.7;
351 doublereal bright = 0.9;
352 s << ", color=" << "\"" << hue << ", " << flxratio + 0.5
353 << ", " << bright << "\"" << endl;
354
355 if (flxratio > label_min) {
356 s << ", label = \" " << flxratio;
357 if (show_details) {
358 s << "\\l";
359 p->writeLabel(s);
360 }
361 s << "\"";
362 }
363 s << "];" << endl;
364 }
365 }
366 }
367 s.precision(2);
368 for (const auto& node : m_nodes) {
369 if (node.second->visible) {
370 s << "s" << node.first << " [ fontname=\""+m_font+"\", label=\"" << node.second->name
371 << "\"];" << endl;
372 }
373 }
374 s << " label = " << "\"" << "Scale = "
375 << flmax << "\\l " << title << "\";" << endl;
376 s << " fontname = \""+m_font+"\";" << endl << "}" << endl;
377}
378
379
380void ReactionPathDiagram::addNode(size_t k, const string& nm, doublereal x)
381{
382 if (!m_nodes[k]) {
383 m_nodes[k] = new SpeciesNode;
384 m_nodes[k]->number = k;
385 m_nodes[k]->name = nm;
386 m_nodes[k]->value = x;
387 m_speciesNumber.push_back(k);
388 }
389}
390
391void ReactionPathDiagram::linkNodes(size_t k1, size_t k2, size_t rxn,
392 doublereal value, string legend)
393{
394 Path* ff = m_paths[k1][k2];
395 if (!ff) {
396 ff= new Path(m_nodes[k1], m_nodes[k2]);
397 m_paths[k1][k2] = ff;
398 m_pathlist.push_back(ff);
399 }
400 ff->addReaction(rxn, value, legend);
401 m_rxns[rxn] = 1;
402 m_flxmax = std::max(ff->flow(), m_flxmax);
403}
404
405std::vector<size_t> ReactionPathDiagram::species()
406{
407 return m_speciesNumber;
408}
409
410int ReactionPathBuilder::findGroups(ostream& logfile, Kinetics& s)
411{
412 m_groups.resize(m_nr);
413 for (size_t i = 0; i < m_nr; i++) { // loop over reactions
414 logfile << endl << "Reaction " << i+1 << ": "
415 << s.reactionString(i);
416
417 if (m_determinate[i]) {
418 logfile << " ... OK." << endl;
419 } else if (m_reac[i].size() == 2 && m_prod[i].size() == 2) {
420 // indices for the two reactants
421 size_t kr0 = m_reac[i][0];
422 size_t kr1 = m_reac[i][1];
423
424 // indices for the two products
425 size_t kp0 = m_prod[i][0];
426 size_t kp1 = m_prod[i][1];
427
428 // references to the Group objects representing the reactants
429 const Group& r0 = m_sgroup[kr0];
430 const Group& r1 = m_sgroup[kr1];
431 const Group& p0 = m_sgroup[kp0];
432 const Group& p1 = m_sgroup[kp1];
433
434 const Group* group_a0=0, *group_b0=0, *group_c0=0,
435 *group_a1=0, *group_b1=0, *group_c1=0;
436 Group b0 = p0 - r0;
437 Group b1 = p1 - r0;
438 if (b0.valid() && b1.valid()) {
439 logfile << " ... ambiguous." << endl;
440 } else if (!b0.valid() && !b1.valid()) {
441 logfile << " ... cannot express as A + BC = AB + C" << endl;
442 } else {
443 logfile << endl;
444 }
445
446 if (b0.valid()) {
447 if (b0.sign() > 0) {
448 group_a0 = &r0;
449 group_b0 = &b0;
450 group_c0 = &p1;
451 m_transfer[i][0][0] = r0;
452 m_transfer[i][1][0] = b0;
453 m_transfer[i][1][1] = p1;
454 } else {
455 group_a0 = &r1;
456 group_c0 = &p0;
457 b0 *= -1;
458 group_b0 = &b0;
459 m_transfer[i][1][1] = r1;
460 m_transfer[i][0][1] = b0;
461 m_transfer[i][0][0] = p0;
462 }
463 logfile << " ";
464 group_a0->fmt(logfile, m_elementSymbols);
465 logfile << " + ";
466 group_b0->fmt(logfile,m_elementSymbols);
467 group_c0->fmt(logfile, m_elementSymbols);
468 logfile << " = ";
469 group_a0->fmt(logfile, m_elementSymbols);
470 group_b0->fmt(logfile, m_elementSymbols);
471 logfile << " + ";
472 group_c0->fmt(logfile, m_elementSymbols);
473 if (b1.valid()) {
474 logfile << " [<= default] " << endl;
475 } else {
476 logfile << endl;
477 }
478 }
479
480 if (b1.valid()) {
481 if (b1.sign() > 0) {
482 group_a1 = &r0;
483 group_b1 = &b1;
484 group_c1 = &p0;
485 if (!b0.valid()) {
486 m_transfer[i][0][1] = r0;
487 m_transfer[i][1][1] = b0;
488 m_transfer[i][1][0] = p0;
489 }
490 } else {
491 group_a1 = &r1;
492 group_c1 = &p1;
493 b1 *= -1;
494 group_b1 = &b1;
495 if (!b0.valid()) {
496 m_transfer[i][1][0] = r1;
497 m_transfer[i][0][0] = b0;
498 m_transfer[i][0][1] = p1;
499 }
500 }
501 logfile << " ";
502 group_a1->fmt(logfile, m_elementSymbols);
503 logfile << " + ";
504 group_b1->fmt(logfile, m_elementSymbols);
505 group_c1->fmt(logfile, m_elementSymbols);
506 logfile << " = ";
507 group_a1->fmt(logfile, m_elementSymbols);
508 group_b1->fmt(logfile, m_elementSymbols);
509 logfile << " + ";
510 group_c1->fmt(logfile, m_elementSymbols);
511 logfile << endl;
512 }
513 } else {
514 logfile << "... cannot parse. [ignored]" << endl;
515 }
516 }
517 return 1;
518}
519
520void ReactionPathBuilder::findElements(Kinetics& kin)
521{
522 m_enamemap.clear();
523 m_nel = 0;
524 for (size_t i = 0; i < kin.nPhases(); i++) {
525 ThermoPhase* p = &kin.thermo(i);
526 // iterate over the elements in this phase
527 for (size_t m = 0; m < p->nElements(); m++) {
528 string ename = p->elementName(m);
529
530 // if no entry is found for this element name, then it is a new
531 // element. In this case, add the name to the list of names,
532 // increment the element count, and add an entry to the
533 // name->(index+1) map.
534 if (m_enamemap.find(ename) == m_enamemap.end()) {
535 m_enamemap[ename] = m_nel + 1;
536 m_elementSymbols.push_back(ename);
537 m_nel++;
538 }
539 }
540 }
541 m_atoms.resize(kin.nTotalSpecies(), m_nel, 0.0);
542 // iterate over the elements
543 for (size_t m = 0; m < m_nel; m++) {
544 size_t k = 0;
545 // iterate over the phases
546 for (size_t ip = 0; ip < kin.nPhases(); ip++) {
547 ThermoPhase* p = &kin.thermo(ip);
548 size_t mlocal = p->elementIndex(m_elementSymbols[m]);
549 for (size_t kp = 0; kp < p->nSpecies(); kp++) {
550 if (mlocal != npos) {
551 m_atoms(k, m) = p->nAtoms(kp, mlocal);
552 }
553 k++;
554 }
555 }
556 }
557}
558
559int ReactionPathBuilder::init(ostream& logfile, Kinetics& kin)
560{
561 m_transfer.clear();
562 m_elementSymbols.clear();
563 findElements(kin);
564 m_ns = kin.nTotalSpecies();
565 m_nr = kin.nReactions();
566
567 // all reactants / products, even ones appearing on both sides of the
568 // reaction
569 vector<vector<size_t> > allProducts(m_nr);
570 vector<vector<size_t> > allReactants(m_nr);
571 for (size_t i = 0; i < m_nr; i++) {
572 for (size_t k = 0; k < m_ns; k++) {
573 for (int n = 0; n < kin.reactantStoichCoeff(k, i); n++) {
574 allReactants[i].push_back(k);
575 }
576 for (int n = 0; n < kin.productStoichCoeff(k, i); n++) {
577 allProducts[i].push_back(k);
578 }
579 }
580 }
581
582 // m_reac and m_prod exclude indices for species that appear on
583 // both sides of the reaction, so that the diagram contains no loops.
584 m_reac.resize(m_nr);
585 m_prod.resize(m_nr);
586 m_ropf.resize(m_nr);
587 m_ropr.resize(m_nr);
588 m_determinate.resize(m_nr);
589 m_x.resize(m_ns); // not currently used ?
590 m_elatoms.resize(m_nel, m_nr);
591
592 for (size_t i = 0; i < m_nr; i++) {
593 // construct the lists of reactant and product indices, not including
594 // molecules that appear on both sides.
595 m_reac[i].clear();
596 m_prod[i].clear();
597 map<size_t, int> net;
598 size_t nr = allReactants[i].size();
599 size_t np = allProducts[i].size();
600 for (size_t ir = 0; ir < nr; ir++) {
601 net[allReactants[i][ir]]--;
602 }
603 for (size_t ip = 0; ip < np; ip++) {
604 net[allProducts[i][ip]]++;
605 }
606
607 for (size_t k = 0; k < m_ns; k++) {
608 if (net[k] < 0) {
609 size_t nmol = -net[k];
610 for (size_t jr = 0; jr < nmol; jr++) {
611 m_reac[i].push_back(k);
612 }
613 } else if (net[k] > 0) {
614 size_t nmol = net[k];
615 for (size_t jp = 0; jp < nmol; jp++) {
616 m_prod[i].push_back(k);
617 }
618 }
619 }
620
621 size_t nrnet = m_reac[i].size();
622
623 // compute number of atoms of each element in each reaction, excluding
624 // molecules that appear on both sides of the reaction. We only need to
625 // compute this for the reactants, since the elements are conserved.
626 for (size_t n = 0; n < nrnet; n++) {
627 size_t k = m_reac[i][n];
628 for (size_t m = 0; m < m_nel; m++) {
629 m_elatoms(m,i) += m_atoms(k,m);
630 }
631 }
632 }
633
634 // build species groups
635 vector_int comp(m_nel);
636 m_sgroup.resize(m_ns);
637 for (size_t j = 0; j < m_ns; j++) {
638 for (size_t m = 0; m < m_nel; m++) {
639 comp[m] = int(m_atoms(j,m));
640 }
641 m_sgroup[j] = Group(comp);
642 }
643
644 // determine whether or not the reaction is "determinate", meaning that
645 // there is no ambiguity about which reactant is the source for any element
646 // in any product. This is false if more than one reactant contains a given
647 // element, *and* more than one product contains the element. In this case,
648 // additional information is needed to determine the partitioning of the
649 // reactant atoms of that element among the products.
650 for (size_t i = 0; i < m_nr; i++) {
651 size_t nr = m_reac[i].size();
652 size_t np = m_prod[i].size();
653 m_determinate[i] = true;
654 for (size_t m = 0; m < m_nel; m++) {
655 int nar = 0;
656 int nap = 0;
657 for (size_t j = 0; j < nr; j++) {
658 if (m_atoms(m_reac[i][j],m) > 0) {
659 nar++;
660 }
661 }
662 for (size_t j = 0; j < np; j++) {
663 if (m_atoms(m_prod[i][j],m) > 0) {
664 nap++;
665 }
666 }
667 if (nar > 1 && nap > 1) {
668 m_determinate[i] = false;
669 break;
670 }
671 }
672 }
673
674 findGroups(logfile, kin);
675 return 1;
676}
677
678string reactionLabel(size_t i, size_t kr, size_t nr,
679 const std::vector<size_t>& slist, const Kinetics& s)
680{
681 string label = "";
682 for (size_t j = 0; j < nr; j++) {
683 if (j != kr) {
684 label += " + "+ s.kineticsSpeciesName(slist[j]);
685 }
686 }
687 if (ba::starts_with(s.reactionTypeStr(i), "three-body")) {
688 label += " + M ";
689 } else if (ba::starts_with(s.reactionTypeStr(i), "falloff")) {
690 label += " (+ M)";
691 }
692 return label;
693}
694
695int ReactionPathBuilder::build(Kinetics& s, const string& element,
696 ostream& output, ReactionPathDiagram& r, bool quiet)
697{
698 map<size_t, int> warn;
699 doublereal threshold = 0.0;
700 size_t m = m_enamemap[element]-1;
701 r.element = element;
702 if (m == npos) {
703 return -1;
704 }
705
706 s.getFwdRatesOfProgress(m_ropf.data());
707 s.getRevRatesOfProgress(m_ropr.data());
708
709 // species explicitly included or excluded
710 vector<string>& in_nodes = r.included();
711 vector<string>& out_nodes = r.excluded();
712
713 vector_int status(s.nTotalSpecies(), 0);
714 for (size_t ni = 0; ni < in_nodes.size(); ni++) {
715 status[s.kineticsSpeciesIndex(in_nodes[ni])] = 1;
716 }
717 for (size_t ne = 0; ne < out_nodes.size(); ne++) {
718 status[s.kineticsSpeciesIndex(out_nodes[ne])] = -1;
719 }
720
721 for (size_t i = 0; i < m_nr; i++) {
722 double ropf = m_ropf[i];
723 double ropr = m_ropr[i];
724
725 // loop over reactions involving element m
726 if (m_elatoms(m, i) > 0) {
727 size_t nr = m_reac[i].size();
728 size_t np = m_prod[i].size();
729
730 for (size_t kr = 0; kr < nr; kr++) {
731 size_t kkr = m_reac[i][kr];
732 string fwdlabel = reactionLabel(i, kr, nr, m_reac[i], s);
733
734 for (size_t kp = 0; kp < np; kp++) {
735 size_t kkp = m_prod[i][kp];
736 string revlabel = "";
737 for (size_t j = 0; j < np; j++) {
738 if (j != kp) {
739 revlabel += " + "+ s.kineticsSpeciesName(m_prod[i][j]);
740 }
741 }
742 if (ba::starts_with(s.reactionTypeStr(i), "three-body")) {
743 revlabel += " + M ";
744 } else if (ba::starts_with(s.reactionTypeStr(i), "falloff")) {
745 revlabel += " (+ M)";
746 }
747
748 // calculate the flow only for pairs that are not the same
749 // species, both contain atoms of element m, and both are
750 // allowed to appear in the diagram
751 if ((kkr != kkp) && (m_atoms(kkr,m) > 0
752 && m_atoms(kkp,m) > 0)
753 && status[kkr] >= 0 && status[kkp] >= 0) {
754 // if neither species contains the full number of atoms
755 // of element m in the reaction, then we must consider
756 // the type of reaction to determine which reactant
757 // species was the source of a given m-atom in the
758 // product
759 double f;
760 if ((m_atoms(kkp,m) < m_elatoms(m, i)) &&
761 (m_atoms(kkr,m) < m_elatoms(m, i))) {
762 map<size_t, map<size_t, Group> >& g = m_transfer[i];
763 if (g.empty()) {
764 if (!warn[i] && !quiet) {
765 output << endl;
766 output << "*************** REACTION IGNORED ***************" << endl;
767 output << "Warning: no rule to determine partitioning of " << element
768 << endl << " in reaction " << s.reactionString(i) << "." << endl
769 << "*************** REACTION IGNORED **************" << endl;
770 output << endl;
771 warn[i] = 1;
772 }
773 f = 0.0;
774 } else {
775 if (!g[kr][kp]) {
776 f = 0.0;
777 } else {
778 f = g[kr][kp].nAtoms(m);
779 }
780 }
781 } else {
782 // no ambiguity about where the m-atoms come from or
783 // go to. Either all reactant m atoms end up in one
784 // product, or only one reactant contains all the
785 // m-atoms. In either case, the number of atoms
786 // transferred is given by the same expression.
787 f = m_atoms(kkp,m) * m_atoms(kkr,m) / m_elatoms(m, i);
788 }
789
790 double fwd = ropf*f;
791 double rev = ropr*f;
792 bool force_incl = ((status[kkr] == 1) || (status[kkp] == 1));
793
794 bool fwd_incl = ((fwd > threshold) ||
795 (fwd > 0.0 && force_incl));
796 bool rev_incl = ((rev > threshold) ||
797 (rev > 0.0 && force_incl));
798 if (fwd_incl || rev_incl) {
799 if (!r.hasNode(kkr)) {
800 r.addNode(kkr, s.kineticsSpeciesName(kkr), m_x[kkr]);
801 }
802 if (!r.hasNode(kkp)) {
803 r.addNode(kkp, s.kineticsSpeciesName(kkp), m_x[kkp]);
804 }
805 }
806 if (fwd_incl) {
807 r.linkNodes(kkr, kkp, int(i), fwd, fwdlabel);
808 }
809 if (rev_incl) {
810 r.linkNodes(kkp, kkr, -int(i), rev, revlabel);
811 }
812 }
813 }
814 }
815 }
816 }
817 return 1;
818}
819
820}
Classes for reaction path analysis.
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
doublereal netFlow(size_t k1, size_t k2)
The net flow from node k1 to node k2.
Definition: ReactionPath.h:178
void exportToDot(std::ostream &s)
Export the reaction path diagram.
virtual ~ReactionPathDiagram()
Destructor.
doublereal flow(size_t k1, size_t k2)
The one-way flow from node k1 to node k2.
Definition: ReactionPath.h:183
Nodes in reaction path graphs.
Definition: ReactionPath.h:27
std::string name
Label on graph.
Definition: ReactionPath.h:38
bool visible
Visible on graph;.
Definition: ReactionPath.h:40
void addPath(Path *path)
add a path to or from this node
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
std::vector< int > vector_int
Vector of ints.
Definition: ct_defs.h:186
void warn(const std::string &warning, const std::string &method, const std::string &msg, const Args &... args)
Print a generic warning raised from method.
Definition: global.h:229
This file defines some constants used to specify reaction types.