Belle II Software  release-06-01-15
ParticleCombiner.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <analysis/ParticleCombiner/ParticleCombiner.h>
10 
11 #include <analysis/DecayDescriptor/DecayDescriptor.h>
12 #include <analysis/DecayDescriptor/DecayDescriptorParticle.h>
13 
14 #include <framework/logging/Logger.h>
15 
16 #include <mdst/dataobjects/ECLCluster.h>
17 
18 #include <algorithm>
19 
20 namespace Belle2 {
26  void ParticleIndexGenerator::init(const std::vector<unsigned>& _sizes)
27  {
28 
29  m_numberOfLists = _sizes.size();
30  sizes = _sizes;
31 
32  m_iCombination = 0;
33 
34  indices.resize(m_numberOfLists);
35  for (unsigned int i = 0; i < m_numberOfLists; ++i) {
36  indices[i] = 0;
37  }
38 
39  m_nCombinations = 1;
40  for (unsigned int i = 0; i < m_numberOfLists; ++i)
41  m_nCombinations *= sizes[i];
42 
43  if (m_numberOfLists == 0) {
44  m_nCombinations = 0;
45  }
46 
47  }
48 
50  {
51 
53  return false;
54  }
55 
56  if (m_iCombination > 0) {
57 
58  // TF SC this does not yet account for double counting so will produce:
59  // { 000, 100, 200, ... 010, 110, .... } even if the first and second
60  // place are the same particle list
61  for (unsigned int i = 0; i < m_numberOfLists; i++) {
62  indices[i]++;
63  if (indices[i] < sizes[i]) break;
64  indices[i] = 0;
65  }
66 
67  }
68 
70  return true;
71  }
72 
73  const std::vector<unsigned int>& ParticleIndexGenerator::getCurrentIndices() const
74  {
75  return indices;
76  }
77 
78  void ListIndexGenerator::init(unsigned int _numberOfLists)
79  {
80  m_numberOfLists = _numberOfLists;
81  m_types.resize(m_numberOfLists);
82 
83  m_iCombination = 0;
84  m_nCombinations = (1 << m_numberOfLists) - 1;
85 
86  }
87 
89  {
90 
92  return false;
93 
94  for (unsigned int i = 0; i < m_numberOfLists; ++i) {
95  bool useSelfConjugatedDaughterList = (m_iCombination & (1 << i));
96  m_types[i] = useSelfConjugatedDaughterList ? ParticleList::c_SelfConjugatedParticle : ParticleList::c_FlavorSpecificParticle;
97  }
98 
100  return true;
101  }
102 
103  const std::vector<ParticleList::EParticleType>& ListIndexGenerator::getCurrentIndices() const
104  {
105  return m_types;
106  }
107 
108  ParticleGenerator::ParticleGenerator(const std::string& decayString, const std::string& cutParameter) : m_iParticleType(0),
109  m_listIndexGenerator(),
110  m_particleIndexGenerator()
111  {
112 
113  DecayDescriptor decaydescriptor;
114  bool valid = decaydescriptor.init(decayString);
115  if (!valid)
116  B2ERROR("Invalid input DecayString: " << decayString);
117 
118  m_properties = decaydescriptor.getProperty();
119 
120  // Mother particle
121  const DecayDescriptorParticle* mother = decaydescriptor.getMother();
122  m_pdgCode = mother->getPDGCode();
123  m_properties |= mother->getProperty();
124 
125  // Daughters
126  m_numberOfLists = decaydescriptor.getNDaughters();
127  for (unsigned int i = 0; i < m_numberOfLists; ++i) {
128  // Get the mother of the subdecaystring of the ith daughter
129  // eg. "B -> [D -> K pi] [tau -> pi pi pi]". The 0th daughter is the
130  // *decaystring* D -> K pi whose mother is the D.
131  const DecayDescriptorParticle* daughter = decaydescriptor.getDaughter(i)->getMother();
132  StoreObjPtr<ParticleList> list(daughter->getFullName());
133  m_plists.push_back(list);
134 
135  int daughterProperty = daughter->getProperty();
136  m_daughterProperties.push_back(daughterProperty);
137  }
138 
139  m_cut = Variable::Cut::compile(cutParameter);
140 
141  m_isSelfConjugated = decaydescriptor.isSelfConjugated();
142 
143  // check if input lists can contain copies
144  // remember (list_i, list_j) pairs, where list_i and list_j can contain copies
145  m_inputListsCollide = false;
146  m_collidingLists.clear();
147  for (unsigned int i = 0; i < m_numberOfLists; ++i) {
148  const DecayDescriptorParticle* daughter_i = decaydescriptor.getDaughter(i)->getMother();
149  for (unsigned int j = i + 1; j < m_numberOfLists; ++j) {
150  const DecayDescriptorParticle* daughter_j = decaydescriptor.getDaughter(j)->getMother();
151 
152  if (abs(daughter_i->getPDGCode()) != abs(daughter_j->getPDGCode()))
153  continue;
154 
155  if (daughter_i->getLabel() != daughter_j->getLabel()) {
156  m_inputListsCollide = true;
157  m_collidingLists.emplace_back(i, j);
158  }
159  }
160  }
161 
162  }
163 
164 
165  ParticleGenerator::ParticleGenerator(const DecayDescriptor& decaydescriptor, const std::string& cutParameter) : m_iParticleType(0),
166  m_listIndexGenerator(),
167  m_particleIndexGenerator()
168  {
169  bool valid = decaydescriptor.isInitOK();
170  if (!valid)
171  B2ERROR("Given decaydescriptor failed to initialized");
172 
173  m_properties = decaydescriptor.getProperty();
174 
175  // Mother particle
176  const DecayDescriptorParticle* mother = decaydescriptor.getMother();
177  m_pdgCode = mother->getPDGCode();
178  m_properties |= mother->getProperty();
179 
180  // Daughters
181  m_numberOfLists = decaydescriptor.getNDaughters();
182  for (unsigned int i = 0; i < m_numberOfLists; ++i) {
183  // Get the mother of the subdecaystring of the ith daughter
184  // eg. "B -> [D -> K pi] [tau -> pi pi pi]". The 0th daughter is the
185  // *decaystring* D -> K pi whose mother is the D.
186  const DecayDescriptorParticle* daughter = decaydescriptor.getDaughter(i)->getMother();
187  StoreObjPtr<ParticleList> list(daughter->getFullName());
188  m_plists.push_back(list);
189 
190  int daughterProperty = daughter->getProperty();
191  m_daughterProperties.push_back(daughterProperty);
192  }
193 
194  m_cut = Variable::Cut::compile(cutParameter);
195 
196  m_isSelfConjugated = decaydescriptor.isSelfConjugated();
197 
198  // check if input lists can contain copies
199  // remember (list_i, list_j) pairs, where list_i and list_j can contain copies
200  m_inputListsCollide = false;
201  m_collidingLists.clear();
202  for (unsigned int i = 0; i < m_numberOfLists; ++i) {
203  const DecayDescriptorParticle* daughter_i = decaydescriptor.getDaughter(i)->getMother();
204  for (unsigned int j = i + 1; j < m_numberOfLists; ++j) {
205  const DecayDescriptorParticle* daughter_j = decaydescriptor.getDaughter(j)->getMother();
206 
207  if (abs(daughter_i->getPDGCode()) != abs(daughter_j->getPDGCode()))
208  continue;
209 
210  if (daughter_i->getLabel() != daughter_j->getLabel()) {
211  m_inputListsCollide = true;
212  m_collidingLists.emplace_back(i, j);
213  }
214  }
215  }
216 
217  }
218 
220  {
221  m_iParticleType = 0;
222 
223  m_particleIndexGenerator.init(std::vector<unsigned int> {}); // ParticleIndexGenerator will be initialised on first call
224  m_listIndexGenerator.init(m_numberOfLists); // ListIndexGenerator must be initialised here!
225  m_usedCombinations.clear();
226  m_indices.resize(m_numberOfLists);
228 
231  }
232 
234  {
235  m_indicesToUniqueIDs.clear();
236 
237  unsigned inputParticlesCount = 0;
238  for (unsigned int i = 0; i < m_numberOfLists; ++i)
239  inputParticlesCount += m_plists[i]->getListSize();
240 
241  m_indicesToUniqueIDs.reserve(inputParticlesCount);
242 
243  int uniqueID = 1;
244 
245  for (auto& collidingList : m_collidingLists) {
246  StoreObjPtr<ParticleList> listA = m_plists[collidingList.first];
247  StoreObjPtr<ParticleList> listB = m_plists[collidingList.second];
248 
249  bool sameSign = (listA->getPDGCode() == listB->getPDGCode());
250 
251  // if sameSign == true then
252  // 1. compare FS to FS particles in lists A and B
253  // 2. compare anti-FS to anti-FS particles in lists A and B
254  // else
255  // 1. compare FS to anti-FS particles in lists A and B
256  // 2. compare anti-FS to FS particles in lists A and B
257  // and in either case compare
258  // 3. compare SC to SC particles in lists A and B
259  if (sameSign) {
260  fillIndicesToUniqueIDMap(listA->getList(ParticleList::c_FlavorSpecificParticle, false),
261  listB->getList(ParticleList::c_FlavorSpecificParticle, false), uniqueID);
262  fillIndicesToUniqueIDMap(listA->getList(ParticleList::c_FlavorSpecificParticle, true),
263  listB->getList(ParticleList::c_FlavorSpecificParticle, true), uniqueID);
264  } else {
265  fillIndicesToUniqueIDMap(listA->getList(ParticleList::c_FlavorSpecificParticle, false),
266  listB->getList(ParticleList::c_FlavorSpecificParticle, true), uniqueID);
267  fillIndicesToUniqueIDMap(listA->getList(ParticleList::c_FlavorSpecificParticle, true),
268  listB->getList(ParticleList::c_FlavorSpecificParticle, false), uniqueID);
269  }
270 
271  fillIndicesToUniqueIDMap(listA->getList(ParticleList::c_SelfConjugatedParticle),
272  listB->getList(ParticleList::c_SelfConjugatedParticle), uniqueID);
273  }
274 
275  // assign unique ids to others as well
276  for (unsigned i = 0; i < m_numberOfLists; i++) {
278 
279  fillIndicesToUniqueIDMap(listA->getList(ParticleList::c_FlavorSpecificParticle, true), uniqueID);
280  fillIndicesToUniqueIDMap(listA->getList(ParticleList::c_FlavorSpecificParticle, false), uniqueID);
281  fillIndicesToUniqueIDMap(listA->getList(ParticleList::c_SelfConjugatedParticle), uniqueID);
282  }
283  }
284 
285  void ParticleGenerator::fillIndicesToUniqueIDMap(const std::vector<int>& listA, const std::vector<int>& listB, int& uniqueID)
286  {
287  const Particle* A, *B;
288  bool copies = false;
289  for (int i : listA) {
290  bool aIsAlreadyIn = m_indicesToUniqueIDs.count(i) ? true : false;
291 
292  if (not aIsAlreadyIn)
293  m_indicesToUniqueIDs[ i ] = uniqueID++;
294 
295  for (int j : listB) {
296  bool bIsAlreadyIn = m_indicesToUniqueIDs.count(j) ? true : false;
297 
298  if (bIsAlreadyIn)
299  continue;
300 
301  // are these two particles copies
302  A = m_particleArray[ i ];
303  B = m_particleArray[ j ];
304  copies = B->isCopyOf(A);
305 
306  if (copies)
308  }
309  }
310  }
311 
312 
313  void ParticleGenerator::fillIndicesToUniqueIDMap(const std::vector<int>& listA, int& uniqueID)
314  {
315  for (int i : listA) {
316  bool aIsAlreadyIn = m_indicesToUniqueIDs.count(i) ? true : false;
317 
318  if (not aIsAlreadyIn)
319  m_indicesToUniqueIDs[ i ] = uniqueID++;
320  }
321  }
322 
323 
324  bool ParticleGenerator::loadNext(bool loadAntiParticle)
325  {
326 
327  bool loadedNext = false;
334  while (true) {
335  if (m_iParticleType == 0) {
336  loadedNext = loadNextParticle(false);
337  } else if (m_iParticleType == 1 and loadAntiParticle) {
338  loadedNext = loadNextParticle(true);
339  } else if (m_iParticleType == 2) {
340  loadedNext = loadNextSelfConjugatedParticle();
341  } else {
342  return false;
343  }
344 
345  if (loadedNext) return true;
346  else ++m_iParticleType;
347 
348  if (m_iParticleType == 2) {
349  std::vector<unsigned int> sizes(m_numberOfLists);
350  for (unsigned int i = 0; i < m_numberOfLists; ++i) {
351  sizes[i] = m_plists[i]->getList(ParticleList::c_SelfConjugatedParticle, false).size();
352  }
354  } else {
356  }
357 
358  }
359  }
360 
361  bool ParticleGenerator::loadNextParticle(bool useAntiParticle)
362  {
363  while (true) {
364 
365  // Load next index combination if available
367 
368  const auto& m_types = m_listIndexGenerator.getCurrentIndices();
369  const auto& indices = m_particleIndexGenerator.getCurrentIndices();
370 
371  for (unsigned int i = 0; i < m_numberOfLists; i++) {
372  const auto& list = m_plists[i]->getList(m_types[i], m_types[i] == ParticleList::c_FlavorSpecificParticle ? useAntiParticle : false);
373  m_indices[i] = list[ indices[i] ];
375  }
376 
377  if (not currentCombinationHasDifferentSources()) continue;
378 
380  if (!m_cut->check(&m_current_particle))
381  continue;
382 
383  if (not currentCombinationIsUnique()) continue;
384 
385  if (not currentCombinationIsECLCRUnique()) continue;
386 
387  return true;
388  }
389 
390  // Load next list combination if available and reset indexCombiner
392  const auto& m_types = m_listIndexGenerator.getCurrentIndices();
393  std::vector<unsigned int> sizes(m_numberOfLists);
394  for (unsigned int i = 0; i < m_numberOfLists; ++i) {
395  sizes[i] = m_plists[i]->getList(m_types[i], m_types[i] == ParticleList::c_FlavorSpecificParticle ? useAntiParticle : false).size();
396  }
398  continue;
399  }
400  return false;
401  }
402 
403  }
404 
406  {
407  while (true) {
408 
409  // Load next index combination if available
411 
412  const auto& indices = m_particleIndexGenerator.getCurrentIndices();
413 
414  for (unsigned int i = 0; i < m_numberOfLists; i++) {
415  m_indices[i] = m_plists[i]->getList(ParticleList::c_SelfConjugatedParticle, false) [ indices[i] ];
417  }
418 
419  if (not currentCombinationHasDifferentSources()) continue;
420 
422  if (!m_cut->check(&m_current_particle))
423  continue;
424 
425  if (not currentCombinationIsUnique()) continue;
426 
427  if (not currentCombinationIsECLCRUnique()) continue;
428 
429  return true;
430  }
431 
432  return false;
433  }
434 
435  }
436 
438  {
439  //TLorentzVector performance is quite horrible, do it ourselves.
440  double px = 0;
441  double py = 0;
442  double pz = 0;
443  double E = 0;
444  for (const Particle* d : m_particles) {
445  px += d->getPx();
446  py += d->getPy();
447  pz += d->getPz();
448  E += d->getEnergy();
449  }
450  const TLorentzVector vec(px, py, pz, E);
451 
452  switch (m_iParticleType) {
455  m_particleArray.getPtr());
458  m_particleArray.getPtr());
459  case 2: return Particle(vec, m_pdgCode, Particle::c_Unflavored, m_indices,
461  m_particleArray.getPtr());
462  default: B2FATAL("You called getCurrentParticle although loadNext should have returned false!");
463  }
464 
465  return Particle(); // This should never happen
466  }
467 
469  {
470  return m_current_particle;
471  }
472 
474  {
475  std::vector<Particle*> stack = m_particles;
476  static std::vector<int> sources; // stack for particle sources
477  sources.clear();
478 
479  // recursively check all daughters and daughters of daughters
480  while (!stack.empty()) {
481  Particle* p = stack.back();
482  stack.pop_back();
483  const std::vector<int>& daughters = p->getDaughterIndices();
484 
485  if (daughters.empty()) {
486  int source = p->getMdstSource();
487  for (int i : sources) {
488  if (source == i) return false;
489  }
490  sources.push_back(source);
491  } else {
492  for (int daughter : daughters) stack.push_back(m_particleArray[daughter]);
493  }
494  }
495  return true;
496  }
497 
498 
500  {
501  std::set<int> indexSet;
502  if (not m_inputListsCollide)
503  indexSet.insert(m_indices.begin(), m_indices.end());
504  else
505  for (unsigned int i = 0; i < m_numberOfLists; i++)
506  indexSet.insert(m_indicesToUniqueIDs.at(m_indices[i]));
507 
508  bool elementInserted = m_usedCombinations.insert(indexSet).second;
509  return elementInserted;
510  }
511 
512  bool ParticleGenerator::inputListsCollide(const std::pair<unsigned, unsigned>& pair) const
513  {
514  for (const auto& collidingList : m_collidingLists)
515  if (pair == collidingList)
516  return true;
517 
518  return false;
519  }
520 
522  {
523  unsigned nECLSource = 0;
524  std::vector<Particle*> stack = m_particles;
525  static std::vector<int> connectedregions;
526  static std::vector<ECLCluster::EHypothesisBit> hypotheses;
527  connectedregions.clear();
528  hypotheses.clear();
529 
530  // recursively check all daughters and daughters of daughters
531  while (!stack.empty()) {
532  Particle* p = stack.back();
533  stack.pop_back();
534  const std::vector<int>& daughters = p->getDaughterIndices();
535 
536  if (daughters.empty()) {
537  // Only test if the particle was created from an ECLCluster at source.
538  // This CAN CHANGE if we change the cluster <--> track matching,
539  // (currently we match nPhotons clusters to all track type particles,
540  // i.e. electrons, pions, kaons etc. We might gain by matching
541  // neutralHadron hypothesis clusters to kaons, for example).
542  //
543  // In the above case one would need to extend the check to ALL
544  // particles with an associated ECLCluster. Then replace the following
545  // active line with two lines...
546  //
547  // auto cluster = p->getECLCluster();
548  // if (cluster) { // then do stuff
549  if (p->getParticleSource() == Particle::EParticleSourceObject::c_ECLCluster) {
550  nECLSource++;
551  auto* cluster = p->getECLCluster();
552  connectedregions.push_back(cluster->getConnectedRegionId());
553  hypotheses.push_back(p->getECLClusterEHypothesisBit());
554  }
555  } else {
556  for (int daughter : daughters) stack.push_back(m_particleArray[daughter]);
557  }
558  }
559 
560  // less than two particles from an ECL source is fine
561  // (unless cluster <--> track matching changes)
562  if (nECLSource < 2) return true;
563 
564  // yes this is a nested for loop but it's fast, we promise
565  for (unsigned icr = 0; icr < connectedregions.size(); ++icr)
566  for (unsigned jcr = icr + 1; jcr < connectedregions.size(); ++jcr)
567  if (connectedregions[icr] == connectedregions[jcr])
568  if (hypotheses[icr] != hypotheses[jcr]) return false;
569 
570  return true;
571  }
572 
573  int ParticleGenerator::getUniqueID(int index) const
574  {
575  if (not m_inputListsCollide)
576  return 0;
577 
578  if (not m_indicesToUniqueIDs.count(index))
579  return -1;
580 
581  return m_indicesToUniqueIDs.at(index);
582  }
584 }
Represents a particle in the DecayDescriptor.
int getPDGCode() const
Return PDG code.
std::string getLabel() const
The label of this particle, "default" returned, when no label set.
The DecayDescriptor stores information about a decay tree or parts of a decay tree.
bool init(const std::string &str)
Initialise the DecayDescriptor from given string.
bool isSelfConjugated() const
Is the decay or the particle self conjugated.
const DecayDescriptorParticle * getMother() const
return mother.
bool isInitOK() const
Check if the object initialized correctly.
int getNDaughters() const
return number of direct daughters.
int getProperty() const
return property of the particle.
const DecayDescriptor * getDaughter(int i) const
return i-th daughter (0 based index).
static std::unique_ptr< GeneralCut > compile(const std::string &cut)
Creates an instance of a cut and returns a unique_ptr to it, if you need a copy-able object instead y...
Definition: GeneralCut.h:104
unsigned int m_iCombination
The current position of the combination.
unsigned int m_numberOfLists
Number of lists which are combined.
std::vector< ParticleList::EParticleType > m_types
The current types of sublist of the ParticleLists for this combination.
unsigned int m_nCombinations
The total amount of combinations.
ListIndexGenerator m_listIndexGenerator
listIndexGenerator makes the combinations of the types of sublists of the ParticleLists
unsigned int m_iParticleType
The type of particle which is currently generated.
bool inputListsCollide() const
True if input lists collide (can contain copies of particles in the input lists).
std::unordered_set< std::set< int > > m_usedCombinations
already used combinations (as sets of indices or unique IDs).
Particle m_current_particle
The current Particle object generated by this combiner.
unsigned int m_numberOfLists
Number of lists which are combined.
std::vector< Particle * > m_particles
Pointers to the particle objects of the current combination.
std::unordered_map< int, int > m_indicesToUniqueIDs
map of store array indices of input Particles to their unique IDs.
std::unique_ptr< Variable::Cut > m_cut
cut object which performs the cuts
const StoreArray< Particle > m_particleArray
Global list of particles.
std::vector< int > m_daughterProperties
Daughter's particle properties.
ParticleIndexGenerator m_particleIndexGenerator
particleIndexGenerator makes the combinations of indices stored in the sublists of the ParticleLists
std::vector< StoreObjPtr< ParticleList > > m_plists
particle lists
int m_pdgCode
PDG Code of the particle which is combined.
int m_properties
Particle property.
bool m_inputListsCollide
True if the daughter lists can contain copies of Particles.
std::vector< int > m_indices
Indices stored in the ParticleLists of the current combination.
std::vector< std::pair< unsigned, unsigned > > m_collidingLists
pairs of lists that can contain copies.
bool m_isSelfConjugated
True if the combined particle is self-conjugated.
unsigned int m_iCombination
The current position of the combination.
unsigned int m_numberOfLists
Number of lists which are combined.
std::vector< unsigned int > indices
The indices of the current loaded combination.
std::vector< unsigned int > sizes
The sizes of the particle lists which are combined.
unsigned int m_nCombinations
The total amount of combinations.
Class to store reconstructed particles.
Definition: Particle.h:74
@ c_Unflavored
Is its own antiparticle or we don't know whether it is a particle/antiparticle.
Definition: Particle.h:93
@ c_Flavored
Is either particle or antiparticle.
Definition: Particle.h:94
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:95
void init()
Initialises the generator to produce the given type of sublist.
void fillIndicesToUniqueIDMap(const std::vector< int > &listA, const std::vector< int > &listB, int &uniqueID)
Assigns unique IDs to all particles in list A, which do not have the unique ID already assigned.
int getUniqueID(int index) const
Returns the unique ID assigned to Particle with given index from the IndicesToUniqueID map.
void init(const std::vector< unsigned int > &_sizes)
Initialises the generator to produce combinations with the given sizes of each particle list.
bool currentCombinationHasDifferentSources()
Check that all FS particles of a combination differ.
bool currentCombinationIsECLCRUnique()
Check that: if the current combination has at least two particles from an ECL source,...
const std::vector< unsigned int > & getCurrentIndices() const
Returns theindices of the current loaded combination.
bool loadNextSelfConjugatedParticle()
Loads the next combination.
bool currentCombinationIsUnique()
Check that the combination is unique.
bool loadNext(bool loadAntiParticle=true)
Loads the next combination.
void initIndicesToUniqueIDMap()
In the case input daughter particle lists collide (two or more lists contain copies of Particles) the...
const std::vector< ParticleList::EParticleType > & getCurrentIndices() const
Returns the type of the sublist of the current loaded combination.
Particle getCurrentParticle() const
Returns the particle.
ParticleGenerator(const std::string &decayString, const std::string &cutParameter="")
Initialises the generator to produce the given type of sublist.
void init(unsigned int _numberOfLists)
Initialises the generator to produce the given type of sublist.
Particle createCurrentParticle() const
Create current particle object.
bool loadNextParticle(bool useAntiParticle)
Loads the next combination.
bool loadNext()
Loads the next combination.
Abstract base class for different kinds of events.