ROOTPWA
isobarDecayTopology.cc
Go to the documentation of this file.
1 
2 //
3 // Copyright 2010
4 //
5 // This file is part of rootpwa
6 //
7 // rootpwa is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // rootpwa is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with rootpwa. If not, see <http://www.gnu.org/licenses/>.
19 //
21 //-------------------------------------------------------------------------
22 // File and Version Information:
23 // $Rev:: $: revision of last commit
24 // $Author:: $: author of last commit
25 // $Date:: $: date of last commit
26 //
27 // Description:
28 // container class that holds all external information for
29 // amplitude calculation of isobar decay
30 //
31 //
32 // Author List:
33 // Boris Grube TUM (original author)
34 //
35 //
36 //-------------------------------------------------------------------------
37 
38 #include "diffractiveDissVertex.h"
39 #include "isobarDecayTopology.h"
40 #include "particleDataTable.h"
41 #include "spinUtils.hpp"
42 
43 
44 using namespace std;
45 using namespace boost;
46 using namespace rpwa;
47 
48 
49 bool isobarDecayTopology::_debug = false;
50 
51 
52 isobarDecayTopology::isobarDecayTopology()
53  : decayTopology()
54 { }
55 
56 
58  const vector<isobarDecayVertexPtr>& isobarDecayVertices,
59  const vector<particlePtr>& fsParticles,
60  const bool performTopologyCheck)
61  : decayTopology()
62 {
63  constructDecay(productionVertex, isobarDecayVertices, fsParticles, performTopologyCheck);
64 }
65 
66 
68  const vector<interactionVertexPtr>& isobarDecayVertices,
69  const vector<particlePtr>& fsParticles,
70  const bool performTopologyCheck)
71  : decayTopology()
72 {
73  constructDecay(productionVertex, isobarDecayVertices, fsParticles, performTopologyCheck);
74 }
75 
76 
78  : decayTopology()
79 {
80  *this = topo;
81 }
82 
83 
85  : decayTopology()
86 {
87  *this = topo;
88 }
89 
90 
92 { }
93 
94 
97 {
98  if (this != &topo) {
101  }
102  return *this;
103 }
104 
105 
108 {
109  if (this != &topo) {
112  }
113  return *this;
114 }
115 
116 
118 isobarDecayTopology::doClone(const bool cloneFsParticles,
119  const bool cloneProdKinematics) const
120 {
121  if (_debug)
122  printDebug << "cloning isobar decay topology '" << name() << "'; "
123  << ((cloneFsParticles ) ? "in" : "ex") << "cluding final-state particles, "
124  << ((cloneProdKinematics) ? "in" : "ex") << "cluding production kinematics particles"
125  << endl;
126  decayTopology* topoClone = decayTopology::doClone(cloneFsParticles, cloneProdKinematics);
127  isobarDecayTopology* isobarTopoClone = new isobarDecayTopology(*topoClone);
128  isobarTopoClone->buildIsobarVertexArray();
129  return isobarTopoClone;
130 }
131 
132 
133 void
135 {
137  _isobarVertices.clear();
138 }
139 
140 
143  const vector<isobarDecayVertexPtr>& isobarDecayVertices,
144  const vector<particlePtr>& fsParticles,
145  const bool performTopologyCheck)
146 {
147  const unsigned int nmbVert = isobarDecayVertices.size();
148  if (_debug)
149  printDebug << "constructing isobar decay topology with "
150  << fsParticles.size() << " final-state particles and "
151  << nmbVert << " isobar decay vertices" << endl;
152  vector<interactionVertexPtr> intVertices(nmbVert);
153  for (unsigned int i = 0; i < nmbVert; ++i)
154  intVertices[i] = isobarDecayVertices[i];
155  decayTopology::constructDecay(productionVertex, intVertices, fsParticles, performTopologyCheck);
156  // copy sorted vertices
158  if (performTopologyCheck and (nmbDecayVertices() != nmbVert)) {
159  printErr << "number of interaction vertices = " << nmbDecayVertices()
160  << " does not match number of vertices given in parameter array = " << nmbVert
161  << ". aborting." << endl;
162  throw;
163  }
164  return *this;
165 }
166 
167 
170  const vector<interactionVertexPtr>& isobarDecayVertices,
171  const vector<particlePtr>& fsParticles,
172  const bool performTopologyCheck)
173 {
174  const unsigned int nmbVert = isobarDecayVertices.size();
175  vector<isobarDecayVertexPtr> decayVertices(nmbVert);
176  for (unsigned int i = 0; i < nmbVert; ++i) {
177  decayVertices[i] = dynamic_pointer_cast<isobarDecayVertex>(isobarDecayVertices[i]);
178  if (not decayVertices[i]) {
179  printErr << "interaction vertex[" << i << "] is not an isobarDecayVertex. aborting." << endl;
180  throw;
181  }
182  }
183  return constructDecay(productionVertex, decayVertices, fsParticles, performTopologyCheck);
184 }
185 
186 
187 bool
189 {
190  // perform basic checks of topology
191  bool topologyIsOkay = decayTopology::checkTopology();
192  // check that decay topology is a tree of isobar decays
193  for (unsigned int i = 0; i < nmbDecayVertices(); ++i) {
194  const nodeDesc& nd = node(_isobarVertices[i]);
195  // check that each isobar decay node has exactly 1 incoming edge (isobar)
196  const unsigned int nmbIn = nmbInEdges(_isobarVertices[i]);
197  if (nmbIn != 1) {
198  printWarn << "number of incoming edges of node[" << nd << "] is "
199  << nmbIn << " != 1" << endl;
200  topologyIsOkay = false;
201  } else if (_debug)
202  printDebug << "number of incoming edges of node[" << nd << "] = " << nmbIn
203  << " is correct" << endl;
204  // check that for each isobar decay node the number of outgoing edges is 2
205  const unsigned int nmbOut = nmbOutEdges(_isobarVertices[i]);
206  if (nmbOut != 2) {
207  printWarn << "number of outgoing edges of node[" << nd << "] is "
208  << nmbOut << " != 2" << endl;
209  topologyIsOkay = false;
210  } else if (_debug)
211  printDebug << "number of outgoing edges of node[" << nd << "] = " << nmbOut
212  << " is correct" << endl;
213  }
214  if (_debug) {
215  if (topologyIsOkay)
216  printDebug << "isobar decay topology passed all tests" << endl;
217  else
218  printWarn << "isobar decay topology did not pass all tests" << endl;
219  }
220  return topologyIsOkay;
221 }
222 
223 
224 bool
226 {
227  bool allVertConsistent = true;
228  for (unsigned int i = 0; i < _isobarVertices.size(); ++i) {
229  const bool vertexConsistent = _isobarVertices[i]->checkConsistency();
230  if (not vertexConsistent) {
231  allVertConsistent = false;
232  if (_debug) {
233  if (vertexConsistent)
234  printDebug << "success: isobar decay vertex " << *_isobarVertices[i]
235  << " is consistent" << endl;
236  else
237  printWarn << "isobar decay vertex " << *_isobarVertices[i] << " is not consistent" << endl;
238  }
239  }
240  }
241  if (_debug) {
242  if (allVertConsistent)
243  printDebug << "success: information in isobar decay vertices is consistent" << endl;
244  else
245  printWarn << "information in isobar decay vertices is not consistent" << endl;
246  }
247  return allVertConsistent;
248 }
249 
250 
251 const TLorentzVector&
253 {
254  // loop over isobar decay vertices and propagate Lorentz-vectors from final-state particles up to X-system
255  for (int i = nmbDecayVertices() - 1; i >= 0; --i) {
256  if (_debug)
257  printDebug << "calculating Lorentz-vector of parent isobar '"
258  << _isobarVertices[i]->parent()->name() << "' "
259  << "of node[" << node(_isobarVertices[i]) << "]" << endl;
260  _isobarVertices[i]->calcParentLzVec();
261  }
262  return XIsobarDecayVertex()->parent()->lzVec();
263 }
264 
265 
266 void
267 isobarDecayTopology::calcIsobarCharges(const bool warnIfNotExistent)
268 {
269  // loop over isobar decay vertices and propagate charges from final-state particles up to X-system
270  for (int i = nmbDecayVertices() - 1; i >= 0; --i) {
271  const particlePtr& isobar = _isobarVertices[i]->parent();
272  if (_debug && warnIfNotExistent)
273  printDebug << "calculating charge of parent isobar '"
274  << isobar->name() << "' "
275  << "of node[" << node(_isobarVertices[i]) << "]" << endl;
276  const int isobarCharge = isobar->charge();
277  if (isobarCharge != _isobarVertices[i]->calcParentCharge())
278  if (isobar != XParticle()) {
279  if(warnIfNotExistent) {
280  printWarn << "fixed charge of isobar '" << isobar->name() << "' "
281  << "from " << isobarCharge << " to " << isobar->charge() << ". "
282  << "please fix wave definition." << endl;
283  }
284  isobar->fillFromDataTable(isobar->name(), warnIfNotExistent);
285  }
286  }
287  // correct C-parity of X, if necessary
288  if ((abs(XParticle()->charge()) > 0) and (XParticle()->C() != 0)) {
289  if(warnIfNotExistent) {
290  printWarn << "X is charged, but has C-parity = " << XParticle()->C()
291  << ". setting C-parity to zero. please fix wave definition." << endl;
292  XParticle()->setC(0);
293  }
294  }
295  // update graph name
296  name() = "\"" + XParticle()->qnSummary() + "\"";
297 }
298 
299 
300 void
302 {
303  // loop over isobar decay vertices and propagate baryon numbers from final-state particles up to X-system
304  for (int i = nmbDecayVertices() - 1; i >= 0; --i) {
305  if (_debug)
306  printDebug << "calculating baryon number of parent isobar '"
307  << _isobarVertices[i]->parent()->name() << "' "
308  << "of node[" << node(_isobarVertices[i]) << "]" << endl;
309  _isobarVertices[i]->calcParentBaryonNmb();
310  }
311 }
312 
313 
314 ostream&
315 isobarDecayTopology::print(ostream& out) const
316 {
317  // print nodes
318  out << "isobar decay topology '" << name() << "' has " << nmbNodes() << " node(s):" << endl;
319  if (productionVertex())
320  out << " production node[" << node(productionVertex()) << "] = "
321  << *productionVertex() << endl;
322  else
323  out << " topology has no production node." << endl;
324  for (unsigned int i = 0; i < nmbDecayVertices(); ++i)
325  out << " isobar decay node[" << node(decayVertices()[i]) << "] = "
326  << *decayVertices()[i] << endl;
327  nodeIterator iNd, iNdEnd;
328  for (tie(iNd, iNdEnd) = nodes(); iNd != iNdEnd; ++iNd)
329  if (isFsVertex(vertex(*iNd)))
330  out << " final-state node[" << *iNd << "] = " << *vertex(*iNd) << endl;
331  // print edges
332  out << "isobar decay topology '" << name() << "' has " << nmbEdges() << " edge(s):" << endl;
333  edgeIterator iEd, iEdEnd;
334  for (tie(iEd, iEdEnd) = edges(); iEd != iEdEnd; ++iEd) {
335  const particlePtr part = particle(*iEd);
336  out << " edge[" << *iEd << "] = " << part->name();
337  const int fsIndex = fsParticlesIndex(part);
338  if (fsIndex >= 0)
339  out << "[" << fsIndex << "] (final state)";
340  out << endl;
341  }
342  return out;
343 }
344 
345 
346 ostream&
348 {
349  if (_debug)
350  printDebug << "generating graphViz file for graph '" << name() << "'" << endl;
351  // set global properties
352  graphNodeAttribute()["style" ] = "filled";
353  graphNodeAttribute()["fillcolor"] = "white";
354  // set node names
355  nodeIterator iNd, iNdEnd;
356  for (tie(iNd, iNdEnd) = nodes(); iNd != iNdEnd; ++iNd) {
357  stringstream label;
358  label << vertex(*iNd)->name();
359  const isobarDecayVertexPtr& vert = dynamic_pointer_cast<isobarDecayVertex>(vertex(*iNd));
360  if (vert)
361  label << ": L = " << spinQn(vert->L()) << ", S = " << spinQn(vert->S());
362  nodeAttribute(*iNd)["label"] = label.str();
363  }
364  // set node shapes
365  nodeAttribute(productionVertex())["shape"] = "box";
366  for (unsigned int i = 0; i < nmbFsParticles(); ++i) {
367  nodeAttribute(toNode(fsParticles()[i]))["shape"] = "diamond";
368  nodeAttribute(toNode(fsParticles()[i]))["style"] = "dashed,filled";
369  }
370  // set edge names
371  edgeIterator iEd, iEdEnd;
372  for (tie(iEd, iEdEnd) = edges(); iEd != iEdEnd; ++iEd)
373  edgeAttribute(*iEd)["label"] = particle(*iEd)->label();
374  // set X edge name
375  edgeAttribute(edge(XParticle()))["label"] = XParticle()->qnSummary();
377  return out;
378 }
379 
380 
381 bool
382 isobarDecayTopology::writeGraphViz(const string& outFileName)
383 {
384  ofstream graphVizFile(outFileName.c_str());
385  if (not graphVizFile) {
386  printWarn << "cannot create file '" << outFileName << "'. graph is not written." << endl;
387  return false;
388  }
389  writeGraphViz(graphVizFile);
390  return true;
391 }
392 
393 
394 void
396 {
397  bool success = true;
399  for (unsigned int i = 0; i < nmbDecayVertices(); ++i) {
400  const interactionVertexPtr& v = decayVertices()[i];
401  _isobarVertices[i] = dynamic_pointer_cast<isobarDecayVertex>(v);
402  if (not _isobarVertices[i]) {
403  printWarn << *v << " is not of type isobarDecayVertex." << endl;
404  success = false;
405  }
406  }
407  if (not success) {
408  printErr << "incompatible topology. some interaction vertices are "
409  << "not of type isobarDecayVertex. aborting." << endl;
410  throw;
411  }
412 }
413 
414 
416 isobarDecayTopology::subDecay(const nodeDesc& startNd,
417  const bool linkToParentTopo)
418 {
419  isobarDecayTopology subTopo(decayTopology::subDecay(startNd, linkToParentTopo));
420  subTopo.name() = "\"" + vertex(startNd)->inParticles()[0]->qnSummary() + "\"";
421  return subTopo;
422 }
423 
424 
425 void
427 {
430 }
431 
432 
435  const vector<isobarDecayTopology>& daughterDecays)
436 {
437  if (_debug) {
438  printDebug << "joining " << daughterDecays.size() << " daughter graphs with parent "
439  << *parentVertex << endl;
440  }
441  isobarDecayTopology newTopo;
442  newTopo.addVertex(parentVertex);
443  for (unsigned int i = 0; i < daughterDecays.size(); ++i)
444  newTopo.addDecay(daughterDecays[i]);
445  return newTopo;
446 }
447 
448 
451  const isobarDecayTopology& daughter1Decay,
452  const isobarDecayTopology& daughter2Decay)
453 {
454  vector<isobarDecayTopology> daughterDecays(2);
455  daughterDecays[0] = daughter1Decay;
456  daughterDecays[1] = daughter2Decay;
457  return joinDaughterDecays(parentVertex, daughterDecays);
458 }
459 
460 
461 double
463 {
464  if (not vertex)
465  vertex = static_pointer_cast<isobarDecayVertex>(XDecayVertex());
466 
467  const particlePtr daughter1 = vertex->daughter1();
468  const particlePtr daughter2 = vertex->daughter2();
469  const particlePtr parent = vertex->parent ();
470 
471  const double clebsch = clebschGordanCoeff<double>(daughter1->isospin(), daughter1->isospinProj(),
472  daughter2->isospin(), daughter2->isospinProj(),
473  parent->isospin (), parent->isospinProj ());
474  double clebschDaughter1 = 1.;
475  double clebschDaughter2 = 1.;
476  if (not isFsParticle(daughter1))
477  clebschDaughter1
478  = getIsospinClebschGordanProduct(static_pointer_cast<isobarDecayVertex>(toVertex(daughter1)));
479  if (not isFsParticle(daughter2))
480  clebschDaughter2
481  = getIsospinClebschGordanProduct(static_pointer_cast<isobarDecayVertex>(toVertex(daughter2)));
482  return clebsch * clebschDaughter1 * clebschDaughter2;
483 }
484 
485 
486 vector<symTermMap>
488 {
489  const vector<particlePtr> fsParts = fsParticles();
490 
491  // Get a vector of groups of particles which have to be permutated.
492  //
493  // The group is defined as all particles with the same J, P and I. A group
494  // consists of a vector where the indices of all particles belonging to the
495  // group are saved.
496  vector< vector<unsigned int> > groups;
497  for(unsigned int i = 0; i < fsParts.size(); ++i) {
498  const particlePtr& particle = fsParts.at(i);
499  bool inserted = false;
500  for(unsigned int j = 0; j < groups.size(); ++j) {
501  const particlePtr& compPart = fsParts.at(groups.at(j).at(0));
502  if(particle->isospin() == compPart->isospin() &&
503  particle->J() == compPart->J() &&
504  particle->P() == compPart->P())
505  {
506  groups.at(j).push_back(i);
507  inserted = true;
508  }
509  }
510  if(!inserted) {
511  groups.push_back(vector<unsigned int>(1,i));
512  }
513  }
514 
515  if(_debug) {
516  printDebug << "Found isospin symmetrization groups:" << endl;
517  for(unsigned int i = 0; i < groups.size(); ++i) {
518  printDebug << "Group " << i << endl;
519  for(unsigned int j = 0; j < groups.at(i).size(); ++j) {
520  printDebug << j << ": " << groups.at(i).at(j)
521  << " (" << fsParts.at(groups.at(i).at(j))->name() << ")" << endl;
522  }
523  }
524  }
525 
526  // Saving all the isospins z-projections.
527  //
528  // Needed to reset everything as it was at the end.
529  vector<int> isospinProjs;
530  for(unsigned int j = 0; j < fsParts.size(); ++j) {
531  isospinProjs.push_back(fsParts.at(j)->isospinProj());
532  }
533 
534  // A vector of tuples to save the found permutations and their Clebsch-Gordans
535  vector<symTermMap> symAmplitudes;
536 
537  // Permutating all the particles and checking if the permutation makes sense
538  //
539  // First, loop over all the groups.
540  for(unsigned int i = 0; i < groups.size(); ++i) {
541  vector<unsigned int> group = groups.at(i);
542 
543  // Again debug output
544  if(_debug) {
545  printDebug << "Group permutations " << i << endl;
546  }
547 
548  // First we need a vector with the unity permutation, which will
549  // subsequently be permutated.
550  vector<unsigned int> permutations;
551  for(unsigned int j = 0; j < group.size(); ++j) {
552  permutations.push_back(j);
553  }
554 
555  // Now loop over all permutations for the current group.
556  do {
557  // Create a unity map. The map has one entry per final-state particle
558  // (unlike the group, which only holds the indices of the final-state
559  // particles in the group).
560  vector<unsigned int> map;
561  for(unsigned int j = 0; j < fsParts.size(); ++j) {
562  map.push_back(j);
563  }
564 
565  // Now apply the permutation to the map. We get the index of the particle
566  // we want to swap from the group vector and the index of the particle we
567  // want to swap it with from the permutations vector. The whole thing is
568  // applied to the map vector.
569  for(unsigned int j = 0; j < group.size(); ++j) {
570  map.at(group.at(j)) = group.at(permutations.at(j));
571  }
572 
573  // Some first checks if the permutation makes sense.
574  bool breaking = false;
575  for(unsigned int j = 0; j < map.size(); ++j) {
576 
577  if(j == map.at(j)) {
578  continue;
579  }
580 
581  // Make sure we are not swapping two indistinguishable particles.
582  if(fsParts.at(j)->name() == fsParts.at(map.at(j))->name()) {
583  breaking = true;
584  break;
585  }
586 
587  // Check if two particles from the same isobar are being swapped.
588  for(unsigned int k = 0; k < map.size(); ++k) {
589  if((map.at(k) == k) || (j == k)) continue;
590  if(fromVertex(fsParts.at(j)) == fromVertex(fsParts.at(k))) {
591  breaking = true;
592  break;
593  }
594  }
595  if(breaking) {
596  break;
597  }
598  }
599 
600  if(breaking) {
601  continue;
602  }
603 
604  // Set the isospin of the final-state particles as given by the premutation.
605  for(unsigned int j = 0; j < map.size(); ++j) {
606  fsParts.at(j)->setIsospinProj(isospinProjs.at(map.at(j)));
607  }
608  calcIsobarCharges(false);
609 
610  // Check for isospin consistency in all vertices.
611  for(unsigned int j = 0; j < _isobarVertices.size(); ++j) {
612  const particlePtr d1 = _isobarVertices.at(j)->daughter1();
613  const particlePtr d2 = _isobarVertices.at(j)->daughter2();
614  const particlePtr p = _isobarVertices.at(j)->parent();
615  if(!spinStatesCanCouple(d1->isospin(), d1->isospinProj(),
616  d2->isospin(), d2->isospinProj(),
617  p->isospin(), p->isospinProj())) {
618  breaking = true;
619  }
620  }
621 
622  // If something is amiss, reset everything and proceed to the next permutation.
623  if(breaking) {
624  for(unsigned int j = 0; j < fsParts.size(); ++j) {
625  fsParts.at(j)->setIsospinProj(isospinProjs.at(j));
626  }
628  continue;
629  }
630 
632 
633  // Survived all the criteria, saving to be returned
635  symAmplitudes.push_back(symTerm);
636 
637  if(_debug) {
638  printDebug << "Found valid permutation: ";
639  for(unsigned int j = 0; j < map.size(); ++j) {
640  cout << map.at(j);
641  }
642  cout << " (";
643  for(unsigned int j = 0; j < map.size(); ++j) {
644  cout << fsParts.at(j)->name();
645  }
646  cout << "), clebsch-gordan=" << clebsch << endl;
647  }
648 
649  // Resetting isospins for the next permutation
650  for(unsigned int j = 0; j < fsParts.size(); ++j) {
651  fsParts.at(j)->setIsospinProj(isospinProjs.at(j));
652  }
654 
655  } while(next_permutation(permutations.begin(), permutations.end()));
656  // End of the loop over permutations.
657 
658  } // End of the loop over the groups.
659 
660  for(unsigned int i = 0; i < symAmplitudes.size(); ++i) {
661  symAmplitudes[i].factor = signum(symAmplitudes[i].factor.real()) / sqrt(symAmplitudes.size());
662  }
663 
664  return symAmplitudes;
665 }
666 
667 
668 vector<symTermMap>
670 {
671  // get final state indistinguishable particles
672  typedef map<string, unsigned int>::const_iterator indistFsPartIt;
673  const map<string, unsigned int> indistFsPart = nmbIndistFsParticles();
674  printInfo << "indistinguishable final-state multiplicities "
675  << "(marked FS particles will be Bose symmetrized): ";
676  for (indistFsPartIt i = indistFsPart.begin(); i != indistFsPart.end(); ++i)
677  cout << i->first << " = " << i->second << ((i->second) >= 2 ? " <<< " : " ");
678  cout << endl;
679 
680  // calculate normalization factor
681  double nmbCombinations = 1;
682  for (indistFsPartIt i = indistFsPart.begin(); i != indistFsPart.end(); ++i)
683  nmbCombinations *= factorial<double>(i->second);
684  const double normFactor = 1 / sqrt(nmbCombinations);
685 
686  // initialize indices used to generate final-state permutation maps
687  // in order to get all permutations with std::next_permutation indices have to be sorted ascending
688  map<string, vector<unsigned int> > origFsPartIndices;
689  for (unsigned int i = 0; i < nmbFsParticles(); ++i) {
690  const string partName = fsParticles()[i]->name();
691  origFsPartIndices[partName].push_back(i);
692  }
693  map<string, vector<unsigned int> > newFsPartIndices = origFsPartIndices;
694  map<string, vector<unsigned int> >::iterator firstEntry = newFsPartIndices.begin();
695  vector<symTermMap> symTermMaps;
696  genBoseSymTermMaps(origFsPartIndices, newFsPartIndices, firstEntry, symTermMaps);
697  for(unsigned int i = 0; i < symTermMaps.size(); ++i) {
698  symTermMaps[i].factor = normFactor;
699  }
700  return symTermMaps;
701 }
702 
703 
704 bool
706  const vector<unsigned int>& permutation) const
707 {
708  vector<unsigned int> fsPartIndices = getFsPartIndicesConnectedToVertex(vertex);
709  for(unsigned int i = 0; i < permutation.size(); ++i) {
710  if(permutation[i] == i) {
711  continue;
712  }
713  if((find(fsPartIndices.begin(), fsPartIndices.end(), permutation[i]) == fsPartIndices.end() and
714  find(fsPartIndices.begin(), fsPartIndices.end(), i) != fsPartIndices.end()) or
715  (find(fsPartIndices.begin(), fsPartIndices.end(), i) == fsPartIndices.end() and
716  find(fsPartIndices.begin(), fsPartIndices.end(), permutation[i]) != fsPartIndices.end()))
717  {
718  return true;
719  }
720  }
721  return false;
722 }
723 
724 
725 bool
727  const vector<unsigned int>& permutation) const
728 {
729  vector<isobarDecayVertexPtr> verticesToTest(1, vertex);
730  isobarDecayVertexPtr daughter;
731  if(not isFsParticle(vertex->daughter1())) {
732  daughter = dynamic_pointer_cast<isobarDecayVertex>(toVertex(vertex->daughter1()));
733  if(not daughter) {
734  printErr << "Got NULL pointer while getting toVertex. Aborting..." << endl;
735  throw;
736  }
737  verticesToTest.push_back(daughter);
738  }
739  if(not isFsParticle(vertex->daughter2())) {
740  daughter = dynamic_pointer_cast<isobarDecayVertex>(toVertex(vertex->daughter2()));
741  if(not daughter) {
742  printErr << "Got NULL pointer while getting toVertex. Aborting..." << endl;
743  throw;
744  }
745  verticesToTest.push_back(daughter);
746  }
747  for(unsigned int i = 0; i < verticesToTest.size(); ++i) {
748  if(isobarIsAffectedByPermutation(verticesToTest[i], permutation)) {
749  return true;
750  }
751  }
752  return false;
753 }
754 
755 
757 {
758  vector<unsigned int> indices;
759  int index1 = fsParticlesIndex(vertex->daughter1());
760  int index2 = fsParticlesIndex(vertex->daughter2());
761  if(index1 >= 0) {
762  indices.push_back(static_cast<unsigned int>(index1));
763  } else {
764  const particlePtr& daughter1 = vertex->daughter1();
765  if(not isFsParticle(daughter1)) {
766  isobarDecayVertexPtr daughterVertex1 = dynamic_pointer_cast<isobarDecayVertex>(toVertex(daughter1));
767  if(not daughterVertex1) {
768  printErr << "Got NULL pointer while getting toVertex. Aborting..." << endl;
769  throw;
770  }
771  vector<unsigned int> indices1 = getFsPartIndicesConnectedToVertex(daughterVertex1);
772  indices.insert(indices.end(), indices1.begin(), indices1.end());
773  }
774  }
775  if(index2 >= 0) {
776  indices.push_back(static_cast<unsigned int>(index2));
777  } else {
778  const particlePtr& daughter2 = vertex->daughter2();
779  if(not isFsParticle(daughter2)) {
780  isobarDecayVertexPtr daughterVertex2 = dynamic_pointer_cast<isobarDecayVertex>(toVertex(daughter2));
781  if(not daughterVertex2) {
782  printErr << "Got NULL pointer while getting toVertex. Aborting..." << endl;
783  throw;
784  }
785  vector<unsigned int> indices2 = getFsPartIndicesConnectedToVertex(daughterVertex2);
786  indices.insert(indices.end(), indices2.begin(), indices2.end());
787  }
788  }
789  return indices;
790 }
791 
792 void
794 (const map<string, vector<unsigned int> >& origFsPartIndices, // original final-state particle ordering sorted by species
795  const map<string, vector<unsigned int> >& newFsPartIndices, // new final-state particle ordering sorted by species
796  map<string, vector<unsigned int> >::iterator& newFsPartIndicesEntry, // entry of particle species that is currently being symmetrized
797  vector<symTermMap>& newSymTermMaps) const // generated permutation maps
798 {
799  // loop over all permutations for current final-state particle species
800  do {
801  map<string, vector<unsigned int> >::iterator nextFsPartIndicesEntry = newFsPartIndicesEntry;
802  if (++nextFsPartIndicesEntry != newFsPartIndices.end())
803  // recurse to permutations of other final-state particle species
804  genBoseSymTermMaps(origFsPartIndices, newFsPartIndices, nextFsPartIndicesEntry,
805  newSymTermMaps);
806  else {
807  // build map for current permutation
808  vector<unsigned int> bosePermMap(nmbFsParticles(), 0);
809  if (_debug)
810  printDebug << "Bose-symmetrization final-state permutation: ";
811  for (map<string, vector<unsigned int> >::const_iterator i = origFsPartIndices.begin();
812  i != origFsPartIndices.end(); ++i) {
813  const string partName = i->first;
814  map<string, vector<unsigned int> >::const_iterator entry = newFsPartIndices.find(partName);
815  assert(entry != newFsPartIndices.end());
816  for (unsigned int j = 0; j < i->second.size(); ++j) {
817  assert(entry->second.size() == i->second.size());
818  const unsigned int origFsPartIndex = i->second[j];
819  const unsigned int newFsPartIndex = entry->second[j];
820  if (_debug)
821  cout << partName << "[" << origFsPartIndex << " -> " << newFsPartIndex << "] ";
822  bosePermMap[origFsPartIndex] = newFsPartIndex;
823  }
824  }
825  if (_debug)
826  cout << endl;
827  // compute effective permutation map by reshuffling Bose
828  // symmetrization permuation map according to base permutation map
829  vector<unsigned int> fsPartPermMap(bosePermMap.size(), 0);
830  for (unsigned int i = 0; i < fsPartPermMap.size(); ++i) {
831  fsPartPermMap[i] = bosePermMap[i];
832  }
833  if (_debug)
834  printDebug << "effective Bose-symmetrization is: final-state permutation map = "
835  << fsPartPermMap << endl;
836  symTermMap symTerm(1, fsPartPermMap);
837  newSymTermMaps.push_back(symTerm);
838  }
839  } while (next_permutation(newFsPartIndicesEntry->second.begin(),
840  newFsPartIndicesEntry->second.end()));
841 }
842 
843 
844 vector<unsigned int>
846 {
847  vector<unsigned int> boseSymVertices;
848  isobarDecayTopologyPtr topo = this->clone(); // needed, because subDecay below is not const
849  for (unsigned int i = 0; i < topo->nmbDecayVertices(); ++i) {
850  // find decay vertex with two isobars as daughters
851  isobarDecayVertexPtr vert = topo->isobarDecayVertices()[i];
852  const particlePtr daughters[2] = {vert->daughter1(), vert->daughter2()};
853  if (topo->isFsParticle(daughters[0]) or topo->isFsParticle(daughters[1]))
854  continue;
855  // make sure the two daughters have the same final state
856  const decayTopology isobarTopos[2] = {topo->subDecay(topo->toNode(daughters[0])),
857  topo->subDecay(topo->toNode(daughters[1]))};
858  if (isobarTopos[0].nmbIndistFsParticles() == isobarTopos[1].nmbIndistFsParticles()) {
859  if (_debug)
860  printDebug << "found isobar decay vertex " << *vert << " "
861  << "that has two isobar daughters which decay into the same final state: " << endl
862  << "daughter 1 " << isobarTopos[0]
863  << "daughter 2 " << isobarTopos[1];
864  boseSymVertices.push_back(i);
865  }
866  }
867  return boseSymVertices;
868 }