Belle II Software  release-06-00-14
ParticleLoaderModule.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 // Own include
10 #include <analysis/modules/ParticleLoader/ParticleLoaderModule.h>
11 
12 // framework aux
13 #include <framework/logging/Logger.h>
14 #include <framework/core/ModuleParam.templateDetails.h>
15 
16 // utilities
17 #include <analysis/DecayDescriptor/ParticleListName.h>
18 #include <analysis/utility/PCmsLabTransform.h>
19 
20 #include <utility>
21 
22 using namespace std;
23 
24 namespace Belle2 {
30  //-----------------------------------------------------------------
31  // Register module
32  //-----------------------------------------------------------------
33 
34  REG_MODULE(ParticleLoader)
35 
36  //-----------------------------------------------------------------
37  // Implementation
38  //-----------------------------------------------------------------
39 
41 
42  {
43  setDescription("Loads MDST dataobjects as Particle objects to the StoreArray<Particle> and collects them in specified ParticleList.");
44  setPropertyFlags(c_ParallelProcessingCertified);
45 
46  // Add parameters
47  addParam("decayStrings", m_decayStrings,
48  "List of decay strings (see :ref:`DecayString` for syntax) that specify all output ParticleLists to be created by the module.",
49  {});
50 
51  addParam("useMCParticles", m_useMCParticles,
52  "Use MCParticles instead of reconstructed MDST dataobjects (tracks, ECL, KLM, clusters, V0s, ...)", false);
53 
54  addParam("useROEs", m_useROEs,
55  "Use ROE instead of reconstructed MDST dataobjects (tracks, ECL, KLM, clusters, V0s, ...)", false);
56 
57  addParam("roeMaskName", m_roeMaskName,
58  "ROE mask name to load", std::string(""));
59 
60  addParam("sourceParticleListName", m_sourceParticleListName,
61  "Particle list name from which we need to get ROEs", std::string(""));
62 
63  addParam("useMissing", m_useMissing,
64  "If true, the Particle List will be filled with missing momentum from the ROE and signal particle.", false);
65 
66  addParam("writeOut", m_writeOut,
67  "If true, the output ParticleList will be saved by RootOutput. If false, it will be ignored when writing the file.", false);
68 
69  addParam("addDaughters", m_addDaughters,
70  "If true, the particles from the bottom part of the selected particle's decay chain will also be created in the datastore and mother-daughter relations are recursively set",
71  false);
72 
73  addParam("skipNonPrimaryDaughters", m_skipNonPrimaryDaughters,
74  "If true, the secondary MC daughters will be skipped, default is false",
75  false);
76 
77  addParam("trackHypothesis", m_trackHypothesis,
78  "Track hypothesis to use when loading the particle. By default, use the particle's own hypothesis.",
79  0);
80 
81  addParam("enforceFitHypothesis", m_enforceFitHypothesis,
82  "If true, a Particle is only created if a track fit with the particle hypothesis passed to the ParticleLoader is available.",
83  m_enforceFitHypothesis);
84  }
85 
86  void ParticleLoaderModule::initialize()
87  {
88  B2INFO("ParticleLoader's Summary of Actions:");
89 
90  m_particles.registerInDataStore();
91  m_particleExtraInfoMap.registerInDataStore();
92  m_eventExtraInfo.registerInDataStore();
93  //register relations if these things exists
94  if (m_mcparticles.isOptional()) {
95  m_particles.registerRelationTo(m_mcparticles);
96  }
97  if (m_pidlikelihoods.isOptional()) {
98  m_particles.registerRelationTo(m_pidlikelihoods);
99  }
100  if (m_trackfitresults.isOptional()) {
101  m_particles.registerRelationTo(m_trackfitresults);
102  }
103 
104  if (m_useMCParticles) {
105  m_mcparticles.isRequired();
106  }
107 
108  if (m_decayStrings.empty()) {
109  B2WARNING("Obsolete usage of the ParticleLoader module (load all MDST objects as all possible Particle object types). Specify the particle type via decayStrings module parameter instead.");
110  } else {
111  for (auto decayString : m_decayStrings) {
112 
113  // obtain the output particle lists from the decay string
114  bool valid = m_decaydescriptor.init(decayString);
115  if (!valid)
116  B2ERROR("ParticleLoaderModule::initialize Invalid input DecayString: " << decayString);
117 
118  // Mother particle
119  const DecayDescriptorParticle* mother = m_decaydescriptor.getMother();
120  int nProducts = m_decaydescriptor.getNDaughters();
121 
122  int pdgCode = mother->getPDGCode();
123  // The default list name is "all"
124  string listName = mother->getName() + ":all";
125  // ROE particles get the full name
126  if (m_useROEs) listName = mother->getFullName();
127  // MC particles get the label "MC"
128  else if (m_useMCParticles) listName = mother->getName() + ":MC";
129  // V0s get the label "V0"
130  else if (nProducts > 0) listName = mother->getName() + ":V0";
131 
132  string antiListName = ParticleListName::antiParticleListName(listName);
133  bool isSelfConjugatedParticle = (listName == antiListName);
134 
135  StoreObjPtr<ParticleList> particleList(listName);
136  // if the particle list doesn't exist, we have to register it
137  if (!particleList.isOptional()) {
138  DataStore::EStoreFlags flags = m_writeOut ? DataStore::c_WriteOut : DataStore::c_DontWriteOut;
139  particleList.registerInDataStore(flags);
140  if (!isSelfConjugatedParticle) {
141  StoreObjPtr<ParticleList> antiParticleList(antiListName);
142  antiParticleList.registerInDataStore(flags);
143  }
144  }
145 
146  if (not isValidPDGCode(pdgCode) and (m_useMCParticles == false and m_useROEs == false))
147  B2ERROR("Invalid particle type requested to be loaded. Set a valid decayString module parameter.");
148 
149  // if we're not loading MCParticles and we are loading K0S, Lambdas, or photons --> ee then this decaystring is a V0
150  bool mdstSourceIsV0 = false;
151  if (!m_useMCParticles &&
152  (abs(pdgCode) == abs(Const::Kshort.getPDGCode()) || abs(pdgCode) == abs(Const::Lambda.getPDGCode())
153  || (abs(pdgCode) == abs(Const::photon.getPDGCode()) && m_addDaughters == true)))
154  mdstSourceIsV0 = true;
155 
156  if (mdstSourceIsV0 == false) {
157  if (nProducts > 0) {
158  if (!m_useROEs) {
159  B2ERROR("ParticleLoaderModule::initialize Invalid input DecayString " << decayString
160  << ". DecayString should not contain any daughters, only the mother particle.");
161  } else {
162  B2INFO("ParticleLoaderModule: Replacing the source particle list name by " <<
163  m_decaydescriptor.getDaughter(0)->getMother()->getFullName()
164  << " all other daughters will be ignored.");
165  m_sourceParticleListName = m_decaydescriptor.getDaughter(0)->getMother()->getFullName();
166  }
167  }
168  } else {
169  if (nProducts != 2)
170  B2ERROR("ParticleLoaderModule::initialize Invalid input DecayString " << decayString
171  << ". MDST source of the particle list is V0, DecayString should contain exactly two daughters, as well as the mother particle.");
172  else {
173  if (m_decaydescriptor.getDaughter(0)->getMother()->getPDGCode() * m_decaydescriptor.getDaughter(1)->getMother()->getPDGCode() > 0)
174  B2ERROR("MDST source of the particle list is V0, the two daughters should have opposite charge");
175  }
176  }
177 
178  // add PList to corresponding collection of Lists
179  B2INFO(" o) creating (anti-)ParticleList with name: " << listName << " (" << antiListName << ")");
180  if (m_useROEs) {
181  B2INFO(" -> MDST source: RestOfEvents");
182  m_ROE2Plists.emplace_back(pdgCode, listName, antiListName, isSelfConjugatedParticle);
183  } else if (m_useMCParticles) {
184  B2INFO(" -> MDST source: MCParticles");
185  m_MCParticles2Plists.emplace_back(pdgCode, listName, antiListName, isSelfConjugatedParticle);
186  } else {
187  bool chargedFSP = Const::chargedStableSet.contains(Const::ParticleType(abs(pdgCode)));
188  if (chargedFSP) {
189  B2INFO(" -> MDST source: Tracks");
190  m_Tracks2Plists.emplace_back(pdgCode, listName, antiListName, isSelfConjugatedParticle);
191  }
192 
193  if (abs(pdgCode) == abs(Const::photon.getPDGCode())) {
194  if (m_addDaughters == false) {
195  m_ECLKLMClusters2Plists.emplace_back(pdgCode, listName, antiListName, isSelfConjugatedParticle);
196  B2INFO(" -> MDST source: ECLClusters and KLMClusters");
197  } else {
198  B2INFO(" -> MDST source: V0");
199  m_V02Plists.emplace_back(pdgCode, listName, antiListName, isSelfConjugatedParticle);
200  }
201  }
202 
203  if (abs(pdgCode) == abs(Const::Kshort.getPDGCode())) {
204  B2INFO(" -> MDST source: V0");
205  m_V02Plists.emplace_back(pdgCode, listName, antiListName, isSelfConjugatedParticle);
206  }
207 
208  if (abs(pdgCode) == abs(Const::Klong.getPDGCode()) || abs(pdgCode) == abs(Const::neutron.getPDGCode())) {
209  B2INFO(" -> MDST source: exclusively KLMClusters or exclusively ECLClusters (matching between those not used)");
210  m_ECLKLMClusters2Plists.emplace_back(pdgCode, listName, antiListName, isSelfConjugatedParticle);
211  }
212 
213  if (abs(pdgCode) == abs(Const::Lambda.getPDGCode())) {
214  B2INFO(" -> MDST source: V0");
215  m_V02Plists.emplace_back(pdgCode, listName, antiListName, isSelfConjugatedParticle);
216  }
217  }
218  }
219  }
220 
221 
222  m_chargeZeroTrackCounts = std::vector<int>(m_Tracks2Plists.size(), 0);
223  m_sameChargeDaughtersV0Counts = std::vector<int>(m_V02Plists.size(), 0);
224  }
225 
226  void ParticleLoaderModule::event()
227  {
228  if (not m_particleExtraInfoMap) {
229  m_particleExtraInfoMap.create();
230  }
231 
232  if (m_useROEs)
233  roeToParticles();
234  else if (m_useMCParticles)
235  mcParticlesToParticles();
236  else {
237  tracksToParticles();
238  eclAndKLMClustersToParticles();
239  v0sToParticles();
240  }
241  }
242 
243  void ParticleLoaderModule::terminate()
244  {
245  // report track errors integrated
246  for (size_t i = 0; i < m_Tracks2Plists.size(); i++)
247  if (m_chargeZeroTrackCounts[i] > 0) {
248  auto track2Plist = m_Tracks2Plists[i];
249  B2WARNING("There were " << m_chargeZeroTrackCounts[i]
250  << " tracks skipped because of zero charge for "
251  << get<c_PListName>(track2Plist));
252  }
253  // report V0 errors integrated
254  for (size_t i = 0; i < m_V02Plists.size(); i++)
255  if (m_sameChargeDaughtersV0Counts[i] > 0) {
256  auto v02Plist = m_V02Plists[i];
257  B2WARNING("There were " << m_sameChargeDaughtersV0Counts[i]
258  << " v0s skipped because of same charge daughters for "
259  << get<c_PListName>(v02Plist));
260  }
261  }
262 
263  void ParticleLoaderModule::roeToParticles()
264  {
265  if (m_ROE2Plists.empty()) // nothing to do
266  return;
267  // Multiple particle lists are not supported
268  auto roe2Plist = m_ROE2Plists[0];
269  string listName = get<c_PListName>(roe2Plist);
270  string antiListName = get<c_AntiPListName>(roe2Plist);
271  int pdgCode = get<c_PListPDGCode>(roe2Plist);
272  bool isSelfConjugatedParticle = get<c_IsPListSelfConjugated>(roe2Plist);
273 
274  StoreObjPtr<ParticleList> plist(listName);
275  // since a particle list in the ParticleLoader always contains all possible objects
276  // we check whether it already exists in this path and can skip any further steps if it does
277  if (plist.isValid())
278  return;
279  plist.create();
280  plist->initialize(pdgCode, listName);
281 
282  if (!isSelfConjugatedParticle) {
283  StoreObjPtr<ParticleList> antiPlist(antiListName);
284  antiPlist.create();
285  antiPlist->initialize(-1 * pdgCode, antiListName);
286  antiPlist->bindAntiParticleList(*(plist));
287  }
288  if (m_sourceParticleListName != "") {
289  // Take related ROEs from a particle list
290  StoreObjPtr<ParticleList> pList(m_sourceParticleListName);
291  if (!pList.isValid())
292  B2FATAL("ParticleList " << m_sourceParticleListName << " could not be found or is not valid!");
293  for (unsigned int i = 0; i < pList->getListSize(); i++) {
294  RestOfEvent* roe = pList->getParticle(i)->getRelatedTo<RestOfEvent>("ALL");
295  if (!roe) {
296  B2ERROR("ParticleList " << m_sourceParticleListName << " has no associated ROEs!");
297  } else {
298  addROEToParticleList(roe, i, pdgCode, isSelfConjugatedParticle);
299  }
300  }
301 
302  } else {
303  // Take all ROE if no particle list provided
304  for (int i = 0; i < m_roes.getEntries(); i++) {
305  addROEToParticleList(m_roes[i], i);
306  }
307  }
308  }
309 
310  void ParticleLoaderModule::addROEToParticleList(RestOfEvent* roe, int mdstIndex, int pdgCode, bool isSelfConjugatedParticle)
311  {
312 
313  Particle* newPart = nullptr;
314  if (!m_useMissing) {
315  // Convert ROE to particle
316  newPart = roe->convertToParticle(m_roeMaskName, pdgCode, isSelfConjugatedParticle);
317  } else {
318  // Create a particle from missing momentum
319  auto* signalSideParticle = roe->getRelatedFrom<Particle>();
321  TLorentzVector boost4Vector = T.getBeamFourMomentum();
322 
323  TLorentzVector signal4Vector = signalSideParticle->get4Vector();
324  TLorentzVector roe4Vector = roe->get4Vector(m_roeMaskName);
325  TLorentzVector missing4Vector;
326  missing4Vector.SetVect(boost4Vector.Vect() - (signal4Vector.Vect() + roe4Vector.Vect()));
327  missing4Vector.SetE(missing4Vector.Vect().Mag());
328  auto isFlavored = (isSelfConjugatedParticle) ? Particle::EFlavorType::c_Unflavored : Particle::EFlavorType::c_Flavored;
329  newPart = m_particles.appendNew(missing4Vector, pdgCode, isFlavored, Particle::EParticleSourceObject::c_Undefined, mdstIndex);
330 
331  }
332  for (auto roe2Plist : m_ROE2Plists) {
333  string listName = get<c_PListName>(roe2Plist);
334  StoreObjPtr<ParticleList> plist(listName);
335  plist->addParticle(newPart);
336  }
337  }
338 
339 
340  void ParticleLoaderModule::v0sToParticles()
341  {
342  if (m_V02Plists.empty()) // nothing to do
343  return;
344 
345  // check if the order of the daughters in the decay string (decided by the user) is the same as the v0 daughters' order (fixed)
346  bool matchingDaughtersOrder = true;
347  if (m_decaydescriptor.getDaughter(0)->getMother()->getPDGCode() < 0
348  && m_decaydescriptor.getDaughter(1)->getMother()->getPDGCode() > 0)
349  matchingDaughtersOrder = false;
350 
351  // loop over all ParticleLists
352  for (size_t ilist = 0; ilist < m_V02Plists.size(); ilist++) {
353  auto v02Plist = m_V02Plists[ilist];
354  string listName = get<c_PListName>(v02Plist);
355  string antiListName = get<c_AntiPListName>(v02Plist);
356  int pdgCode = get<c_PListPDGCode>(v02Plist);
357  bool isSelfConjugatedParticle = get<c_IsPListSelfConjugated>(v02Plist);
358 
359  StoreObjPtr<ParticleList> plist(listName);
360  // since a particle list in the ParticleLoader always contains all possible objects
361  // we check whether it already exists in this path and can skip any further steps if it does
362  if (plist.isValid())
363  continue;
364  plist.create();
365  plist->initialize(pdgCode, listName);
366 
367  if (!isSelfConjugatedParticle) {
368  StoreObjPtr<ParticleList> antiPlist(antiListName);
369  antiPlist.create();
370  antiPlist->initialize(-1 * pdgCode, antiListName);
371 
372  antiPlist->bindAntiParticleList(*(plist));
373  }
374 
375  // load reconstructed V0s as Kshorts (pi-pi+ combination), Lambdas (p+pi- combinations), and converted photons (e-e+ combinations)
376  for (int i = 0; i < m_v0s.getEntries(); i++) {
377  const V0* v0 = m_v0s[i];
378  Const::ParticleType v0Type = v0->getV0Hypothesis();
379 
380  if (abs(pdgCode) != abs(v0Type.getPDGCode()))
381  continue;
382 
383  // check if the charge of the 2 V0's daughters is opposite
384  if (v0->getTrackFitResults().first->getChargeSign() == v0->getTrackFitResults().second->getChargeSign()) {
385  B2DEBUG(19, "V0 with same charge daughters skipped!");
386  m_sameChargeDaughtersV0Counts[ilist]++;
387  continue;
388  }
389 
390  Const::ChargedStable pTypeP(Const::pion);
391  Const::ChargedStable pTypeM(Const::pion);
392  Particle::EFlavorType v0FlavorType = Particle::c_Unflavored;
393 
394  if (v0Type.getPDGCode() == Const::Kshort.getPDGCode()) { // K0s -> pi+ pi-
395  pTypeP = Const::pion;
396  pTypeM = Const::pion;
397  } else if (v0Type.getPDGCode() == Const::Lambda.getPDGCode()) { // Lambda -> p+ pi-
398  pTypeP = Const::proton;
399  pTypeM = Const::pion;
400  v0FlavorType = Particle::c_Flavored; // K0s are not flavoured, lambdas are
401  } else if (v0Type.getPDGCode() == Const::antiLambda.getPDGCode()) { // anti-Lambda -> pi+ anti-p-
402  pTypeP = Const::pion;
403  pTypeM = Const::proton;
404  v0FlavorType = Particle::c_Flavored;
405  } else if (v0Type.getPDGCode() == Const::photon.getPDGCode()) { // gamma -> e+ e-
406  pTypeP = Const::electron;
407  pTypeM = Const::electron;
408  } else {
409  B2WARNING("Unknown V0 hypothesis!");
410  }
411 
412  // check if, given the initial user's decay descriptor, the current v0 is a particle or an anti-particle.
413  // in the V0 the order of the daughters is fixed, first the positive and then the negative; to be coherent with the decay descriptor, when creating
414  // one particle list and one anti-particle, the v0 daughters' order has to be switched only in one case
415  bool correctOrder = matchingDaughtersOrder;
416  if (abs(v0Type.getPDGCode()) == abs(m_decaydescriptor.getMother()->getPDGCode())
417  && v0Type.getPDGCode() != m_decaydescriptor.getMother()->getPDGCode())
418  correctOrder = !correctOrder;
419 
420  std::pair<Track*, Track*> v0Tracks = v0->getTracks();
421  std::pair<TrackFitResult*, TrackFitResult*> v0TrackFitResults = v0->getTrackFitResults();
422 
423  Particle daugP((v0Tracks.first)->getArrayIndex(), v0TrackFitResults.first, pTypeP);
424  Particle daugM((v0Tracks.second)->getArrayIndex(), v0TrackFitResults.second, pTypeM);
425 
426  const PIDLikelihood* pidP = (v0Tracks.first)->getRelated<PIDLikelihood>();
427  const PIDLikelihood* pidM = (v0Tracks.second)->getRelated<PIDLikelihood>();
428 
429  const auto& mcParticlePWithWeight = (v0Tracks.first)->getRelatedToWithWeight<MCParticle>();
430  const auto& mcParticleMWithWeight = (v0Tracks.second)->getRelatedToWithWeight<MCParticle>();
431 
432  // add V0 daughters to the Particle StoreArray
433  Particle* newDaugP;
434  Particle* newDaugM;
435 
436  if (correctOrder) {
437  newDaugP = m_particles.appendNew(daugP);
438  newDaugM = m_particles.appendNew(daugM);
439  } else {
440  newDaugM = m_particles.appendNew(daugM);
441  newDaugP = m_particles.appendNew(daugP);
442  }
443 
444  // if there are PIDLikelihoods and MCParticles then also add relations to the particles
445  if (pidP)
446  newDaugP->addRelationTo(pidP);
447  if (mcParticlePWithWeight.first)
448  newDaugP->addRelationTo(mcParticlePWithWeight.first, mcParticlePWithWeight.second);
449  newDaugP->addRelationTo(v0TrackFitResults.first);
450 
451  if (pidM)
452  newDaugM->addRelationTo(pidM);
453  if (mcParticleMWithWeight.first)
454  newDaugM->addRelationTo(mcParticleMWithWeight.first, mcParticleMWithWeight.second);
455  newDaugM->addRelationTo(v0TrackFitResults.second);
456 
457  // sum the 4-momenta of the daughters and construct a particle object
458  TLorentzVector v0Momentum = newDaugP->get4Vector() + newDaugM->get4Vector();
459  Particle v0P(v0Momentum, v0Type.getPDGCode(), v0FlavorType,
460  Particle::EParticleSourceObject::c_V0, v0->getArrayIndex());
461 
462  // add the daughters of the V0 (in the correct order) and don't update
463  // the type to c_Composite (i.e. maintain c_V0)
464  if (correctOrder) {
465  v0P.appendDaughter(newDaugP, false);
466  v0P.appendDaughter(newDaugM, false);
467  } else {
468  v0P.appendDaughter(newDaugM, false);
469  v0P.appendDaughter(newDaugP, false);
470  }
471 
472  // append the particle to the Particle StoreArray and add the new particle to the ParticleList
473  Particle* newPart = m_particles.appendNew(v0P);
474  plist->addParticle(newPart);
475  }
476  }
477  }
478 
479  void ParticleLoaderModule::tracksToParticles()
480  {
481  if (m_Tracks2Plists.empty()) // nothing to do
482  return;
483 
484  // loop over all requested particle lists
485  for (size_t ilist = 0; ilist < m_Tracks2Plists.size(); ilist++) {
486  auto track2Plist = m_Tracks2Plists[ilist];
487  string listName = get<c_PListName>(track2Plist);
488  string antiListName = get<c_AntiPListName>(track2Plist);
489  int pdgCode = get<c_PListPDGCode>(track2Plist);
490  bool isSelfConjugatedParticle = get<c_IsPListSelfConjugated>(track2Plist);
491 
492  StoreObjPtr<ParticleList> plist(listName);
493  // since a particle list in the ParticleLoader always contains all possible objects
494  // we check whether it already exists in this path and can skip any further steps if it does
495  if (plist.isValid())
496  continue;
497  plist.create();
498  plist->initialize(pdgCode, listName);
499 
500  // if cc exists then also create and bind that list
501  if (!isSelfConjugatedParticle) {
502  StoreObjPtr<ParticleList> antiPlist(antiListName);
503  antiPlist.create();
504  antiPlist->initialize(-1 * pdgCode, antiListName);
505 
506  antiPlist->bindAntiParticleList(*(plist));
507  }
508 
509  // the inner loop over all tracks from which Particles
510  // are created, and get sorted in the particle lists
511  for (int i = 0; i < m_tracks.getEntries(); i++) {
512  const Track* track = m_tracks[i];
513  const PIDLikelihood* pid = track->getRelated<PIDLikelihood>();
514  const auto& mcParticleWithWeight = track->getRelatedToWithWeight<MCParticle>();
515 
516  // if a special track hypothesis is requested, use it
517  if (m_trackHypothesis != 0) pdgCode = m_trackHypothesis;
518  Const::ChargedStable type(abs(pdgCode));
519 
520  // load the TrackFitResult for the requested particle or if not available use
521  // the one with the closest mass
522  const TrackFitResult* trackFit = track->getTrackFitResultWithClosestMass(type);
523 
524  if (!trackFit) { // should never happen with the "closest mass" getter - leave as a sanity check
525  B2WARNING("Track returned null TrackFitResult pointer for ChargedStable::getPDGCode() = " << type.getPDGCode());
526  continue;
527  }
528 
529  if (m_enforceFitHypothesis && (trackFit->getParticleType().getPDGCode() != type.getPDGCode())) {
530  // the required hypothesis does not exist for this track, skip it
531  continue;
532  }
533 
534  // charge zero tracks can appear, filter them and
535  // count number of tracks filtered out
536  int charge = trackFit->getChargeSign();
537  if (charge == 0) {
538  B2DEBUG(19, "Track with charge = 0 skipped!");
539  m_chargeZeroTrackCounts[ilist]++;
540  continue;
541  }
542 
543  // create particle and add it to the Particle list.
544  Particle particle(track->getArrayIndex(), trackFit, type);
545 
546  if (particle.getParticleSource() == Particle::c_Track) { // should always hold but...
547 
548  Particle* newPart = m_particles.appendNew(particle);
549  if (pid)
550  newPart->addRelationTo(pid);
551  if (mcParticleWithWeight.first)
552  newPart->addRelationTo(mcParticleWithWeight.first, mcParticleWithWeight.second);
553  newPart->addRelationTo(trackFit);
554 
555  plist->addParticle(newPart);
556 
557  } // sanity check correct particle type
558  } // loop over tracks
559  } // particle lists
560  }
561 
562  void ParticleLoaderModule::eclAndKLMClustersToParticles()
563  {
564  if (m_ECLKLMClusters2Plists.empty()) // nothing to do
565  return;
566 
567  // loop over all ParticleLists
568  for (auto eclKLMCluster2Plist : m_ECLKLMClusters2Plists) {
569  string listName = get<c_PListName>(eclKLMCluster2Plist);
570  string antiListName = get<c_AntiPListName>(eclKLMCluster2Plist);
571  int pdgCode = get<c_PListPDGCode>(eclKLMCluster2Plist);
572  bool isSelfConjugatedParticle = get<c_IsPListSelfConjugated>(eclKLMCluster2Plist);
573 
574  StoreObjPtr<ParticleList> plist(listName);
575  // since a particle list in the ParticleLoader always contains all possible objects
576  // we check whether it already exists in this path and can skip any further steps if it does
577  if (plist.isValid())
578  continue;
579  plist.create();
580  plist->initialize(pdgCode, listName);
581 
582  // create anti-particle list if necessary
583  if (!isSelfConjugatedParticle) {
584  StoreObjPtr<ParticleList> antiPlist(antiListName);
585  antiPlist.create();
586  antiPlist->initialize(-1 * pdgCode, antiListName);
587 
588  antiPlist->bindAntiParticleList(*(plist));
589  }
590 
591  // load reconstructed neutral ECL clusters as photons or Klongs or neutrons
592  for (int i = 0; i < m_eclclusters.getEntries(); i++) {
593  const ECLCluster* cluster = m_eclclusters[i];
594 
595  // ECLClusters can be reconstructed under different hypotheses, for
596  // example photons or neutral hadrons, we only load particles from these
597  // for now
598  if (!cluster->isNeutral()) continue;
599  if (not cluster->hasHypothesis(ECLCluster::EHypothesisBit::c_nPhotons)
600  and not cluster->hasHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron))
601  continue;
602 
603  // ECLCluster can be matched to multiple MCParticles
604  // order the relations by weights and set Particle -> multiple MCParticle relation
605  // preserve the weight
606  RelationVector<MCParticle> mcRelations = cluster->getRelationsTo<MCParticle>();
607  // order relations by weights
608  std::vector<std::pair<int, double>> weightsAndIndices;
609  for (unsigned int iMCParticle = 0; iMCParticle < mcRelations.size(); iMCParticle++) {
610  const MCParticle* relMCParticle = mcRelations[iMCParticle];
611  double weight = mcRelations.weight(iMCParticle);
612  if (relMCParticle)
613  weightsAndIndices.emplace_back(relMCParticle->getArrayIndex(), weight);
614  }
615  // sort descending by weight
616  std::sort(weightsAndIndices.begin(), weightsAndIndices.end(),
617  [](const std::pair<int, double>& left, const std::pair<int, double>& right) {
618  return left.second > right.second;
619  });
620 
621  Const::ParticleType thisType(pdgCode);
622 
623  // don't fill photon list with clusters that don't have
624  // the nPhotons hypothesis (ECL people call this N1)
625  if (pdgCode == Const::photon.getPDGCode()
626  and not cluster->hasHypothesis(ECLCluster::EHypothesisBit::c_nPhotons))
627  continue;
628 
629  // don't fill a KLong list with clusters that don't have the neutral
630  // hadron hypothesis set (ECL people call this N2)
631  if (pdgCode == Const::Klong.getPDGCode()
632  and not cluster->hasHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron))
633  continue;
634 
635  // don't fill a neutron list with clusters that don't have the neutral
636  // hadron hypothesis set (ECL people call this N2)
637  if (abs(pdgCode) == Const::neutron.getPDGCode()
638  and not cluster->hasHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron))
639  continue;
640 
641  // create particle and check it before adding to list
642  Particle particle(cluster, thisType);
643  if (particle.getParticleSource() != Particle::c_ECLCluster) {
644  B2FATAL("Particle created from ECLCluster does not have ECLCluster type.");
645  continue;
646  }
647  Particle* newPart = m_particles.appendNew(particle);
648 
649  // set relations to mcparticles
650  for (auto& weightsAndIndex : weightsAndIndices) {
651  const MCParticle* relMCParticle = m_mcparticles[weightsAndIndex.first];
652  double weight = weightsAndIndex.second;
653 
654  // TODO: study this further and avoid hard-coded values
655  // set the relation only if the MCParticle(reconstructed Particle)'s
656  // energy contribution to this cluster amounts to at least 30(20)%
657  if (relMCParticle)
658  if (weight / newPart->getECLClusterEnergy() > 0.20
659  && weight / relMCParticle->getEnergy() > 0.30)
660  newPart->addRelationTo(relMCParticle, weight);
661  }
662 
663  // add particle to list
664  plist->addParticle(newPart);
665  }
666 
667  // load reconstructed KLM clusters as Klongs or neutrons or photons
668  for (int i = 0; i < m_klmclusters.getEntries(); i++) {
669  const KLMCluster* cluster = m_klmclusters[i];
670 
671  if (std::isnan(cluster->getMomentumMag())) {
672  B2DEBUG(19, "Skipping KLMCluster because its momentum is NaN. "
673  "This can happen if the timing calibration is missing or wrong, so that the velocity is calculated to be negative.");
674  continue;
675  }
676 
677  const MCParticle* mcParticle = cluster->getRelated<MCParticle>();
678 
679  // create particle and check its type before adding it to list
680  Particle particle(cluster, pdgCode);
681  if (particle.getParticleSource() != Particle::c_KLMCluster) {
682  B2FATAL("Particle created from KLMCluster does not have KLMCluster type.");
683  }
684  Particle* newPart = m_particles.appendNew(particle);
685 
686  if (mcParticle)
687  newPart->addRelationTo(mcParticle);
688 
689  // add particle to list
690  plist->addParticle(newPart);
691  }
692  } // loop over particle lists
693  }
694 
695  void ParticleLoaderModule::mcParticlesToParticles()
696  {
697  if (m_MCParticles2Plists.empty()) // nothing to do
698  return;
699 
700  // create all lists
701  for (auto mcParticle2Plist : m_MCParticles2Plists) {
702  string listName = get<c_PListName>(mcParticle2Plist);
703  string antiListName = get<c_AntiPListName>(mcParticle2Plist);
704  int pdgCode = get<c_PListPDGCode>(mcParticle2Plist);
705  bool isSelfConjugatedParticle = get<c_IsPListSelfConjugated>(mcParticle2Plist);
706 
707  StoreObjPtr<ParticleList> plist(listName);
708  // since a particle list in the ParticleLoader always contains all possible objects
709  // we check whether it already exists in this path and can skip any further steps if it does
710  if (plist.isValid())
711  continue;
712  plist.create();
713  plist->initialize(pdgCode, listName);
714 
715  if (!isSelfConjugatedParticle) {
716  StoreObjPtr<ParticleList> antiPlist(antiListName);
717  antiPlist.create();
718  antiPlist->initialize(-1 * pdgCode, antiListName);
719 
720  antiPlist->bindAntiParticleList(*(plist));
721  }
722 
723  for (int i = 0; i < m_mcparticles.getEntries(); i++) {
724  const MCParticle* mcParticle = m_mcparticles[i];
725 
726  if (abs(pdgCode) != abs(mcParticle->getPDG()))
727  continue;
728 
729  Particle particle(mcParticle);
730  Particle* newPart = m_particles.appendNew(particle);
731  newPart->addRelationTo(mcParticle);
732 
733  //append the whole bottom part of the decay tree to this particle
734  if (m_addDaughters) appendDaughtersRecursive(newPart);
735 
736  plist->addParticle(newPart);
737  }
738  }
739  }
740 
741  bool ParticleLoaderModule::isValidPDGCode(const int pdgCode)
742  {
743  bool result = false;
744 
745  // is particle type = charged final state particle?
746  if (Const::chargedStableSet.find(abs(pdgCode)) != Const::invalidParticle)
747  return true;
748 
749  if (abs(pdgCode) == abs(Const::photon.getPDGCode()))
750  return true;
751 
752  if (abs(pdgCode) == abs(Const::Kshort.getPDGCode()))
753  return true;
754 
755  if (abs(pdgCode) == abs(Const::Klong.getPDGCode()))
756  return true;
757 
758  if (abs(pdgCode) == abs(Const::Lambda.getPDGCode()))
759  return true;
760 
761  if (abs(pdgCode) == abs(Const::neutron.getPDGCode()))
762  return true;
763 
764  return result;
765  }
766 
767  void ParticleLoaderModule::appendDaughtersRecursive(Particle* mother)
768  {
769  auto* mcmother = mother->getRelated<MCParticle>();
770 
771  if (!mcmother)
772  return;
773 
774  vector<MCParticle*> mcdaughters = mcmother->getDaughters();
775 
776  for (auto& mcdaughter : mcdaughters) {
777  if (!mcdaughter->hasStatus(MCParticle::c_PrimaryParticle) and m_skipNonPrimaryDaughters) continue;
778  Particle particle(mcdaughter);
779  Particle* daughter = m_particles.appendNew(particle);
780  daughter->addRelationTo(mcdaughter);
781  mother->appendDaughter(daughter, false);
782 
783  if (mcdaughter->getNDaughters() > 0)
784  appendDaughtersRecursive(daughter);
785  }
786  }
787 
788 
790 } // end Belle2 namespace
Provides a type-safe way to pass members of the chargedStableSet set.
Definition: Const.h:470
The ParticleType class for identifying different particle types.
Definition: Const.h:289
int getPDGCode() const
PDG code.
Definition: Const.h:354
EStoreFlags
Flags describing behaviours of objects etc.
Definition: DataStore.h:69
Represents a particle in the DecayDescriptor.
ECL cluster data.
Definition: ECLCluster.h:27
KLM cluster data.
Definition: KLMCluster.h:28
A Class to store the Monte Carlo particle information.
Definition: MCParticle.h:32
float getEnergy() const
Return particle energy in GeV.
Definition: MCParticle.h:147
std::vector< Belle2::MCParticle * > getDaughters() const
Get vector of all daughter particles, empty vector if none.
Definition: MCParticle.cc:50
int getArrayIndex() const
Get 0-based index of the particle in the corresponding MCParticle list.
Definition: MCParticle.h:244
int getPDG() const
Return PDG code of particle.
Definition: MCParticle.h:112
Base class for Modules.
Definition: Module.h:72
Class to hold Lorentz transformations from/to CMS and boost vector.
TLorentzVector getBeamFourMomentum() const
Returns LAB four-momentum of e+e-.
Class to collect log likelihoods from TOP, ARICH, dEdx, ECL and KLM aimed for output to mdst includes...
Definition: PIDLikelihood.h:26
Loads MDST dataobjects as Particle objects to the StoreArray<Particle> and collects them in specified...
Class to store reconstructed particles.
Definition: Particle.h:74
TLorentzVector get4Vector() const
Returns Lorentz vector.
Definition: Particle.h:477
EFlavorType
describes flavor type, see getFlavorType().
Definition: Particle.h:92
double getECLClusterEnergy() const
Returns the energy of the ECLCluster for the particle.
Definition: Particle.cc:888
void appendDaughter(const Particle *daughter, const bool updateType=true)
Appends index of daughter to daughters index array.
Definition: Particle.cc:677
Class for type safe access to objects that are referred to in relations.
size_t size() const
Get number of relations.
float weight(int index) const
Get weight with index.
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
int getArrayIndex() const
Returns this object's array index (in StoreArray), or -1 if not found.
FROM * getRelatedFrom(const std::string &name="", const std::string &namedRelation="") const
Get the object from which this object has a relation.
This is a general purpose class for collecting reconstructed MDST data objects that are not used in r...
Definition: RestOfEvent.h:57
TLorentzVector get4Vector(const std::string &maskName="") const
Get 4-momentum vector all (no mask) or a subset (use mask) of all Tracks and ECLClusters in ROE.
Definition: RestOfEvent.cc:283
Particle * convertToParticle(const std::string &maskName="", int pdgCode=0, bool isSelfConjugated=true)
Converts ROE to Particle and adds it to StoreArray.
Definition: RestOfEvent.cc:420
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
bool create(bool replace=false)
Create a default object in the data store.
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:95
bool isValid() const
Check whether the object was created.
Definition: StoreObjPtr.h:110
Values of the result of a track fit with a given particle hypothesis.
short getChargeSign() const
Return track charge (1 or -1).
Const::ParticleType getParticleType() const
Getter for ParticleType of the mass hypothesis of the track fit.
Class that bundles various TrackFitResults.
Definition: Track.h:25
Object holding information for V0s.
Definition: V0.h:30
std::pair< TrackFitResult *, TrackFitResult * > getTrackFitResults() const
Get pair of the TrackFitResults, that are part of the V0 particle.
Definition: V0.h:53
std::pair< Track *, Track * > getTracks() const
Get pair of yhe Tracks, that are part of the V0 particle.
Definition: V0.h:40
Const::ParticleType getV0Hypothesis() const
Get the hypothesis under which the V0 particle was created.
Definition: V0.cc:28
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.