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