Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DecayFinder.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file DecayFinder.cc
1 /*
2  * Find decay topologies in HepMC
3  * Cameron Dean
4  * 04/06/2021
5  */
6 
7 // This is an array of PID's which decay faster than we can see in the detector
8 // This means if we find them in the HepMC record we need to skip over them
9 // If your decay descriptor contains one of these IDs then it will be removed from the list before starting the search
10 
11 #include "DecayFinder.h"
12 
13 #include "DecayFinderContainerBase.h" // for DecayFinderContainerBase::Iter
14 #include "DecayFinderContainer_v1.h" // for DecayFinderContainer_v1
15 
17 
20 
22 
23 #include <phool/PHCompositeNode.h>
24 #include <phool/PHIODataNode.h> // for PHIODataNode
25 #include <phool/PHNodeIterator.h> // for PHNodeIterator
26 #include <phool/getClass.h>
27 
28 #pragma GCC diagnostic push
29 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
30 #include <HepMC/GenEvent.h>
31 #include <HepMC/GenVertex.h> // for GenVertex::particle_iterator
32 #pragma GCC diagnostic pop
33 
34 #include <HepMC/GenParticle.h>
35 #include <HepMC/IteratorRange.h>
36 #include <HepMC/SimpleVector.h>
37 
38 #include <TDatabasePDG.h>
39 
40 #include <algorithm> // for find, for_each, sort, transform
41 #include <cctype> // for toupper
42 #include <cstdlib> // for abs, size_t
43 #include <iostream> // for operator<<, endl, basic_ostream
44 #include <iterator> // for end, begin
45 #include <map> // for map, map<>::mapped_type, _Rb...
46 #include <memory> // for allocator_traits<>::value_type
47 
48 int listOfResonantPIDs[] = {111, 113, 213, 333, 310, 311, 313, 323, 413, 423, 513, 523, 441, 443, 100443, 9000111, 9000211, 100111, 100211, 10111,
49  10211, 9010111, 9010211, 10113, 10213, 20113, 20213, 9000113, 9000213, 100113, 100213, 9010113, 9010213, 9020113, 9020213,
50  30113, 30213, 9030113, 9030213, 9040113, 9040213, 115, 215, 10115, 10215, 9000115, 9000215, 9010115, 9010215, 117, 217,
51  9000117, 9000217, 9010117, 9010217, 119, 219, 221, 331, 9000221, 9010221, 100221, 10221, 9020221, 100331, 9030221, 10331,
52  9040221, 9050221, 9060221, 9070221, 9080221, 223, 10223, 20223, 10333, 20333, 1000223, 9000223, 9010223, 30223, 100333, 225,
53  9000225, 335, 9010225, 9020225, 10225, 9030225, 10335, 9040225, 9050225, 9060225, 9070225, 9080225, 9090225, 227, 337, 229,
54  9000229, 9010229, 9000311, 9000321, 10311, 10321, 100311, 100321, 9010311, 9010321, 9020311, 9020321, 10313, 10323, 20313,
55  20323, 100313, 100323, 9000313, 9000323, 30313, 30323, 315, 325, 9000315, 9000325, 10315, 10325, 20315, 20325, 9010315,
56  9010325, 9020315, 9020325, 317, 327, 9010317, 9010327, 319, 329, 9000319, 9000329, 10411, 10421, 10413, 10423, 20413, 20423,
57  415, 425, 431, 10431, 433, 10433, 20433, 435, 10511, 10521, 10513, 10523, 20513, 20523, 515, 525, 10531, 533, 10533, 20533, 535,
58  10541, 543, 10543, 20543, 545, 10441, 100441, 10443, 20443, 30443, 9000443, 9010443, 9020443, 445, 100445, 551, 10551, 100551,
59  110551, 200551, 210551, 10553, 20553, 30553, 110553, 120553, 130553, 210553, 220553, 9000553, 9010553, 555, 10555, 20555, 100555,
60  110555, 120555, 200555, 557, 100557, 2224, 2214, 2114, 1114, 3212, 3224, 3214, 3114, 3324, 3314, 4222, 4212, 4112, 4224, 4214,
61  4114, 4232, 4132, 4322, 4312, 4324, 4314, 4332, 4334, 4412, 4422, 4414, 4424, 4432, 4434, 4444};
62 
64  : SubsysReco("DECAYFINDER")
65  , m_save_dst(false)
66 {
67 }
68 
70  : SubsysReco(name)
71  , m_save_dst(false)
72 {
73 }
74 
76 {
77  if (Verbosity() >= VERBOSITY_SOME)
78  {
79  std::cout << "DecayFinder name: " << Name() << std::endl;
80  std::cout << "Decay descriptor: " << m_decayDescriptor << std::endl;
81  }
82 
83  int canSearchDecay = parseDecayDescriptor();
84 
85  if (m_save_dst)
86  {
87  createDecayNode(topNode);
88  }
89 
90  return canSearchDecay;
91 }
92 
94 {
95  bool decayFound = findDecay(topNode);
96 
97  if (decayFound && m_save_dst && Verbosity() >= VERBOSITY_MORE)
98  {
99  printNode(topNode);
100  }
101 
102  if (m_triggerOnDecay && !decayFound)
103  {
104  if (Verbosity() >= VERBOSITY_MORE)
105  {
106  std::cout << "The decay, " << m_decayDescriptor << " was not found or reconstructable in this event, skipping" << std::endl;
107  }
109  }
110  else
111  {
113  }
114 }
115 
117 {
118  if (Verbosity() >= VERBOSITY_SOME)
119  {
120  printInfo();
121  }
122 
123  return 0;
124 }
125 
126 /*
127  *
128  *This function tries to intepret the string you wrote
129  *Will be semi-independent of the rest of this class
130  *(semi- because we need this to set the charges and strings)
131  *
132  */
134 {
135  bool ddCanBeParsed = true;
136 
137  size_t daughterLocator;
138 
139  std::string mother;
140  std::string intermediate;
141  std::string daughter;
142 
143  int mother_charge = 0;
144  std::vector<int> intermediates_charge;
145  std::vector<int> daughters_charge;
146 
147  std::string decayArrow = "->";
148  std::string chargeIndicator = "^";
149  std::string startIntermediate = "{";
150  std::string endIntermediate = "}";
151 
152  // These tracks require a + or - after their name for TDatabasePDG
153  std::string specialTracks[] = {"e", "mu", "pi", "K"};
154 
155  std::string manipulateDecayDescriptor = m_decayDescriptor;
156 
157  // Remove all white space before we begin
158  size_t pos;
159  while ((pos = manipulateDecayDescriptor.find(' ')) != std::string::npos)
160  {
161  manipulateDecayDescriptor.replace(pos, 1, "");
162  }
163 
164  // Check for charge conjugate requirement
165  std::string checkForCC = manipulateDecayDescriptor.substr(0, 1) + manipulateDecayDescriptor.substr(manipulateDecayDescriptor.size() - 3, 3);
166  std::for_each(checkForCC.begin(), checkForCC.end(), [](char& c)
167  { c = ::toupper(c); });
168 
169  // Remove the CC check if needed
170  if (checkForCC == "[]CC")
171  {
172  manipulateDecayDescriptor = manipulateDecayDescriptor.substr(1, manipulateDecayDescriptor.size() - 4);
173  m_getChargeConjugate = true;
174  }
175 
176  // Try and find the initial particle
177  size_t findMotherEndPoint = manipulateDecayDescriptor.find(decayArrow);
178  mother = manipulateDecayDescriptor.substr(0, findMotherEndPoint);
179  if (findParticle(mother))
180  {
181  m_mother_ID = abs(get_pdgcode(mother));
182  }
183  else
184  {
185  ddCanBeParsed = false;
186  }
187  manipulateDecayDescriptor.erase(0, findMotherEndPoint + decayArrow.length());
188 
189  // Try and find the intermediates
190  while ((pos = manipulateDecayDescriptor.find(startIntermediate)) != std::string::npos)
191  {
192  size_t findIntermediateStartPoint = manipulateDecayDescriptor.find(startIntermediate, pos);
193  size_t findIntermediateEndPoint = manipulateDecayDescriptor.find(endIntermediate, pos);
194  std::string intermediateDecay = manipulateDecayDescriptor.substr(pos + 1, findIntermediateEndPoint - (pos + 1));
195 
196  intermediate = intermediateDecay.substr(0, intermediateDecay.find(decayArrow));
197  if (findParticle(intermediate))
198  {
199  m_intermediates_ID.push_back(abs(get_pdgcode(intermediate)));
200  }
201  else
202  {
203  ddCanBeParsed = false;
204  }
205 
206  // Now find the daughters associated to this intermediate
207  int nDaughters = 0;
208  intermediateDecay.erase(0, intermediateDecay.find(decayArrow) + decayArrow.length());
209  while ((daughterLocator = intermediateDecay.find(chargeIndicator)) != std::string::npos)
210  {
211  daughter = intermediateDecay.substr(0, daughterLocator);
212  std::string daughterChargeString = intermediateDecay.substr(daughterLocator + 1, 1);
213  if (std::find(std::begin(specialTracks), std::end(specialTracks), daughter) != std::end(specialTracks))
214  {
215  daughter += daughterChargeString;
216  }
217  if (findParticle(daughter))
218  {
219  m_daughters_ID.push_back(abs(get_pdgcode(daughter)));
220  daughters_charge.push_back(get_charge(daughter));
221  }
222  else
223  {
224  ddCanBeParsed = false;
225  }
226  intermediateDecay.erase(0, daughterLocator + 2);
227  ++nDaughters;
228  }
229  manipulateDecayDescriptor.erase(findIntermediateStartPoint, findIntermediateEndPoint + 1 - findIntermediateStartPoint);
230  m_nTracksFromIntermediates.push_back(nDaughters);
231  m_nTracksFromMother += 1;
232  }
233 
234  // Now find any remaining reconstructable tracks from the mother
235  while ((daughterLocator = manipulateDecayDescriptor.find(chargeIndicator)) != std::string::npos)
236  {
237  daughter = manipulateDecayDescriptor.substr(0, daughterLocator);
238  std::string daughterChargeString = manipulateDecayDescriptor.substr(daughterLocator + 1, 1);
239  if (std::find(std::begin(specialTracks), std::end(specialTracks), daughter) != std::end(specialTracks))
240  {
241  daughter += daughterChargeString;
242  }
243  if (findParticle(daughter))
244  {
245  m_daughters_ID.push_back(abs(get_pdgcode(daughter)));
246  daughters_charge.push_back(get_charge(daughter));
247  }
248  else
249  {
250  ddCanBeParsed = false;
251  }
252  manipulateDecayDescriptor.erase(0, daughterLocator + 2);
253  m_nTracksFromMother += 1;
254  }
255 
256  unsigned int trackStart = 0;
257  unsigned int trackEnd = 0;
258  for (unsigned int i = 0; i < m_intermediates_ID.size(); ++i)
259  {
260  trackStart = trackEnd;
261  trackEnd = m_nTracksFromIntermediates[i] + trackStart;
262 
263  int vtxCharge = 0;
264 
265  for (unsigned int j = trackStart; j < trackEnd; ++j)
266  {
267  vtxCharge += daughters_charge[j];
268  }
269 
270  intermediates_charge.push_back(vtxCharge);
271  }
272 
273  for (unsigned int i = 0; i < m_daughters_ID.size(); ++i)
274  {
275  mother_charge += daughters_charge[i];
276  }
277 
278  m_mother_ID = mother_charge == 0 ? m_mother_ID : mother_charge * m_mother_ID;
279  for (unsigned int i = 0; i < m_intermediates_ID.size(); ++i)
280  {
281  m_intermediates_ID[i] = intermediates_charge[i] == 0 ? m_intermediates_ID[i] : intermediates_charge[i] * m_intermediates_ID[i];
282  m_motherDecayProducts.push_back(m_intermediates_ID[i]);
283  }
284  for (unsigned int i = 0; i < m_daughters_ID.size(); ++i)
285  {
286  m_daughters_ID[i] = daughters_charge[i] == 0 ? m_daughters_ID[i] : daughters_charge[i] * m_daughters_ID[i];
287  if (i >= trackEnd)
288  {
289  m_motherDecayProducts.push_back(m_daughters_ID[i]);
290  }
291  }
292 
293  if (ddCanBeParsed)
294  {
295  if (Verbosity() >= VERBOSITY_MORE)
296  {
297  std::cout << "Your decay descriptor can be parsed" << std::endl;
298  }
299  return 0;
300  }
301  else
302  {
303  if (Verbosity() >= VERBOSITY_SOME)
304  {
305  std::cout << "Your decay descriptor cannot be parsed, " << Name() << " will not be registered" << std::endl;
306  }
308  }
309 }
310 
311 /*
312  * Main body for searching your decay
313  * Slight issue if you limit the generator decay
314  * as decays wont enter the HepMC record
315  * need a switch to go to Geant4 record
316  */
318 {
319  bool decayWasFound = false;
320  bool reconstructableDecayWasFound = false;
321  bool aTrackFailedPT = false;
322  bool aTrackFailedETA = false;
323  bool aMotherHasPhoton = false;
324  bool aMotherHasPi0 = false;
325  std::vector<int> correctMotherProducts;
326  std::vector<int> positive_motherDecayProducts;
327 
328  int n = sizeof(listOfResonantPIDs) / sizeof(listOfResonantPIDs[0]);
329  for (int i : m_intermediates_ID)
330  {
332  }
333 
334  for (int m_motherDecayProduct : m_motherDecayProducts)
335  {
336  positive_motherDecayProducts.push_back(std::abs(m_motherDecayProduct));
337  }
338 
339  m_truthinfo = findNode::getClass<PHG4TruthInfoContainer>(topNode, "G4TruthInfo");
340  if (!m_truthinfo)
341  {
342  std::cout << "DecayFinder: Missing node G4TruthInfo" << std::endl;
343  }
344 
345  m_geneventmap = findNode::getClass<PHHepMCGenEventMap>(topNode, "PHHepMCGenEventMap");
346  if (!m_geneventmap)
347  {
348  std::cout << "DecayFinder: Missing node PHHepMCGenEventMap" << std::endl;
349  }
350 
351  if (!m_truthinfo && !m_geneventmap)
352  {
353  std::cout << "You have neither the PHHepMCGenEventMap or G4TruthInfo nodes" << std::endl;
354  std::cout << "DecayFinder will crash, exiting now!" << std::endl;
355  exit(1);
356  }
357 
358  if (m_truthinfo && !m_geneventmap) // This should use the truth info container if we have no HepMC record
359  {
360  if (Verbosity() >= VERBOSITY_SOME)
361  {
362  std::cout << "You have a valid G4TruthInfo but no PHHepMCGenEventMap container" << std::endl;
363  std::cout << "You are probably using a particle gun" << std::endl;
364  }
365 
367  for (PHG4TruthInfoContainer::ConstIterator iter = range.first; iter != range.second; ++iter)
368  {
369  PHG4Particle* g4particle = iter->second;
370  int this_pid = m_getChargeConjugate ? abs(g4particle->get_pid()) : g4particle->get_pid();
371  if (this_pid == m_mother_ID)
372  {
373  if (Verbosity() >= VERBOSITY_MAX)
374  {
375  std::cout << "parent->pdg_id(): " << g4particle->get_pid() << std::endl;
376  }
377 
378  bool breakOut = false;
379  correctMotherProducts.clear();
380  decayChain.clear();
381  decayChain.push_back(std::make_pair(std::make_pair(g4particle->get_primary_id(), g4particle->get_barcode()), g4particle->get_pid()));
382 
383  searchGeant4Record(g4particle->get_barcode(), g4particle->get_pid(), positive_motherDecayProducts, breakOut, aMotherHasPhoton, aMotherHasPi0, aTrackFailedPT, aTrackFailedETA, correctMotherProducts);
384 
385  if (breakOut)
386  {
387  continue;
388  }
389 
390  decayWasFound = compareDecays(m_motherDecayProducts, correctMotherProducts);
391 
392  if (decayWasFound)
393  {
394  m_counter += 1;
395  if (aTrackFailedPT && !aTrackFailedETA)
396  {
397  m_nCandFail_pT += 1;
398  }
399  else if (!aTrackFailedPT && aTrackFailedETA)
400  {
401  m_nCandFail_eta += 1;
402  }
403  else if (aTrackFailedPT && aTrackFailedETA)
404  {
406  }
407  else
408  {
410  reconstructableDecayWasFound = true;
411  if (m_save_dst)
412  {
413  fillDecayNode(topNode, decayChain);
414  }
415  if (aMotherHasPhoton && !aMotherHasPi0)
416  {
417  m_nCandHas_Photon += 1;
418  }
419  else if (!aMotherHasPhoton && aMotherHasPi0)
420  {
421  m_nCandHas_Pi0 += 1;
422  }
423  else if (aMotherHasPhoton && aMotherHasPi0)
424  {
426  }
427  else
428  {
430  }
431  }
432  }
433  }
434  }
435  return reconstructableDecayWasFound;
436  }
437 
439  riter != m_geneventmap->rend(); ++riter)
440  {
441  m_genevt = riter->second;
442  if (!m_genevt)
443  {
444  std::cout << "DecayFinder: Missing node PHHepMCGenEvent" << std::endl;
445  return false;
446  }
447 
448  HepMC::GenEvent* theEvent = m_genevt->getEvent();
449 
450  for (HepMC::GenEvent::particle_const_iterator p = theEvent->particles_begin(); p != theEvent->particles_end(); ++p)
451  {
452  int this_pid = m_getChargeConjugate ? abs((*p)->pdg_id()) : (*p)->pdg_id();
453  if (this_pid == m_mother_ID)
454  {
455  if (Verbosity() >= VERBOSITY_MAX)
456  {
457  std::cout << "parent->pdg_id(): " << (*p)->pdg_id() << std::endl;
458  }
459 
460  bool breakOut = false;
461  correctMotherProducts.clear();
462  decayChain.clear();
463  decayChain.push_back(std::make_pair(std::make_pair(m_genevt->get_embedding_id(), (*p)->barcode()), (*p)->pdg_id()));
464 
465  // Make sure that the mother has a decay in our record
466  if (!(*p)->end_vertex()) // Mother has no end vertex, decay volume was limited
467  {
468  searchGeant4Record((*p)->barcode(), (*p)->pdg_id(), positive_motherDecayProducts, breakOut, aMotherHasPhoton, aMotherHasPi0, aTrackFailedPT, aTrackFailedETA, correctMotherProducts);
469  }
470  else
471  {
472  searchHepMCRecord((*p), positive_motherDecayProducts, breakOut, aMotherHasPhoton, aMotherHasPi0, aTrackFailedPT, aTrackFailedETA, correctMotherProducts);
473  }
474 
475  if (breakOut)
476  {
477  continue;
478  }
479 
480  decayWasFound = compareDecays(m_motherDecayProducts, correctMotherProducts);
481 
482  if (decayWasFound)
483  {
484  m_counter += 1;
485  if (aTrackFailedPT && !aTrackFailedETA)
486  {
487  m_nCandFail_pT += 1;
488  }
489  else if (!aTrackFailedPT && aTrackFailedETA)
490  {
491  m_nCandFail_eta += 1;
492  }
493  else if (aTrackFailedPT && aTrackFailedETA)
494  {
496  }
497  else
498  {
500  reconstructableDecayWasFound = true;
501  if (m_save_dst)
502  {
503  fillDecayNode(topNode, decayChain);
504  }
505  if (aMotherHasPhoton && !aMotherHasPi0)
506  {
507  m_nCandHas_Photon += 1;
508  }
509  else if (!aMotherHasPhoton && aMotherHasPi0)
510  {
511  m_nCandHas_Pi0 += 1;
512  }
513  else if (aMotherHasPhoton && aMotherHasPi0)
514  {
516  }
517  else
518  {
520  }
521  }
522  }
523  }
524  }
525  }
526 
527  return reconstructableDecayWasFound;
528 }
529 
530 /*
531  * Function to search HepMC record
532  * Can switch to Geant4 search if needed
533  */
534 void DecayFinder::searchHepMCRecord(HepMC::GenParticle* particle, std::vector<int> decayProducts,
535  bool& breakLoop, bool& hasPhoton, bool& hasPi0, bool& failedPT, bool& failedETA,
536  std::vector<int>& actualDecayProducts)
537 {
538  for (HepMC::GenVertex::particle_iterator children = particle->end_vertex()->particles_begin(HepMC::children);
539  children != particle->end_vertex()->particles_end(HepMC::children); ++children)
540  {
541  if (Verbosity() >= VERBOSITY_MAX)
542  {
543  std::cout << "--children->pdg_id(): " << (*children)->pdg_id() << std::endl;
544  }
545 
546  if ((*children)->pdg_id() == 22)
547  {
548  hasPhoton = true;
549  }
550  if ((*children)->pdg_id() == 111)
551  {
552  hasPi0 = true;
553  }
554  if ((!m_allowPhotons && (*children)->pdg_id() == 22) || (!m_allowPi0 && (*children)->pdg_id() == 111))
555  {
556  breakLoop = true;
557  break;
558  }
559 
560  // This is one of the children we are looking for
561  if (std::find(decayProducts.begin(), decayProducts.end(),
562  std::abs((*children)->pdg_id())) != decayProducts.end())
563  {
564  if (Verbosity() >= VERBOSITY_MAX)
565  {
566  std::cout << "This is a child you were looking for" << std::endl;
567  }
568  // Check if this is an internediate decay that didnt decay in the generator
569  std::vector<int> positive_intermediates_ID;
570  for (int i : m_intermediates_ID)
571  {
572  positive_intermediates_ID.push_back(abs(i));
573  }
574 
575  if (!(*children)->end_vertex() && std::find(positive_intermediates_ID.begin(), positive_intermediates_ID.end(),
576  abs((*children)->pdg_id())) != positive_intermediates_ID.end())
577  {
578  std::vector<int> requiredIntermediateDecayProducts;
579  std::vector<int> positive_requiredIntermediateDecayProducts;
580  std::vector<int> actualIntermediateDecayProducts;
581  // Which intermediate decay list to we need
582  auto it = std::find(positive_intermediates_ID.begin(), positive_intermediates_ID.end(), abs((*children)->pdg_id()));
583  int index = std::distance(positive_intermediates_ID.begin(), it);
584 
585  unsigned int trackStart = 0, trackStop = 0;
586  if (index == 0)
587  {
588  trackStop = m_nTracksFromIntermediates[0];
589  }
590  else
591  {
592  for (int i = 0; i < index; ++i)
593  {
594  trackStart += m_nTracksFromIntermediates[i];
595  }
596  trackStop = trackStart + m_nTracksFromIntermediates[index];
597  }
598  for (unsigned int i = trackStart; i < trackStop; ++i)
599  {
600  requiredIntermediateDecayProducts.push_back(m_daughters_ID[i]);
601  positive_requiredIntermediateDecayProducts.push_back(abs(m_daughters_ID[i]));
602  }
603 
604  searchGeant4Record((*children)->barcode(), (*children)->pdg_id(), positive_requiredIntermediateDecayProducts,
605  breakLoop, hasPhoton, hasPi0, failedPT, failedETA, actualIntermediateDecayProducts);
606 
607  bool needThisParticle = compareDecays(requiredIntermediateDecayProducts, actualIntermediateDecayProducts);
608  if (needThisParticle)
609  {
610  actualDecayProducts.push_back((*children)->pdg_id());
611  decayChain.push_back(std::make_pair(std::make_pair(m_genevt->get_embedding_id(), (*children)->barcode()), (*children)->pdg_id()));
612  }
613  else // Remove what I added to the decayChain
614  {
616  decayChain.pop_back();
617  }
618 
620  }
621  else
622  {
623  bool needThisParticle = checkIfCorrectHepMCParticle((*children), failedPT, failedETA);
624  if (needThisParticle)
625  {
626  actualDecayProducts.push_back((*children)->pdg_id());
627  decayChain.push_back(std::make_pair(std::make_pair(m_genevt->get_embedding_id(), (*children)->barcode()), (*children)->pdg_id()));
628  }
629  }
630  } // Now check if it's part of the resonance list
632  std::abs((*children)->pdg_id())) != std::end(listOfResonantPIDs))
633  {
634  if (Verbosity() >= VERBOSITY_MAX)
635  {
636  std::cout << "This is a resonance to investigate further" << std::endl;
637  }
638  if (!(*children)->end_vertex())
639  {
640  searchGeant4Record((*children)->barcode(), (*children)->pdg_id(), decayProducts,
641  breakLoop, hasPhoton, hasPi0, failedPT, failedETA, actualDecayProducts);
642  }
643  else
644  {
645  for (HepMC::GenVertex::particle_iterator grandchildren = (*children)->end_vertex()->particles_begin(HepMC::children);
646  grandchildren != (*children)->end_vertex()->particles_end(HepMC::children); ++grandchildren)
647  {
648  bool needThisParticle = checkIfCorrectHepMCParticle((*grandchildren), failedPT, failedETA);
649  if (needThisParticle)
650  {
651  actualDecayProducts.push_back((*grandchildren)->pdg_id());
652  decayChain.push_back(std::make_pair(std::make_pair(m_genevt->get_embedding_id(), (*grandchildren)->barcode()), (*grandchildren)->pdg_id()));
653  }
654  }
655  }
656  }
657  else
658  {
659  breakLoop = true; // This particle is not in the decay descriptor, stop
660  break;
661  }
662  }
663 }
664 
665 /*
666  *Function to search Geant4 record
667  * Cannot switch to HepMC search (doesn't make sense to)
668  */
669 void DecayFinder::searchGeant4Record(int barcode, int pid, std::vector<int> decayProducts, bool& breakLoop, bool& hasPhoton, bool& hasPi0, bool& failedPT, bool& failedETA, std::vector<int>& actualDecayProducts)
670 {
672  for (PHG4TruthInfoContainer::ConstIterator iter = range.first; iter != range.second; ++iter)
673  {
674  if (decayChain.size() == 100)
675  {
676  breakLoop = true; // Stuck in loop. Sympton not cause!
677  break;
678  }
679 
680  PHG4Particle* g4particle = iter->second;
681  PHG4Particle* mother = nullptr;
682  if (g4particle->get_parent_id() != 0)
683  {
684  mother = m_truthinfo->GetParticle(g4particle->get_parent_id());
685  }
686  else
687  {
688  continue;
689  }
690  if (mother->get_barcode() == barcode && abs(mother->get_pid()) == abs(pid))
691  {
692  int particleID = g4particle->get_pid();
693  if (Verbosity() >= VERBOSITY_MAX)
694  {
695  std::cout << "--children->pdg_id(): " << particleID << std::endl;
696  }
697  if (particleID == 22)
698  {
699  hasPhoton = true;
700  }
701  if (particleID == 111)
702  {
703  hasPi0 = true;
704  }
705 
706  if ((!m_allowPhotons && particleID == 22) || (!m_allowPi0 && particleID == 111))
707  {
708  breakLoop = true;
709  break;
710  }
711  // This is one of the children we are looking for
712  if (std::find(decayProducts.begin(), decayProducts.end(),
713  std::abs(particleID)) != decayProducts.end())
714  {
715  bool needThisParticle = checkIfCorrectGeant4Particle(g4particle, hasPhoton, hasPi0, failedPT, failedETA);
716  if (needThisParticle)
717  {
718  if (Verbosity() >= VERBOSITY_MAX)
719  {
720  std::cout << "This is a child you were looking for" << std::endl;
721  }
722  actualDecayProducts.push_back(particleID);
723  int embedding_id = m_geneventmap ? m_genevt->get_embedding_id() : g4particle->get_primary_id();
724  decayChain.push_back(std::make_pair(std::make_pair(embedding_id, g4particle->get_barcode()), particleID));
725  }
726  } // Now check if it's part of the other resonance list
727  else if ((m_allowPhotons && particleID == 22) || (m_allowPi0 && particleID == 111))
728  {
729  continue;
730  }
732  std::abs(particleID)) != std::end(listOfResonantPIDs))
733  {
734  if (Verbosity() >= VERBOSITY_MAX)
735  {
736  std::cout << "This is a resonance to investigate further" << std::endl;
737  }
738  searchGeant4Record(g4particle->get_barcode(), g4particle->get_pid(), decayProducts,
739  breakLoop, hasPhoton, hasPi0, failedPT, failedETA, actualDecayProducts);
740  }
741  else
742  {
743  breakLoop = true; // This particle is not in the decay descriptor, stop
744  break;
745  }
746  }
747  }
748 }
749 
750 /*
751  * Checks if this prticle really matches your request
752  */
753 bool DecayFinder::checkIfCorrectHepMCParticle(HepMC::GenParticle* particle, bool& trackFailedPT, bool& trackFailedETA)
754 {
755  bool acceptParticle = false;
756 
757  std::vector<int> positive_intermediates_ID;
758  for (int i : m_intermediates_ID)
759  {
760  positive_intermediates_ID.push_back(abs(i));
761  }
762 
763  // Check if it is an intermediate or a final track
764  if (std::find(positive_intermediates_ID.begin(), positive_intermediates_ID.end(),
765  abs(particle->pdg_id())) != positive_intermediates_ID.end())
766  {
767  std::vector<int> requiredIntermediateDecayProducts;
768  std::vector<int> actualIntermediateDecayProducts;
769  // Which intermediate decay list to we need
770  auto it = std::find(positive_intermediates_ID.begin(), positive_intermediates_ID.end(), abs(particle->pdg_id()));
771  int index = std::distance(positive_intermediates_ID.begin(), it);
772 
773  unsigned int trackStart = 0, trackStop = 0;
774  if (index == 0)
775  {
776  trackStop = m_nTracksFromIntermediates[0];
777  }
778  else
779  {
780  for (int i = 0; i < index; ++i)
781  {
782  trackStart += m_nTracksFromIntermediates[i];
783  }
784  trackStop = trackStart + m_nTracksFromIntermediates[index];
785  }
786 
787  for (unsigned int i = trackStart; i < trackStop; ++i)
788  {
789  requiredIntermediateDecayProducts.push_back(m_daughters_ID[i]);
790  }
791 
792  for (HepMC::GenVertex::particle_iterator grandchildren = particle->end_vertex()->particles_begin(HepMC::children);
793  grandchildren != particle->end_vertex()->particles_end(HepMC::children); ++grandchildren)
794  {
795  if (Verbosity() >= VERBOSITY_MAX)
796  {
797  std::cout << "----grandchildren->pdg_id(): " << (*grandchildren)->pdg_id() << std::endl;
798  }
799 
801  std::abs((*grandchildren)->pdg_id())) != std::end(listOfResonantPIDs))
802  {
803  for (HepMC::GenVertex::particle_iterator greatgrandchildren = (*grandchildren)->end_vertex()->particles_begin(HepMC::children);
804  greatgrandchildren != (*grandchildren)->end_vertex()->particles_end(HepMC::children); ++greatgrandchildren)
805  {
806  if (Verbosity() >= VERBOSITY_MAX)
807  {
808  std::cout << "--------greatgrandchildren->pdg_id(): " << (*greatgrandchildren)->pdg_id() << std::endl;
809  }
810 
811  if ((m_allowPhotons && (*greatgrandchildren)->pdg_id() == 22) || (m_allowPi0 && (*greatgrandchildren)->pdg_id() == 111))
812  {
813  continue;
814  }
815  else if ((!m_allowPhotons && (*greatgrandchildren)->pdg_id() == 22) || (!m_allowPi0 && (*greatgrandchildren)->pdg_id() == 111))
816  {
817  break;
818  }
819  else
820  {
821  actualIntermediateDecayProducts.push_back((*greatgrandchildren)->pdg_id());
822  decayChain.push_back(std::make_pair(std::make_pair(m_genevt->get_embedding_id(), (*greatgrandchildren)->barcode()), (*greatgrandchildren)->pdg_id()));
824 
825  HepMC::FourVector myFourVector = (*greatgrandchildren)->momentum();
826 
827  HepMC::GenVertex* thisVtx = (*greatgrandchildren)->production_vertex();
828  double vtxPos[3] = {thisVtx->point3d().x(), thisVtx->point3d().y(), thisVtx->point3d().z()};
830  {
831  recalculateEta(myFourVector.py(), vtxPos);
832  }
833 
834  if (myFourVector.perp() < m_pt_req)
835  {
836  trackFailedPT = true;
837  }
838  if (!isInRange(m_eta_low_req, myFourVector.eta(), m_eta_high_req))
839  {
840  trackFailedETA = true;
841  }
842 
843  if (Verbosity() >= VERBOSITY_MAX)
844  {
845  std::cout << "pT = " << myFourVector.perp() << ", eta = " << myFourVector.eta() << std::endl;
846  std::string ptPass = trackFailedPT ? "passed" : "failed";
847  std::string etaPass = trackFailedETA ? "passed" : "failed";
848  std::cout << "The track " << ptPass << " the pT requirement, the track " << etaPass << " the eta requirement." << std::endl;
849  }
850  }
851  }
852  }
853  else if ((m_allowPhotons && (*grandchildren)->pdg_id() == 22) || (m_allowPi0 && (*grandchildren)->pdg_id() == 111))
854  {
855  continue;
856  }
857  else if ((!m_allowPhotons && (*grandchildren)->pdg_id() == 22) || (!m_allowPi0 && (*grandchildren)->pdg_id() == 111))
858  {
859  break;
860  }
861  else
862  {
863  actualIntermediateDecayProducts.push_back((*grandchildren)->pdg_id());
864  decayChain.push_back(std::make_pair(std::make_pair(m_genevt->get_embedding_id(), (*grandchildren)->barcode()), (*grandchildren)->pdg_id()));
866 
867  HepMC::FourVector myFourVector = (*grandchildren)->momentum();
868 
869  HepMC::GenVertex* thisVtx = (*grandchildren)->production_vertex();
870  double vtxPos[3] = {thisVtx->point3d().x(), thisVtx->point3d().y(), thisVtx->point3d().z()};
872  {
873  recalculateEta(myFourVector.py(), vtxPos);
874  }
875 
876  if (myFourVector.perp() < m_pt_req)
877  {
878  trackFailedPT = true;
879  }
880  if (!isInRange(m_eta_low_req, myFourVector.eta(), m_eta_high_req))
881  {
882  trackFailedETA = true;
883  }
884 
885  if (Verbosity() >= VERBOSITY_MAX)
886  {
887  std::cout << "pT = " << myFourVector.perp() << ", eta = " << myFourVector.eta() << std::endl;
888  std::string ptPass = trackFailedPT ? "passed" : "failed";
889  std::string etaPass = trackFailedETA ? "passed" : "failed";
890  std::cout << "The track " << ptPass << " the pT requirement, the track " << etaPass << " the eta requirement." << std::endl;
891  }
892  }
893  }
894 
895  acceptParticle = compareDecays(requiredIntermediateDecayProducts, actualIntermediateDecayProducts);
896  }
897  else if ((particle->pdg_id() == 22) || (particle->pdg_id() == 111))
898  {
899  return false;
900  }
901  else
902  {
903  if (Verbosity() >= VERBOSITY_MAX)
904  {
905  std::cout << "This is a final state track" << std::endl;
906  }
907  const HepMC::FourVector& myFourVector = particle->momentum();
908 
909  HepMC::GenVertex* thisVtx = particle->production_vertex();
910  double vtxPos[3] = {thisVtx->point3d().x(), thisVtx->point3d().y(), thisVtx->point3d().z()};
912  {
913  recalculateEta(myFourVector.py(), vtxPos);
914  }
915 
916  if (myFourVector.perp() < m_pt_req)
917  {
918  trackFailedPT = true;
919  }
920  if (!isInRange(m_eta_low_req, myFourVector.eta(), m_eta_high_req))
921  {
922  trackFailedETA = true;
923  }
924 
925  if (Verbosity() >= VERBOSITY_MAX)
926  {
927  std::cout << "pT = " << myFourVector.perp() << ", eta = " << myFourVector.eta() << std::endl;
928  std::string ptPass = trackFailedPT ? "passed" : "failed";
929  std::string etaPass = trackFailedETA ? "passed" : "failed";
930  std::cout << "The track " << ptPass << " the pT requirement, the track " << etaPass << " the eta requirement." << std::endl;
931  }
932  acceptParticle = true;
933  }
934 
935  return acceptParticle;
936 }
937 
938 /*
939  * Checks if this prticle really matches your request
940  */
941 bool DecayFinder::checkIfCorrectGeant4Particle(PHG4Particle* particle, bool& hasPhoton, bool& hasPi0, bool& trackFailedPT, bool& trackFailedETA)
942 {
943  bool acceptParticle = false;
944 
945  std::vector<int> positive_intermediates_ID;
946  for (int i : m_intermediates_ID)
947  {
948  positive_intermediates_ID.push_back(abs(i));
949  }
950  // Check if it is an intermediate or a final track
951  if (std::find(positive_intermediates_ID.begin(), positive_intermediates_ID.end(),
952  abs(particle->get_pid())) != positive_intermediates_ID.end())
953  {
954  std::vector<int> requiredIntermediateDecayProducts;
955  std::vector<int> actualIntermediateDecayProducts;
956  // Which intermediate decay list to we need
957  auto it = std::find(positive_intermediates_ID.begin(), positive_intermediates_ID.end(), abs(particle->get_pid()));
958  int index = std::distance(positive_intermediates_ID.begin(), it);
959 
960  unsigned int trackStart = 0, trackStop = 0;
961  if (index == 0)
962  {
963  trackStop = m_nTracksFromIntermediates[0];
964  }
965  else
966  {
967  for (int i = 0; i < index; ++i)
968  {
969  trackStart += m_nTracksFromIntermediates[i];
970  }
971  trackStop = trackStart + m_nTracksFromIntermediates[index];
972  }
973 
974  std::vector<int> positive_intermediateDecayProducts;
975  for (unsigned int i = trackStart; i < trackStop; ++i)
976  {
977  positive_intermediateDecayProducts.push_back(abs(m_daughters_ID[i]));
978  requiredIntermediateDecayProducts.push_back(m_daughters_ID[i]);
979  }
980 
981  bool fakeBreak = false;
982  searchGeant4Record(particle->get_barcode(), particle->get_pid(), positive_intermediateDecayProducts, fakeBreak,
983  hasPhoton, hasPi0, trackFailedPT, trackFailedETA, actualIntermediateDecayProducts);
984 
985  acceptParticle = compareDecays(requiredIntermediateDecayProducts, actualIntermediateDecayProducts);
986  }
987  else if ((particle->get_pid() == 22) || (particle->get_pid() == 111))
988  {
989  return false;
990  }
991  else
992  {
993  if (Verbosity() >= VERBOSITY_MAX)
994  {
995  std::cout << "This is a final state track" << std::endl;
996  }
997  double px = particle->get_px();
998  double py = particle->get_py();
999  double pz = particle->get_pz();
1000 
1001  double pt = std::sqrt(std::pow(px, 2) + std::pow(py, 2));
1002  double p = std::sqrt(std::pow(px, 2) + std::pow(py, 2) + std::pow(pz, 2));
1003  double eta = 0.5 * std::log((p + pz) / (p - pz));
1004 
1005  PHG4VtxPoint* thisVtx = m_truthinfo->GetVtx(particle->get_vtx_id());
1006  double vtxPos[3] = {thisVtx->get_x(), thisVtx->get_y(), thisVtx->get_z()};
1008  {
1009  recalculateEta(py, vtxPos);
1010  }
1011 
1012  if (pt < m_pt_req)
1013  {
1014  trackFailedPT = true;
1015  }
1017  {
1018  trackFailedETA = true;
1019  }
1020 
1021  if (Verbosity() >= VERBOSITY_MAX)
1022  {
1023  std::cout << "pT = " << pt << ", eta = " << eta << std::endl;
1024  std::string ptPass = trackFailedPT ? "passed" : "failed";
1025  std::string etaPass = trackFailedETA ? "passed" : "failed";
1026  std::cout << "The track " << ptPass << " the pT requirement, the track " << etaPass << " the eta requirement." << std::endl;
1027  }
1028 
1029  acceptParticle = true;
1030  }
1031 
1032  return acceptParticle;
1033 }
1034 
1035 /*
1036  * Check if the decay matches what we need
1037  */
1038 bool DecayFinder::compareDecays(std::vector<int> required, std::vector<int> actual)
1039 {
1040  bool accept = false;
1041  multiplyVectorByScalarAndSort(required, +1);
1042  multiplyVectorByScalarAndSort(actual, +1);
1043 
1044  if (Verbosity() >= VERBOSITY_MAX)
1045  {
1046  std::cout << "Printing required decay products: ";
1047  for (int i : required)
1048  {
1049  std::cout << i << ", ";
1050  }
1051  std::cout << std::endl;
1052  std::cout << "Printing actual decay products: ";
1053  for (int i : actual)
1054  {
1055  std::cout << i << ", ";
1056  }
1057  std::cout << std::endl;
1058  if (required == actual)
1059  {
1060  std::cout << "*\n* These vectors match\n*\n"
1061  << std::endl;
1062  }
1063  else
1064  {
1065  std::cout << "*\n* These vectors DONT match\n*\n"
1066  << std::endl;
1067  }
1068  }
1069 
1070  if (required == actual)
1071  {
1072  accept = true;
1073  }
1074 
1075  if (m_getChargeConjugate && !accept)
1076  {
1077  multiplyVectorByScalarAndSort(required, -1);
1078  if (required == actual)
1079  {
1080  accept = true;
1081  }
1082 
1083  if (Verbosity() >= VERBOSITY_MAX)
1084  {
1085  std::cout << "Checking CC state" << std::endl;
1086  if (required == actual)
1087  {
1088  std::cout << "*\n* These vectors match\n*\n"
1089  << std::endl;
1090  }
1091  else
1092  {
1093  std::cout << "*\n* These vectors DONT match\n*\n"
1094  << std::endl;
1095  }
1096  }
1097  }
1098  return accept;
1099 }
1100 
1101 /*
1102  * Everything below this is helper functions
1103  */
1104 int DecayFinder::deleteElement(int arr[], int n, int x)
1105 {
1106  // https://www.geeksforgeeks.org/delete-an-element-from-array-using-two-traversals-and-one-traversal/
1107  // Search x in array
1108  int i;
1109  for (i = 0; i < n; i++)
1110  {
1111  if (arr[i] == x)
1112  {
1113  break;
1114  }
1115  }
1116 
1117  // If x found in array
1118  if (i < n)
1119  {
1120  // reduce size of array and move all
1121  // elements on space ahead
1122  n = n - 1;
1123  for (int j = i; j < n; j++)
1124  {
1125  arr[j] = arr[j + 1];
1126  }
1127  }
1128 
1129  return n;
1130 }
1131 
1133 {
1134  bool particleFound = true;
1135  if (!TDatabasePDG::Instance()->GetParticle(particle.c_str()))
1136  {
1137  if (Verbosity() >= VERBOSITY_SOME)
1138  {
1139  std::cout << "The particle, " << particle << " is not in the TDatabasePDG particle list" << std::endl;
1140  }
1141  particleFound = false;
1142  }
1143 
1144  return particleFound;
1145 }
1146 
1147 void DecayFinder::multiplyVectorByScalarAndSort(std::vector<int>& v, int k)
1148 {
1149  // https://slaystudy.com/c-multiply-vector-by-scalar/
1150  std::transform(v.begin(), v.end(), v.begin(), [k](const int& c)
1151  { return c * k; });
1152  std::sort(v.begin(), v.end());
1153 }
1154 
1156 {
1157  if (findParticle(name))
1158  {
1159  return TDatabasePDG::Instance()->GetParticle(name.c_str())->PdgCode();
1160  }
1161  else
1162  {
1163  return 0;
1164  }
1165 }
1166 
1168 {
1169  if (findParticle(name))
1170  {
1171  return TDatabasePDG::Instance()->GetParticle(name.c_str())->Charge() / 3;
1172  }
1173  else
1174  {
1175  return -99;
1176  }
1177 }
1178 
1179 bool DecayFinder::isInRange(float min, float value, float max)
1180 {
1181  return min <= value && value <= max;
1182 }
1183 
1184 void DecayFinder::calculateEffectiveTPCradius(double vertex[3], double& effective_top_r, double& effective_bottom_r)
1185 {
1186  double r_sq = std::pow(m_tpc_r, 2);
1187  double x_sq = std::pow(vertex[0], 2);
1188 
1189  effective_top_r = std::sqrt(r_sq - x_sq) - vertex[1];
1190  effective_bottom_r = std::sqrt(r_sq - x_sq) + vertex[1];
1191 }
1192 
1193 void DecayFinder::recalculateEta(double py, double vertex[3])
1194 {
1196 
1197  double z_north = m_tpc_z - vertex[2]; // Distance between north end of TPC and vertex z position
1198  double z_south = m_tpc_z + vertex[2]; // Same but for south of TPC
1199 
1200  double top_north_theta = atan(m_effective_top_tpc_r / z_north);
1201  double bottom_north_theta = atan(m_effective_bottom_tpc_r / z_north);
1202  double top_south_theta = atan(m_effective_top_tpc_r / z_south);
1203  double bottom_south_theta = atan(m_effective_bottom_tpc_r / z_south);
1204 
1205  double top_north_eta = -1 * std::log(tan(top_north_theta / 2));
1206  double bottom_north_eta = -1 * std::log(tan(bottom_north_theta / 2));
1207  double top_south_eta = std::log(tan(top_south_theta / 2));
1208  double bottom_south_eta = std::log(tan(bottom_south_theta / 2));
1209 
1210  m_eta_high_req = py < 0 ? bottom_north_eta : top_north_eta;
1211  m_eta_low_req = py < 0 ? bottom_south_eta : top_south_eta;
1212 }
1213 
1215 {
1216  PHNodeIterator iter(topNode);
1217 
1218  PHCompositeNode* lowerNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
1219  if (!lowerNode)
1220  {
1221  lowerNode = new PHCompositeNode("DST");
1222  topNode->addNode(lowerNode);
1223  std::cout << "DST node added" << std::endl;
1224  }
1225 
1226  std::string baseName;
1227 
1228  if (m_container_name.empty())
1229  {
1230  // baseName = "decay";
1231  baseName = Name();
1232  }
1233  else
1234  {
1235  baseName = m_container_name;
1236  }
1237 
1238  // Cant have forward slashes in DST or else you make a subdirectory on save!!!
1239  size_t pos;
1240  std::string undrscr = "_";
1241  std::string nothing = "";
1242  std::map<std::string, std::string> forbiddenStrings;
1243  forbiddenStrings["/"] = undrscr;
1244  forbiddenStrings["("] = undrscr;
1245  forbiddenStrings[")"] = nothing;
1246  forbiddenStrings["+"] = "plus";
1247  forbiddenStrings["-"] = "minus";
1248  forbiddenStrings["*"] = "star";
1249  forbiddenStrings[" "] = nothing;
1250  for (auto const& [badString, goodString] : forbiddenStrings)
1251  {
1252  while ((pos = baseName.find(badString)) != std::string::npos)
1253  {
1254  baseName.replace(pos, 1, goodString);
1255  }
1256  }
1257 
1258  m_nodeName = baseName + "_DecayMap";
1259 
1261  PHIODataNode<PHObject>* decayNode = new PHIODataNode<PHObject>(m_decayMap, m_nodeName.c_str(), "PHObject");
1262  lowerNode->addNode(decayNode);
1263  std::cout << m_nodeName << " node added" << std::endl;
1264 
1266 }
1267 
1269 {
1270  m_decayMap = findNode::getClass<DecayFinderContainer_v1>(topNode, m_nodeName.c_str());
1271  m_decayMap->insert(decay);
1272 }
1273 
1275 {
1276  std::cout << "\n---------------DecayFinder information---------------" << std::endl;
1277  std::cout << "Module name: " << Name() << std::endl;
1278  std::cout << "Decay descriptor: " << m_decayDescriptor << std::endl;
1279  std::cout << "Number of generated decays: " << m_counter << std::endl;
1280  std::cout << " Number of decays that failed pT requirement: " << m_nCandFail_pT << std::endl;
1281  std::cout << " Number of decays that failed eta requirement: " << m_nCandFail_eta << std::endl;
1282  std::cout << " Number of decays that failed pT and eta requirements: " << m_nCandFail_pT_and_eta << std::endl;
1283  std::cout << "Number of decays that could be reconstructed: " << m_nCandReconstructable << std::endl;
1284  std::cout << " Number of reconstructable mothers with associated photon: " << m_nCandHas_Photon << std::endl;
1285  std::cout << " Number of reconstructable mothers with associated Pi0: " << m_nCandHas_Pi0 << std::endl;
1286  std::cout << " Number of reconstructable mothers with associated photon and Pi0: " << m_nCandHas_Photon_and_Pi0 << std::endl;
1287  std::cout << " Number of reconstructable mothers with no associated photons or Pi0s: " << m_nCandHas_noPhoton_and_noPi0 << std::endl;
1288  std::cout << "-----------------------------------------------------\n";
1289  std::cout << std::endl;
1290 }
1291 
1293 {
1294  std::cout << "----------------";
1295  std::cout << " DecayFinderNode: " << m_nodeName << " information ";
1296  std::cout << "----------------" << std::endl;
1297  DecayFinderContainer_v1* map = findNode::getClass<DecayFinderContainer_v1>(topNode, m_nodeName.c_str());
1298  for (auto& iter : *map)
1299  {
1300  Decay decay = iter.second;
1301  for (auto& i : decay)
1302  {
1303  std::cout << "Particle embedding ID: " << i.first.first << ", particle barcode: " << i.first.second << ", particle PDG ID: " << i.second << std::endl;
1304  }
1305  }
1306  std::cout << "--------------------------------------------------------------------------------------------------" << std::endl;
1307 }