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