Cantera  2.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 // Copyright 2001 California Institute of Technology
6 
10 #include "cantera/kinetics/Group.h"
11 
12 using namespace std;
13 
14 namespace Cantera
15 {
16 
17 /// add a path to or from this node
18 void SpeciesNode::addPath(Path* path)
19 {
20  m_paths.push_back(path);
21  if (path->begin() == this) {
22  m_out += path->flow();
23  } else if (path->end() == this) {
24  m_in += path->flow();
25  } else {
26  throw CanteraError("addPath","path added to wrong node");
27  }
28 }
29 
30 void SpeciesNode::printPaths()
31 {
32  for (size_t i = 0; i < m_paths.size(); i++) {
33  cout << m_paths[i]->begin()->name << " --> "
34  << m_paths[i]->end()->name << ": "
35  << m_paths[i]->flow() << endl;
36  }
37 }
38 
39 
40 /**
41  * Construct a path connecting two species nodes.
42  */
43 Path::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 
50 
51 /**
52  * add a reaction to the path. Increment the flow from this
53  * reaction, the total flow, and the flow associated with this
54  * label.
55  */
56 void Path::addReaction(size_t rxnNumber, doublereal value,
57  string label)
58 {
59  m_rxn[rxnNumber] += value;
60  m_total += value;
61  if (label != "") {
62  m_label[label] += value;
63  }
64 }
65 
66 
67 /**
68  * Write the label for a path connecting two species, indicating
69  * the percent of the total flow due to each reaction.
70  */
71 void Path::writeLabel(ostream& s, doublereal threshold)
72 {
73  size_t nn = m_label.size();
74  if (nn == 0) {
75  return;
76  }
77  doublereal v;
78  map<string, doublereal>::const_iterator i = m_label.begin();
79  for (; i != m_label.end(); ++i) {
80  v = i->second/m_total;
81  if (nn == 1) {
82  s << i->first << "\\l";
83  } else if (v > threshold) {
84  s << i->first;
85  int percent = int(100*v + 0.5);
86  if (percent < 100) {
87  s << " (" << percent << "%)\\l";
88  } else {
89  s << "\\l";
90  }
91  }
92  }
93 }
94 
95 
96 /**
97  * Default constructor.
98  */
100 {
101  name = "reaction_paths";
102  m_flxmax = 0.0;
103  bold_color = "blue";
104  normal_color = "steelblue";
105  dashed_color = "gray";
106  dot_options = "center=1;";
107  m_font = "Helvetica"; // RXNPATH_FONT;
108  bold_min = 0.2;
109  dashed_max = 0.0;
110  label_min = 0.0;
111  threshold = 0.005;
112  flow_type = NetFlow;
113  scale = -1;
114  x_size = -1.0;
115  y_size = -1.0;
116  arrow_width = -5.0;
117  show_details = false;
118  arrow_hue = 0.6666;
119  title = "";
120  m_local = npos;
121 }
122 
123 
124 /**
125  * Destructor. Deletes all nodes and paths in the diagram.
126  */
128 {
129  // delete the nodes
130  map<size_t, SpeciesNode*>::const_iterator i = m_nodes.begin();
131  for (; i != m_nodes.end(); ++i) {
132  delete i->second;
133  }
134 
135  // delete the paths
136  size_t nn = nPaths();
137  for (size_t n = 0; n < nn; n++) {
138  delete m_pathlist[n];
139  }
140 }
141 
142 
143 vector_int ReactionPathDiagram::reactions()
144 {
145  size_t i, npaths = nPaths();
146  doublereal flmax = 0.0, flxratio;
147  Path* p;
148  for (i = 0; i < npaths; i++) {
149  p = path(i);
150  if (p->flow() > flmax) {
151  flmax = p->flow();
152  }
153  }
154  m_rxns.clear();
155  for (i = 0; i < npaths; i++) {
156  p = path(i);
157  const Path::rxn_path_map& rxns = p->reactionMap();
158  Path::rxn_path_map::const_iterator m = rxns.begin();
159  for (; m != rxns.end(); ++m) {
160  flxratio = m->second/flmax;
161  if (flxratio > threshold) {
162  m_rxns[m->first] = 1;
163  }
164  }
165  }
166  vector_int r;
167  map<size_t, int>::const_iterator begin = m_rxns.begin();
168  for (; begin != m_rxns.end(); ++begin) {
169  r.push_back(int(begin->first));
170  }
171  return r;
172 }
173 
174 void ReactionPathDiagram::add(ReactionPathDiagram& d)
175 {
176  // doublereal f1, f2;
177  // int nnodes = nNodes();
178  // if (nnodes != d.nNodes()) {
179  // throw CanteraError("ReactionPathDiagram::add",
180  // "number of nodes must be the same");
181  // }
182  size_t np = nPaths();
183  size_t n, k1, k2;
184  Path* p = 0;
185  for (n = 0; n < np; n++) {
186  p = path(n);
187  k1 = p->begin()->number;
188  k2 = p->end()->number;
189  p->setFlow(p->flow() + d.flow(k1,k2));
190  }
191 }
192 
193 void ReactionPathDiagram::findMajorPaths(doublereal athreshold, size_t lda,
194  doublereal* a)
195 {
196  size_t nn = nNodes();
197  size_t n, m, k1, k2;
198  doublereal fl, netmax = 0.0;
199  for (n = 0; n < nn; n++) {
200  for (m = n+1; m < nn; m++) {
201  k1 = m_speciesNumber[n];
202  k2 = m_speciesNumber[m];
203  fl = fabs(netFlow(k1,k2));
204  if (fl > netmax) {
205  netmax = fl;
206  }
207  }
208  }
209  for (n = 0; n < nn; n++) {
210  for (m = n+1; m < nn; m++) {
211  k1 = m_speciesNumber[n];
212  k2 = m_speciesNumber[m];
213  fl = fabs(netFlow(k1,k2));
214  if (fl > athreshold*netmax) {
215  a[lda*k1 + k2] = 1;
216  }
217  }
218  }
219 }
220 
221 void ReactionPathDiagram::writeData(ostream& s)
222 {
223  doublereal f1, f2;
224  size_t nnodes = nNodes();
225  size_t i1, i2, k1, k2;
226  s << title << endl;
227  for (i1 = 0; i1 < nnodes; i1++) {
228  k1 = m_speciesNumber[i1];
229  s << m_nodes[k1]->name << " ";
230  }
231  s << endl;
232  for (i1 = 0; i1 < nnodes; i1++) {
233  k1 = m_speciesNumber[i1];
234  for (i2 = i1+1; i2 < nnodes; i2++) {
235  k2 = m_speciesNumber[i2];
236  f1 = flow(k1, k2);
237  f2 = flow(k2, k1);
238  //if (f1 > 0.001 || f2 > 0.001) {
239  s << m_nodes[k1]->name << " " << m_nodes[k2]->name
240  << " " << f1 << " " << -f2 << endl;
241  //}
242  }
243  }
244 }
245 
246 
247 /**
248  * Export the reaction path diagram. This method writes to stream
249  * \c s the commands for the 'dot' program in the \c GraphViz
250  * package from AT&T. (GraphViz may be downloaded from
251  * www.graphviz.org.)
252  *
253  * To generate a postscript reaction path diagram from the
254  * output of this method saved in file paths.dot, for example, give
255  * the command:
256  * \code
257  * dot -Tps paths.dot > paths.ps
258  * \endcode
259  * To generate a GIF image, replace -Tps with -Tgif
260  */
262 {
263  doublereal flxratio, flmax = 0.0, lwidth;
264  //s.flags(std::ios_base::showpoint+std::ios_base::fixed);
265  s.precision(3);
266 
267  // a directed graph
268  s << "digraph " << name << " {" << endl;
269 
270  // the graph will be no larger than x_size, y_size
271  if (x_size > 0.0) {
272  if (y_size < 0.0) {
273  y_size = x_size;
274  }
275  s << "size = \""
276  << x_size << ","
277  << y_size << "\";"
278  << endl;
279  }
280 
281  //s << "color = white;" << endl;
282  if (dot_options != "") {
283  s << dot_options << endl;
284  }
285 
286  Path* p;
287 
288  size_t kbegin, kend, i1, i2, k1, k2;
289  doublereal flx;
290 
291  // draw paths representing net flows
292  if (flow_type == NetFlow) {
293 
294  // if no scale was specified, normalize
295  // net flows by the maximum net flow
296  if (scale <= 0.0) {
297  for (i1 = 0; i1 < nNodes(); i1++) {
298  k1 = m_speciesNumber[i1];
299  node(k1)->visible = false;
300  for (i2 = i1+1; i2 < nNodes(); i2++) {
301  k2 = m_speciesNumber[i2];
302  flx = netFlow(k1, k2);
303  if (flx < 0.0) {
304  flx = -flx;
305  }
306  if (flx > flmax) {
307  flmax = flx;
308  }
309  }
310  }
311  } else {
312  flmax = scale;
313  }
314 
315  if (flmax < 1.e-10) {
316  flmax = 1.e-10;
317  }
318 
319  // loop over all unique pairs of nodes
320 
321  for (i1 = 0; i1 < nNodes(); i1++) {
322  k1 = m_speciesNumber[i1];
323  for (i2 = i1+1; i2 < nNodes(); i2++) {
324  k2 = m_speciesNumber[i2];
325  flx = netFlow(k1, k2);
326  if (m_local != npos) {
327  if (k1 != m_local && k2 != m_local) {
328  flx = 0.0;
329  }
330  }
331  if (flx != 0.0) {
332  // set beginning and end of the path based on the
333  // sign of the net flow
334 
335  if (flx > 0.0) {
336  kbegin = k1;
337  kend = k2;
338  flxratio = flx/flmax;
339  } else {
340  kbegin = k2;
341  kend = k1;
342  flxratio = -flx/flmax;
343  }
344 
345  // write out path specification if the net flow
346  // is greater than the threshold
347 
348  if (flxratio >= threshold) {
349  // make nodes visible
350  node(kbegin)->visible = true;
351  node(kend)->visible = true;
352 
353  s << "s" << kbegin << " -> s" << kend;
354 
355  if (arrow_width < 0) {
356  lwidth = 1.0 - 4.0
357  * log10(flxratio/threshold)/log10(threshold) + 1.0;
358  s << "[fontname=\""+m_font+"\", style=\"setlinewidth("
359  << lwidth << ")\"";
360  s << ", arrowsize="
361  << std::min(6.0, 0.5*lwidth);
362  } else {
363  s << ", style=\"setlinewidth("
364  << arrow_width << ")\"";
365  s << ", arrowsize=" << flxratio + 1;
366  }
367 
368  doublereal hue = 0.7;
369  doublereal bright = 0.9;
370  s << ", color=" << "\"" << hue << ", "
371  << flxratio + 0.5
372  << ", " << bright << "\"" << endl;
373 
374  if (flxratio > label_min) {
375  s << ", label=\" " << flxratio;
376  if (show_details) {
377  if (flow(kbegin, kend) > 0.0) {
378  s << "\\l fwd: "
379  << flow(kbegin, kend)/flmax << "\\l";
380  path(kbegin, kend)->writeLabel(s);
381  }
382  if (flow(kend, kbegin) > 0.0) {
383  s << " \\l rev: "
384  << flow(kend,kbegin)/flmax << "\\l";
385  path(kend, kbegin)->writeLabel(s);
386  }
387  }
388  s << "\"";
389  }
390  s << "];" << endl;
391  }
392  }
393  }
394  }
395  }
396 
397  else {
398  for (size_t i = 0; i < nPaths(); i++) {
399  p = path(i);
400  if (p->flow() > flmax) {
401  flmax = p->flow();
402  }
403  }
404 
405  for (size_t i = 0; i < nPaths(); i++) {
406  p = path(i);
407  flxratio = p->flow()/flmax;
408  if (m_local != npos) {
409  if (p->begin()->number != m_local
410  && p->end()->number != m_local) {
411  flxratio = 0.0;
412  }
413  }
414  if (flxratio > threshold) {
415  p->begin()->visible = true;
416  p->end()->visible = true;
417  s << "s" << p->begin()->number
418  << " -> s" << p->end()->number;
419 
420  if (arrow_width < 0) {
421  lwidth = 1.0 - 4.0 * log10(flxratio/threshold)/log10(threshold)
422  + 1.0;
423  s << "[fontname=\""+m_font+"\", style=\"setlinewidth("
424  //<< 1.0 - arrow_width*flxratio
425  << lwidth
426  << ")\"";
427  s << ", arrowsize="
428  << std::min(6.0, 0.5*lwidth); // 1 - arrow_width*flxratio;
429  } else {
430  s << ", style=\"setlinewidth("
431  << arrow_width << ")\"";
432  s << ", arrowsize=" << flxratio + 1;
433  }
434  doublereal hue = 0.7; //2.0/(1.0 + pow(log10(flxratio),2)) ;
435  doublereal bright = 0.9;
436  s << ", color=" << "\"" << hue << ", " << flxratio + 0.5
437  << ", " << bright << "\"" << endl;
438 
439  if (flxratio > label_min) {
440  s << ", label = \" " << flxratio;
441  if (show_details) {
442  s << "\\l";
443  p->writeLabel(s);
444  }
445  s << "\"";
446  }
447  s << "];" << endl;
448  }
449  }
450  }
451  s.precision(2);
452  map<size_t, SpeciesNode*>::const_iterator b = m_nodes.begin();
453  for (; b != m_nodes.end(); ++b) {
454  if (b->second->visible) {
455  s << "s" << b->first << " [ fontname=\""+m_font+"\", label=\"" << b->second->name
456  //<< " \\n " << b->second->value
457  << "\"];" << endl;
458  }
459  }
460  s << " label = " << "\"" << "Scale = "
461  << flmax << "\\l " << title << "\";" << endl; //created with Cantera (www.cantera.org)\\l\";"
462  s << " fontname = \""+m_font+"\";" << endl << "}" << endl;
463 }
464 
465 
466 void ReactionPathDiagram::addNode(size_t k, string nm, doublereal x)
467 {
468  if (!m_nodes[k]) {
469  m_nodes[k] = new SpeciesNode;
470  m_nodes[k]->number = k;
471  m_nodes[k]->name = nm;
472  m_nodes[k]->value = x;
473  m_speciesNumber.push_back(k);
474  }
475 }
476 
477 void ReactionPathDiagram::linkNodes(size_t k1, size_t k2, size_t rxn,
478  doublereal value, string legend)
479 {
480  SpeciesNode* begin = m_nodes[k1];
481  SpeciesNode* end = m_nodes[k2];
482  Path* ff = m_paths[k1][k2];
483  if (!ff) {
484  ff= new Path(begin, end);
485  m_paths[k1][k2] = ff;
486  m_pathlist.push_back(ff);
487  }
488  ff->addReaction(rxn, value, legend);
489  m_rxns[rxn] = 1;
490  if (ff->flow() > m_flxmax) {
491  m_flxmax = ff->flow();
492  }
493 }
494 
495 std::vector<size_t> ReactionPathDiagram::species()
496 {
497  return m_speciesNumber;
498 }
499 
500 
501 /**
502  * analyze a reaction to determine which reactants lead to which products.
503  */
504 int ReactionPathBuilder::findGroups(ostream& logfile, Kinetics& s)
505 {
506  m_groups.resize(m_nr);
507 
508  for (size_t i = 0; i < m_nr; i++) { // loop over reactions
509  logfile << endl << "Reaction " << i+1 << ": "
510  << s.reactionString(i);
511 
512  size_t nrnet = m_reac[i].size();
513  size_t npnet = m_prod[i].size();
514  const std::vector<size_t>& r = s.reactants(i);
515  const std::vector<size_t>& p = s.products(i);
516 
517  size_t nr = r.size();
518  size_t np = p.size();
519 
520  Group b0, b1, bb;
521 
522  vector<string>& e = m_elementSymbols;
523 
524  const vector<grouplist_t>& rgroups = s.reactantGroups(i);
525  const vector<grouplist_t>& pgroups = s.productGroups(i);
526 
527  if (m_determinate[i]) {
528  logfile << " ... OK." << endl;
529  }
530 
531  else if (rgroups.size() > 0) {
532  logfile << " ... specified groups." << endl;
533  size_t nrg = rgroups.size();
534  size_t npg = pgroups.size();
535  size_t kr, kp, ngrpr, ngrpp;
536  Group gr, gp;
537 
538  if (nrg != nr || npg != np) {
539  return -1;
540  }
541 
542  // loop over reactants
543  for (size_t igr = 0; igr < nrg; igr++) {
544  kr = r[igr];
545  ngrpr = rgroups[igr].size();
546 
547  // loop over products
548  for (size_t igp = 0; igp < npg; igp++) {
549  kp = p[igp];
550  ngrpp = pgroups[igp].size();
551 
552  // loop over pairs of reactant and product groups
553  for (size_t kgr = 0; kgr < ngrpr; kgr++) {
554  gr = Group(rgroups[igr][kgr]);
555  for (size_t kgp = 0; kgp < ngrpp; kgp++) {
556  gp = Group(pgroups[igp][kgp]);
557  if (gr == gp) {
558  m_transfer[i][kr][kp] = gr;
559  }
560  }
561  }
562  }
563  }
564  }
565 
566  else if (nrnet == 2 && npnet == 2) {
567  // indices for the two reactants
568  size_t kr0 = m_reac[i][0];
569  size_t kr1 = m_reac[i][1];
570 
571  // indices for the two products
572  size_t kp0 = m_prod[i][0];
573  size_t kp1 = m_prod[i][1];
574 
575  // references to the Group objects representing the
576  // reactants
577  const Group& r0 = m_sgroup[kr0];
578  const Group& r1 = m_sgroup[kr1];
579  const Group& p0 = m_sgroup[kp0];
580  const Group& p1 = m_sgroup[kp1];
581 
582  const Group* group_a0=0, *group_b0=0, *group_c0=0,
583  *group_a1=0, *group_b1=0, *group_c1=0;
584  b0 = p0 - r0;
585  b1 = p1 - r0;
586  if (b0.valid() && b1.valid()) {
587  logfile << " ... ambiguous." << endl;
588  } else if (!b0.valid() && !b1.valid()) {
589  logfile << " ... cannot express as A + BC = AB + C" << endl;
590  } else {
591  logfile << endl;
592  }
593 
594  if (b0.valid()) {
595  if (b0.sign() > 0) {
596  group_a0 = &r0;
597  group_b0 = &b0;
598  group_c0 = &p1;
599  m_transfer[i][kr0][kp0] = r0;
600  m_transfer[i][kr1][kp0] = b0;
601  m_transfer[i][kr1][kp1] = p1;
602  } else {
603  group_a0 = &r1;
604  group_c0 = &p0;
605  b0 *= -1;
606  group_b0 = &b0;
607  m_transfer[i][kr1][kp1] = r1;
608  m_transfer[i][kr0][kp1] = b0;
609  m_transfer[i][kr0][kp0] = p0;
610  }
611  logfile << " ";
612  group_a0->fmt(logfile,e);
613  logfile << " + ";
614  group_b0->fmt(logfile,e);
615  group_c0->fmt(logfile,e);
616  logfile << " = ";
617  group_a0->fmt(logfile,e);
618  group_b0->fmt(logfile,e);
619  logfile << " + ";
620  group_c0->fmt(logfile,e);
621  if (b1.valid()) {
622  logfile << " [<= default] " << endl;
623  } else {
624  logfile << endl;
625  }
626  }
627 
628 
629  if (b1.valid()) {
630  if (b1.sign() > 0) {
631  group_a1 = &r0;
632  group_b1 = &b1;
633  group_c1 = &p0;
634  if (!b0.valid()) {
635  m_transfer[i][kr0][kp1] = r0;
636  m_transfer[i][kr1][kp1] = b0;
637  m_transfer[i][kr1][kp0] = p0;
638  }
639  } else {
640  group_a1 = &r1;
641  group_c1 = &p1;
642  b1 *= -1;
643  group_b1 = &b1;
644  if (!b0.valid()) {
645  m_transfer[i][kr1][kp0] = r1;
646  m_transfer[i][kr0][kp0] = b0;
647  m_transfer[i][kr0][kp1] = p1;
648  }
649  }
650  logfile << " ";
651  group_a1->fmt(logfile,e);
652  logfile << " + ";
653  group_b1->fmt(logfile,e);
654  group_c1->fmt(logfile,e);
655  logfile << " = ";
656  group_a1->fmt(logfile,e);
657  group_b1->fmt(logfile,e);
658  logfile << " + ";
659  group_c1->fmt(logfile,e);
660  logfile << endl;
661  }
662  } else {
663  logfile << "... cannot parse. [ignored]" << endl;
664  }
665  }
666  return 1;
667 }
668 
669 void ReactionPathBuilder::writeGroup(ostream& out, const Group& g)
670 {
671  g.fmt(out, m_elementSymbols);
672 }
673 
674 void ReactionPathBuilder::findElements(Kinetics& kin)
675 {
676 
677  string ename;
678  m_enamemap.clear();
679  m_nel = 0;
680  size_t np = kin.nPhases();
681  ThermoPhase* p;
682  for (size_t i = 0; i < np; i++) {
683  p = &kin.thermo(i);
684  // iterate over the elements in this phase
685  size_t nel = p->nElements();
686  for (size_t m = 0; m < nel; m++) {
687  ename = p->elementName(m);
688 
689  // if no entry is found for this element name, then
690  // it is a new element. In this case, add the name
691  // to the list of names, increment the element count,
692  // and add an entry to the name->(index+1) map.
693  if (m_enamemap.find(ename) == m_enamemap.end()) {
694  m_enamemap[ename] = m_nel + 1;
695  m_elementSymbols.push_back(ename);
696  m_nel++;
697  }
698  }
699  }
700  m_atoms.resize(kin.nTotalSpecies(), m_nel, 0.0);
701  string sym;
702  // iterate over the elements
703  for (size_t m = 0; m < m_nel; m++) {
704  sym = m_elementSymbols[m];
705  size_t k = 0;
706  // iterate over the phases
707  for (size_t ip = 0; ip < np; ip++) {
708  ThermoPhase* p = &kin.thermo(ip);
709  size_t nsp = p->nSpecies();
710  size_t mlocal = p->elementIndex(sym);
711  for (size_t kp = 0; kp < nsp; kp++) {
712  if (mlocal != npos) {
713  m_atoms(k, m) = p->nAtoms(kp, mlocal);
714  }
715  k++;
716  }
717  }
718  }
719 }
720 
721 
722 
723 int ReactionPathBuilder::init(ostream& logfile, Kinetics& kin)
724 {
725  //m_warn.clear();
726  m_transfer.clear();
727 
728  //const Kinetics::thermo_t& ph = kin.thermo();
729 
730  m_elementSymbols.clear();
731  findElements(kin);
732  //m_nel = ph.nElements();
733  m_ns = kin.nTotalSpecies(); //ph.nSpecies();
734  m_nr = kin.nReactions();
735 
736  //for (m = 0; m < m_nel; m++) {
737  // m_elementSymbols.push_back(ph.elementName(m));
738  //}
739 
740  // all reactants / products, even ones appearing on both sides
741  // of the reaction
742  // mod 8/18/01 dgg
743  vector<vector<size_t> > allProducts;
744  vector<vector<size_t> > allReactants;
745  for (size_t i = 0; i < m_nr; i++) {
746  allReactants.push_back(kin.reactants(i));
747  allProducts.push_back(kin.products(i));
748  }
749 
750  // m_reac and m_prod exclude indices for species that appear on
751  // both sides of the reaction, so that the diagram contains no loops.
752 
753  m_reac.resize(m_nr);
754  m_prod.resize(m_nr);
755 
756  m_ropf.resize(m_nr);
757  m_ropr.resize(m_nr);
758  m_determinate.resize(m_nr);
759 
760  m_x.resize(m_ns); // not currently used ?
761  m_elatoms.resize(m_nel, m_nr);
762 
763  size_t nr, np, n, k;
764  size_t nmol;
765  map<size_t, int> net;
766 
767  for (size_t i = 0; i < m_nr; i++) {
768 
769  // construct the lists of reactant and product indices, not
770  // including molecules that appear on both sides.
771 
772  m_reac[i].clear();
773  m_prod[i].clear();
774  net.clear();
775  nr = allReactants[i].size();
776  np = allProducts[i].size();
777  for (size_t ir = 0; ir < nr; ir++) {
778  net[allReactants[i][ir]]--;
779  }
780  for (size_t ip = 0; ip < np; ip++) {
781  net[allProducts[i][ip]]++;
782  }
783 
784  for (k = 0; k < m_ns; k++) {
785  if (net[k] < 0) {
786  nmol = -net[k];
787  for (size_t jr = 0; jr < nmol; jr++) {
788  m_reac[i].push_back(k);
789  }
790  } else if (net[k] > 0) {
791  nmol = net[k];
792  for (size_t jp = 0; jp < nmol; jp++) {
793  m_prod[i].push_back(k);
794  }
795  }
796  }
797 
798  size_t nrnet = m_reac[i].size();
799  // int npnet = m_prod[i].size();
800 
801  // compute number of atoms of each element in each reaction,
802  // excluding molecules that appear on both sides of the
803  // reaction. We only need to compute this for the reactants,
804  // since the elements are conserved.
805 
806  for (n = 0; n < nrnet; n++) {
807  k = m_reac[i][n];
808  for (size_t m = 0; m < m_nel; m++) {
809  m_elatoms(m,i) += m_atoms(k,m); //ph.nAtoms(k,m);
810  }
811  }
812  }
813 
814  // build species groups
815  vector_int comp(m_nel);
816  m_sgroup.resize(m_ns);
817  for (size_t j = 0; j < m_ns; j++) {
818  for (size_t m = 0; m < m_nel; m++) {
819  comp[m] = int(m_atoms(j,m)); //ph.nAtoms(j,m));
820  }
821  m_sgroup[j] = Group(comp);
822  }
823 
824 
825  // determine whether or not the reaction is "determinate", meaning
826  // that there is no ambiguity about which reactant is the source for
827  // any element in any product. This is false if more than one
828  // reactant contains a given element, *and* more than one product
829  // contains the element. In this case, additional information is
830  // needed to determine the partitioning of the reactant atoms of
831  // that element among the products.
832 
833  int nar, nap;
834  for (size_t i = 0; i < m_nr; i++) {
835  nr = m_reac[i].size();
836  np = m_prod[i].size();
837  m_determinate[i] = true;
838  for (size_t m = 0; m < m_nel; m++) {
839  nar = 0;
840  nap = 0;
841  for (size_t j = 0; j < nr; j++) {
842  // if (ph.nAtoms(m_reac[i][j],m) > 0) nar++;
843  if (m_atoms(m_reac[i][j],m) > 0) {
844  nar++;
845  }
846  }
847  for (size_t j = 0; j < np; j++) {
848  if (m_atoms(m_prod[i][j],m) > 0) {
849  nap++;
850  }
851  }
852  if (nar > 1 && nap > 1) {
853  m_determinate[i] = false;
854  break;
855  }
856  }
857  }
858 
859  findGroups(logfile, kin);
860  return 1;
861 }
862 
863 string reactionLabel(size_t i, size_t kr, size_t nr,
864  const std::vector<size_t>& slist, const Kinetics& s)
865 {
866 
867  //int np = s.nPhases();
868  string label = "";
869  for (size_t l = 0; l < nr; l++) {
870  if (l != kr) {
871  label += " + "+ s.kineticsSpeciesName(slist[l]);
872  }
873  }
874  if (s.reactionType(i) == THREE_BODY_RXN) {
875  label += " + M ";
876  } else if (s.reactionType(i) == FALLOFF_RXN) {
877  label += " (+ M)";
878  }
879  return label;
880 }
881 
882 
883 int ReactionPathBuilder::build(Kinetics& s,
884  string element, ostream& output, ReactionPathDiagram& r, bool quiet)
885 {
886  doublereal f, ropf, ropr, fwd, rev;
887  string fwdlabel, revlabel;
888  map<size_t, int> warn;
889 
890  doublereal threshold = 0.0;
891  bool fwd_incl, rev_incl, force_incl;
892 
893  // const Kinetics::thermo_t& ph = s.thermo();
894  size_t m = m_enamemap[element]-1; //ph.elementIndex(element);
895 
896  r.element = element;
897  if (m == npos) {
898  return -1;
899  }
900 
901  s.getFwdRatesOfProgress(DATA_PTR(m_ropf));
902  s.getRevRatesOfProgress(DATA_PTR(m_ropr));
903 
904  //ph.getMoleFractions(m_x.begin());
905 
906  //doublereal sum = 0.0;
907  //for (k = 0; k < kk; k++) {
908  // sum += m_x[k] * ph.nAtoms(k,m);
909  //}
910  //sum *= ph.molarDensity();
911 
912  // species explicitly included or excluded
913  vector<string>& in_nodes = r.included();
914  vector<string>& out_nodes = r.excluded();
915 
916  vector_int status;
917  status.resize(s.nTotalSpecies(), 0);
918  for (size_t ni = 0; ni < in_nodes.size(); ni++) {
919  status[s.kineticsSpeciesIndex(in_nodes[ni])] = 1;
920  }
921  for (size_t ne = 0; ne < out_nodes.size(); ne++) {
922  status[s.kineticsSpeciesIndex(out_nodes[ne])] = -1;
923  }
924 
925  for (size_t i = 0; i < m_nr; i++) {
926  ropf = m_ropf[i];
927  ropr = m_ropr[i];
928 
929  // loop over reactions involving element m
930  if (m_elatoms(m, i) > 0) {
931  size_t nr = m_reac[i].size();
932  size_t np = m_prod[i].size();
933 
934  for (size_t kr = 0; kr < nr; kr++) {
935  size_t kkr = m_reac[i][kr];
936 
937  fwdlabel = reactionLabel(i, kr, nr, m_reac[i], s);
938 
939  for (size_t kp = 0; kp < np; kp++) {
940  size_t kkp = m_prod[i][kp];
941  revlabel = "";
942  for (size_t l = 0; l < np; l++) {
943  if (l != kp) {
944  revlabel += " + "+ s.kineticsSpeciesName(m_prod[i][l]);
945  }
946  }
947  if (s.reactionType(i) == THREE_BODY_RXN) {
948  revlabel += " + M ";
949  } else if (s.reactionType(i) == FALLOFF_RXN) {
950  revlabel += " (+ M)";
951  }
952 
953 
954  // calculate the flow only for pairs that are
955  // not the same species, both contain atoms of
956  // element m, and both are allowed to appear in
957  // the diagram
958 
959  if ((kkr != kkp) && (m_atoms(kkr,m) > 0
960  && m_atoms(kkp,m) > 0)
961  && status[kkr] >= 0 && status[kkp] >= 0) {
962 
963  // if neither species contains the full
964  // number of atoms of element m in the
965  // reaction, then we must consider the
966  // type of reaction to determine which
967  // reactant species was the source of a
968  // given m-atom in the product
969 
970  if ((m_atoms(kkp,m) < m_elatoms(m, i)) &&
971  (m_atoms(kkr,m) < m_elatoms(m, i))) {
972  map<size_t, map<size_t, Group> >& g = m_transfer[i];
973  if (g.empty()) {
974  if (!warn[i]) {
975  if (!quiet) {
976  output << endl;
977  output << "*************** REACTION IGNORED ***************" << endl;
978  output << "Warning: no rule to determine partitioning of " << element
979  << endl << " in reaction " << s.reactionString(i) << "." << endl
980  << "*************** REACTION IGNORED **************" << endl;
981  output << endl;
982  warn[i] = 1;
983  }
984  }
985  f = 0.0;
986  } else {
987  if (!g[kkr][kkp]) {
988  f = 0.0;
989  } else {
990  f = g[kkr][kkp].nAtoms(m);
991  }
992  }
993  }
994 
995  // no ambiguity about where the m-atoms come
996  // from or go to. Either all reactant m atoms
997  // end up in one product, or only one reactant
998  // contains all the m-atoms. In either case,
999  // the number of atoms transferred is given by
1000  // the same expression.
1001 
1002  else {
1003  f = m_atoms(kkp,m) * m_atoms(kkr,m) / m_elatoms(m, i);
1004  }
1005 
1006  fwd = ropf*f;
1007  rev = ropr*f;
1008  force_incl = ((status[kkr] == 1) || (status[kkp] == 1));
1009 
1010  fwd_incl = ((fwd > threshold) ||
1011  (fwd > 0.0 && force_incl));
1012  rev_incl = ((rev > threshold) ||
1013  (rev > 0.0 && force_incl));
1014  if (fwd_incl || rev_incl) {
1015  if (!r.hasNode(kkr)) {
1016  r.addNode(kkr, s.kineticsSpeciesName(kkr), m_x[kkr]);
1017  }
1018  if (!r.hasNode(kkp)) {
1019  r.addNode(kkp, s.kineticsSpeciesName(kkp), m_x[kkp]);
1020  }
1021  }
1022  if (fwd_incl) {
1023  r.linkNodes(kkr, kkp, int(i), fwd, fwdlabel);
1024  }
1025  if (rev_incl) {
1026  r.linkNodes(kkp, kkr, -int(i), rev, revlabel);
1027  }
1028  }
1029  }
1030  }
1031  }
1032  }
1033  return 1;
1034 }
1035 
1036 
1037 }