Belle II Software  light-2205-abys
MCTruthVariables.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/variables/MCTruthVariables.h>
11 
12 // include VariableManager
13 #include <analysis/VariableManager/Manager.h>
14 
15 #include <analysis/dataobjects/Particle.h>
16 #include <analysis/dataobjects/TauPairDecay.h>
17 #include <analysis/utility/MCMatching.h>
18 #include <analysis/utility/ReferenceFrame.h>
19 
20 #include <mdst/dataobjects/MCParticle.h>
21 #include <mdst/dataobjects/ECLCluster.h>
22 
23 #include <framework/datastore/StoreArray.h>
24 #include <framework/datastore/StoreObjPtr.h>
25 #include <framework/dataobjects/EventMetaData.h>
26 #include <framework/gearbox/Const.h>
27 #include <framework/logging/Logger.h>
28 #include <framework/database/DBObjPtr.h>
29 #include <framework/dbobjects/BeamParameters.h>
30 
31 #include <queue>
32 
33 namespace Belle2 {
38  namespace Variable {
39 
40  static const double realNaN = std::numeric_limits<double>::quiet_NaN();
41 
42 
43  double isSignal(const Particle* part)
44  {
45  const MCParticle* mcparticle = part->getMCParticle();
46  if (!mcparticle) return realNaN;
47 
48  int status = MCMatching::getMCErrors(part, mcparticle);
49  return (status == MCMatching::c_Correct);
50  }
51 
52  double isSignalAcceptWrongFSPs(const Particle* part)
53  {
54  const MCParticle* mcparticle = part->getMCParticle();
55  if (!mcparticle) return realNaN;
56 
57  int status = MCMatching::getMCErrors(part, mcparticle);
58  //remove the following bits
59  status &= (~MCMatching::c_MisID);
60  status &= (~MCMatching::c_AddedWrongParticle);
61 
62  return (status == MCMatching::c_Correct);
63  }
64 
65  double isPrimarySignal(const Particle* part)
66  {
67  return (isSignal(part) > 0.5 and particleMCPrimaryParticle(part) > 0.5);
68  }
69 
70  double isMisidentified(const Particle* part)
71  {
72  const MCParticle* mcp = part->getMCParticle();
73  if (!mcp) return realNaN;
74  int st = MCMatching::getMCErrors(part, mcp);
75  return ((st & MCMatching::c_MisID) != 0);
76  }
77 
78  double isWrongCharge(const Particle* part)
79  {
80  const MCParticle* mcp = part->getMCParticle();
81  if (!mcp) return realNaN;
82  return (part->getCharge() != mcp->getCharge());
83  }
84 
85  double isCloneTrack(const Particle* particle)
86  {
87  // neutrals and composites don't make sense
88  if (!Const::chargedStableSet.contains(Const::ParticleType(abs(particle->getPDGCode()))))
89  return realNaN;
90  // get mcparticle weight (mcmatch weight)
91  const auto mcpww = particle->getRelatedToWithWeight<MCParticle>();
92  if (!mcpww.first) return realNaN;
93  return (mcpww.second < 0);
94  }
95 
96  double isOrHasCloneTrack(const Particle* particle)
97  {
98  // use std::queue to check daughters-- granddaughters etc recursively
99  std::queue<const Particle*> qq;
100  qq.push(particle);
101  while (!qq.empty()) {
102  const auto d = qq.front(); // get daughter
103  qq.pop(); // remove the daughter from the queue
104  if (isCloneTrack(d) == 1.0) return 1.0;
105  size_t nDau = d->getNDaughters(); // number of daughters of daughters
106  for (size_t iDau = 0; iDau < nDau; ++iDau)
107  qq.push(d->getDaughter(iDau));
108  }
109  return 0.0;
110  }
111 
112  double genNthMotherPDG(const Particle* part, const std::vector<double>& args)
113  {
114  const MCParticle* mcparticle = part->getMCParticle();
115  if (!mcparticle) return 0.0;
116 
117  unsigned int nLevels = args.empty() ? 0 : args[0];
118 
119  const MCParticle* curMCParticle = mcparticle;
120  for (unsigned int i = 0; i <= nLevels; ++i) {
121  const MCParticle* curMCMother = curMCParticle->getMother();
122  if (!curMCMother) return 0.0;
123  curMCParticle = curMCMother;
124  }
125  return curMCParticle->getPDG();
126  }
127 
128  double genNthMotherIndex(const Particle* part, const std::vector<double>& args)
129  {
130  const MCParticle* mcparticle = part->getMCParticle();
131  if (!mcparticle) return 0.0;
132 
133  unsigned int nLevels = args.empty() ? 0 : args[0];
134 
135  const MCParticle* curMCParticle = mcparticle;
136  for (unsigned int i = 0; i <= nLevels; ++i) {
137  const MCParticle* curMCMother = curMCParticle->getMother();
138  if (!curMCMother) return 0.0;
139  curMCParticle = curMCMother;
140  }
141  return curMCParticle->getArrayIndex();
142  }
143 
144  double genMotherPDG(const Particle* part)
145  {
146  return genNthMotherPDG(part, {});
147  }
148 
149  double genMotherP(const Particle* part)
150  {
151  const MCParticle* mcparticle = part->getMCParticle();
152  if (!mcparticle) return realNaN;
153 
154  const MCParticle* mcmother = mcparticle->getMother();
155  if (!mcmother) return realNaN;
156 
157  return mcmother->getMomentum().Mag();
158  }
159 
160  double genMotherIndex(const Particle* part)
161  {
162  return genNthMotherIndex(part, {});
163  }
164 
165  double genParticleIndex(const Particle* part)
166  {
167  const MCParticle* mcparticle = part->getMCParticle();
168  if (!mcparticle) return realNaN;
169  return mcparticle->getArrayIndex();
170  }
171 
172  double isSignalAcceptMissingNeutrino(const Particle* part)
173  {
174  const MCParticle* mcparticle = part->getMCParticle();
175  if (!mcparticle) return realNaN;
176 
177  int status = MCMatching::getMCErrors(part, mcparticle);
178  //remove the following bits
179  status &= (~MCMatching::c_MissNeutrino);
180 
181  return (status == MCMatching::c_Correct);
182  }
183 
184  double isSignalAcceptMissingMassive(const Particle* part)
185  {
186  const MCParticle* mcparticle = part->getMCParticle();
187  if (!mcparticle) return realNaN;
188 
189  int status = MCMatching::getMCErrors(part, mcparticle);
190  //remove the following bits
191  status &= (~MCMatching::c_MissMassiveParticle);
192  status &= (~MCMatching::c_MissKlong);
193 
194  return (status == MCMatching::c_Correct);
195  }
196 
197  double isSignalAcceptMissingGamma(const Particle* part)
198  {
199  const MCParticle* mcparticle = part->getMCParticle();
200  if (!mcparticle) return realNaN;
201 
202  int status = MCMatching::getMCErrors(part, mcparticle);
203  //remove the following bits
204  status &= (~MCMatching::c_MissGamma);
205 
206  return (status == MCMatching::c_Correct);
207  }
208 
209  double isSignalAcceptMissing(const Particle* part)
210  {
211  const MCParticle* mcparticle = part->getMCParticle();
212  if (!mcparticle) return realNaN;
213 
214  int status = MCMatching::getMCErrors(part, mcparticle);
215  //remove the following bits
216  status &= (~MCMatching::c_MissGamma);
217  status &= (~MCMatching::c_MissMassiveParticle);
218  status &= (~MCMatching::c_MissKlong);
219  status &= (~MCMatching::c_MissNeutrino);
220 
221  return (status == MCMatching::c_Correct);
222  }
223 
224  double isSignalAcceptBremsPhotons(const Particle* part)
225  {
226  const MCParticle* mcparticle = part->getMCParticle();
227  if (!mcparticle) return realNaN;
228 
229  int status = MCMatching::getMCErrors(part, mcparticle);
230  //remove the following bits
231  status &= (~MCMatching::c_AddedRecoBremsPhoton);
232 
233  return (status == MCMatching::c_Correct);
234  }
235 
236  double particleMCMatchPDGCode(const Particle* part)
237  {
238  const MCParticle* mcparticle = part->getMCParticle();
239  if (!mcparticle) return realNaN;
240  return mcparticle->getPDG();
241  }
242 
243  double particleMCErrors(const Particle* part)
244  {
245  return MCMatching::getMCErrors(part);
246  }
247 
248  double particleNumberOfMCMatch(const Particle* particle)
249  {
250  RelationVector<MCParticle> mcRelations = particle->getRelationsTo<MCParticle>();
251  return (mcRelations.size());
252  }
253 
254  double particleMCMatchWeight(const Particle* particle)
255  {
256  auto relWithWeight = particle->getRelatedToWithWeight<MCParticle>();
257  if (!relWithWeight.first) return realNaN;
258  return relWithWeight.second;
259  }
260 
261  double particleMCMatchDecayTime(const Particle* part)
262  {
263  const MCParticle* mcparticle = part->getMCParticle();
264  if (!mcparticle) return realNaN;
265  return mcparticle->getDecayTime();
266  }
267 
268  double particleMCMatchLifeTime(const Particle* part)
269  {
270  const MCParticle* mcparticle = part->getMCParticle();
271  if (!mcparticle) return realNaN;
272  return mcparticle->getLifetime();
273  }
274 
275  double particleMCMatchPX(const Particle* part)
276  {
277  const MCParticle* mcparticle = part->getMCParticle();
278  if (!mcparticle) return realNaN;
279 
280  const auto& frame = ReferenceFrame::GetCurrent();
281  ROOT::Math::PxPyPzEVector mcpP4 = mcparticle->get4Vector();
282  return frame.getMomentum(mcpP4).Px();
283  }
284 
285  double particleMCMatchPY(const Particle* part)
286  {
287  const MCParticle* mcparticle = part->getMCParticle();
288  if (!mcparticle) return realNaN;
289 
290  const auto& frame = ReferenceFrame::GetCurrent();
291  ROOT::Math::PxPyPzEVector mcpP4 = mcparticle->get4Vector();
292  return frame.getMomentum(mcpP4).Py();
293  }
294 
295  double particleMCMatchPZ(const Particle* part)
296  {
297  const MCParticle* mcparticle = part->getMCParticle();
298  if (!mcparticle) return realNaN;
299 
300  const auto& frame = ReferenceFrame::GetCurrent();
301  ROOT::Math::PxPyPzEVector mcpP4 = mcparticle->get4Vector();
302  return frame.getMomentum(mcpP4).Pz();
303  }
304 
305  double particleMCMatchPT(const Particle* part)
306  {
307  const MCParticle* mcparticle = part->getMCParticle();
308  if (!mcparticle) return realNaN;
309 
310  const auto& frame = ReferenceFrame::GetCurrent();
311  ROOT::Math::PxPyPzEVector mcpP4 = mcparticle->get4Vector();
312  return frame.getMomentum(mcpP4).Pt();
313  }
314 
315  double particleMCMatchE(const Particle* part)
316  {
317  const MCParticle* mcparticle = part->getMCParticle();
318  if (!mcparticle) return realNaN;
319 
320  const auto& frame = ReferenceFrame::GetCurrent();
321  ROOT::Math::PxPyPzEVector mcpP4 = mcparticle->get4Vector();
322  return frame.getMomentum(mcpP4).E();
323  }
324 
325  double particleMCMatchP(const Particle* part)
326  {
327  const MCParticle* mcparticle = part->getMCParticle();
328  if (!mcparticle) return realNaN;
329 
330  const auto& frame = ReferenceFrame::GetCurrent();
331  ROOT::Math::PxPyPzEVector mcpP4 = mcparticle->get4Vector();
332  return frame.getMomentum(mcpP4).P();
333  }
334 
335  double particleMCMatchTheta(const Particle* part)
336  {
337  const MCParticle* mcparticle = part->getMCParticle();
338  if (!mcparticle) return realNaN;
339 
340  const auto& frame = ReferenceFrame::GetCurrent();
341  ROOT::Math::PxPyPzEVector mcpP4 = mcparticle->get4Vector();
342  return frame.getMomentum(mcpP4).Theta();
343  }
344 
345  double particleMCMatchPhi(const Particle* part)
346  {
347  const MCParticle* mcparticle = part->getMCParticle();
348  if (!mcparticle) return realNaN;
349 
350  const auto& frame = ReferenceFrame::GetCurrent();
351  ROOT::Math::PxPyPzEVector mcpP4 = mcparticle->get4Vector();
352  return frame.getMomentum(mcpP4).Phi();
353  }
354 
355  double particleMCRecoilMass(const Particle* part)
356  {
357  StoreArray<MCParticle> mcparticles;
358  if (mcparticles.getEntries() < 1) return realNaN;
359 
360  ROOT::Math::PxPyPzEVector pInitial = mcparticles[0]->get4Vector();
361  ROOT::Math::PxPyPzEVector pDaughters;
362  const std::vector<Particle*> daughters = part->getDaughters();
363  for (auto daughter : daughters) {
364  const MCParticle* mcD = daughter->getMCParticle();
365  if (!mcD) return realNaN;
366 
367  pDaughters += mcD->get4Vector();
368  }
369  return (pInitial - pDaughters).M();
370  }
371 
372  ROOT::Math::PxPyPzEVector MCInvisibleP4(const MCParticle* mcparticle)
373  {
374  ROOT::Math::PxPyPzEVector ResultP4;
375  int pdg = abs(mcparticle->getPDG());
376  bool isNeutrino = (pdg == 12 or pdg == 14 or pdg == 16);
377 
378  if (mcparticle->getNDaughters() > 0) {
379  const std::vector<MCParticle*> daughters = mcparticle->getDaughters();
380  for (auto daughter : daughters)
381  ResultP4 += MCInvisibleP4(daughter);
382  } else if (isNeutrino)
383  ResultP4 += mcparticle->get4Vector();
384 
385  return ResultP4;
386  }
387 
388  double particleMCCosThetaBetweenParticleAndNominalB(const Particle* part)
389  {
390  int particlePDG = abs(part->getPDGCode());
391  if (particlePDG != 511 and particlePDG != 521)
392  B2FATAL("The variable mcCosThetaBetweenParticleAndNominalB is only meant to be used on B mesons!");
393 
394  PCmsLabTransform T;
395  double e_Beam = T.getCMSEnergy() / 2.0; // GeV
396  double m_B = part->getPDGMass();
397 
398  // Y(4S) mass according PDG (https://pdg.lbl.gov/2020/listings/rpp2020-list-upsilon-4S.pdf)
399  const double mY4S = 10.5794; // GeV
400 
401  // if this is a continuum run, use an approximate Y(4S) CMS energy
402  if (e_Beam * e_Beam - m_B * m_B < 0) {
403  e_Beam = mY4S / 2.0;
404  }
405  double p_B = std::sqrt(e_Beam * e_Beam - m_B * m_B);
406 
407  // Calculate cosThetaBY with daughter neutrino momenta subtracted
408  const MCParticle* mcB = part->getMCParticle();
409  if (!mcB) return realNaN;
410 
411  int mcParticlePDG = abs(mcB->getPDG());
412  if (mcParticlePDG != 511 and mcParticlePDG != 521)
413  return realNaN;
414 
415  ROOT::Math::PxPyPzEVector p = T.rotateLabToCms() * (mcB->get4Vector() - MCInvisibleP4(mcB));
416  double e_d = p.E();
417  double m_d = p.M();
418  double p_d = p.P();
419 
420  double theta_BY = (2 * e_Beam * e_d - m_B * m_B - m_d * m_d)
421  / (2 * p_B * p_d);
422  return theta_BY;
423  }
424 
425  double mcParticleSecondaryPhysicsProcess(const Particle* p)
426  {
427  const MCParticle* mcp = p->getMCParticle();
428  if (!mcp) return realNaN;
429  return mcp->getSecondaryPhysicsProcess();
430  }
431 
432  double mcParticleStatus(const Particle* p)
433  {
434  const MCParticle* mcp = p->getMCParticle();
435  if (!mcp) return realNaN;
436  return mcp->getStatus();
437  }
438 
439  double particleMCPrimaryParticle(const Particle* p)
440  {
441  const MCParticle* mcp = p->getMCParticle();
442  if (!mcp) return realNaN;
443 
444  unsigned int bitmask = MCParticle::c_PrimaryParticle;
445  return mcp->hasStatus(bitmask);
446  }
447 
448  double particleMCVirtualParticle(const Particle* p)
449  {
450  const MCParticle* mcp = p->getMCParticle();
451  if (!mcp) return realNaN;
452 
453  unsigned int bitmask = MCParticle::c_IsVirtual;
454  return mcp->hasStatus(bitmask);
455  }
456 
457  double particleMCInitialParticle(const Particle* p)
458  {
459  const MCParticle* mcp = p->getMCParticle();
460  if (!mcp) return realNaN;
461 
462  unsigned int bitmask = MCParticle::c_Initial;
463  return mcp->hasStatus(bitmask);
464  }
465 
466  double particleMCISRParticle(const Particle* p)
467  {
468  const MCParticle* mcp = p->getMCParticle();
469  if (!mcp) return realNaN;
470 
471  unsigned int bitmask = MCParticle::c_IsISRPhoton;
472  return mcp->hasStatus(bitmask);
473  }
474 
475  double particleMCFSRParticle(const Particle* p)
476  {
477  const MCParticle* mcp = p->getMCParticle();
478  if (!mcp) return realNaN;
479 
480  unsigned int bitmask = MCParticle::c_IsFSRPhoton;
481  return mcp->hasStatus(bitmask);
482  }
483 
484  double particleMCPhotosParticle(const Particle* p)
485  {
486  const MCParticle* mcp = p->getMCParticle();
487  if (!mcp) return realNaN;
488 
489  unsigned int bitmask = MCParticle::c_IsPHOTOSPhoton;
490  return mcp->hasStatus(bitmask);
491  }
492 
493  double generatorEventWeight(const Particle*)
494  {
495  StoreObjPtr<EventMetaData> evtMetaData;
496  if (!evtMetaData) return realNaN;
497  return evtMetaData->getGeneratedWeight();
498  }
499 
500  int tauPlusMcMode(const Particle*)
501  {
502  StoreObjPtr<TauPairDecay> tauDecay;
503  if (!tauDecay) {
504  B2WARNING("Cannot find tau decay ID, did you forget to run TauDecayMarkerModule?");
505  return 0;
506  }
507  return tauDecay->getTauPlusIdMode();
508  }
509 
510  int tauMinusMcMode(const Particle*)
511  {
512  StoreObjPtr<TauPairDecay> tauDecay;
513  if (!tauDecay) {
514  B2WARNING("Cannot find tau decay ID, did you forget to run TauDecayMarkerModule?");
515  return 0;
516  }
517  return tauDecay->getTauMinusIdMode();
518  }
519 
520  int tauPlusMcProng(const Particle*)
521  {
522  StoreObjPtr<TauPairDecay> tauDecay;
523  if (!tauDecay) {
524  B2WARNING("Cannot find tau prong, did you forget to run TauDecayMarkerModule?");
525  return 0;
526  }
527  return tauDecay->getTauPlusMcProng();
528  }
529 
530  int tauMinusMcProng(const Particle*)
531  {
532  StoreObjPtr<TauPairDecay> tauDecay;
533  if (!tauDecay) {
534  B2WARNING("Cannot find tau prong, did you forget to run TauDecayMarkerModule?");
535  return 0;
536  }
537  return tauDecay->getTauMinusMcProng();
538  }
539 
540  double isReconstructible(const Particle* p)
541  {
542  if (p->getParticleSource() == Particle::EParticleSourceObject::c_Composite)
543  return realNaN;
544  const MCParticle* mcp = p->getMCParticle();
545  if (!mcp) return realNaN;
546 
547  // If charged: make sure it was seen in the SVD.
548  // If neutral: make sure it was seen in the ECL.
549  return (abs(mcp->getCharge()) > 0) ? seenInSVD(p) : seenInECL(p);
550  }
551 
552  double seenInPXD(const Particle* p)
553  {
554  if (p->getParticleSource() == Particle::EParticleSourceObject::c_Composite)
555  return realNaN;
556  const MCParticle* mcp = p->getMCParticle();
557  if (!mcp) return realNaN;
558  return mcp->hasSeenInDetector(Const::PXD);
559  }
560 
561  double seenInSVD(const Particle* p)
562  {
563  if (p->getParticleSource() == Particle::EParticleSourceObject::c_Composite)
564  return realNaN;
565  const MCParticle* mcp = p->getMCParticle();
566  if (!mcp) return realNaN;
567  return mcp->hasSeenInDetector(Const::SVD);
568  }
569 
570  double seenInCDC(const Particle* p)
571  {
572  if (p->getParticleSource() == Particle::EParticleSourceObject::c_Composite)
573  return realNaN;
574  const MCParticle* mcp = p->getMCParticle();
575  if (!mcp) return realNaN;
576  return mcp->hasSeenInDetector(Const::CDC);
577  }
578 
579  double seenInTOP(const Particle* p)
580  {
581  if (p->getParticleSource() == Particle::EParticleSourceObject::c_Composite)
582  return realNaN;
583  const MCParticle* mcp = p->getMCParticle();
584  if (!mcp) return realNaN;
585  return mcp->hasSeenInDetector(Const::TOP);
586  }
587 
588  double seenInECL(const Particle* p)
589  {
590  if (p->getParticleSource() == Particle::EParticleSourceObject::c_Composite)
591  return realNaN;
592  const MCParticle* mcp = p->getMCParticle();
593  if (!mcp) return realNaN;
594  return mcp->hasSeenInDetector(Const::ECL);
595  }
596 
597  double seenInARICH(const Particle* p)
598  {
599  if (p->getParticleSource() == Particle::EParticleSourceObject::c_Composite)
600  return realNaN;
601  const MCParticle* mcp = p->getMCParticle();
602  if (!mcp) return realNaN;
603  return mcp->hasSeenInDetector(Const::ARICH);
604  }
605 
606  double seenInKLM(const Particle* p)
607  {
608  if (p->getParticleSource() == Particle::EParticleSourceObject::c_Composite)
609  return realNaN;
610  const MCParticle* mcp = p->getMCParticle();
611  if (!mcp) return realNaN;
612  return mcp->hasSeenInDetector(Const::KLM);
613  }
614 
615  int genNStepsToDaughter(const Particle* p, const std::vector<double>& arguments)
616  {
617  if (arguments.size() != 1)
618  B2FATAL("Wrong number of arguments for genNStepsToDaughter");
619 
620  const MCParticle* mcp = p->getMCParticle();
621  if (!mcp) {
622  B2WARNING("No MCParticle is associated to the particle");
623  return 0;
624  }
625 
626  int nChildren = p->getNDaughters();
627  if (arguments[0] >= nChildren) {
628  return 0;
629  }
630 
631  const Particle* daugP = p->getDaughter(arguments[0]);
632  const MCParticle* daugMCP = daugP->getMCParticle();
633  if (!daugMCP) {
634  // This is a strange case.
635  // The particle, p, has the related MC particle, but i-th daughter does not have the related MC Particle.
636  B2WARNING("No MCParticle is associated to the i-th daughter");
637  return 0;
638  }
639 
640  if (nChildren == 1) return 1;
641 
642  std::vector<int> genMothers;
643  MCMatching::fillGenMothers(daugMCP, genMothers);
644  auto match = std::find(genMothers.begin(), genMothers.end(), mcp->getIndex());
645  return match - genMothers.begin();
646  }
647 
648  int genNMissingDaughter(const Particle* p, const std::vector<double>& arguments)
649  {
650  if (arguments.size() < 1)
651  B2FATAL("Wrong number of arguments for genNMissingDaughter");
652 
653  const std::vector<int> PDGcodes(arguments.begin(), arguments.end());
654 
655  const MCParticle* mcp = p->getMCParticle();
656  if (!mcp) {
657  B2WARNING("No MCParticle is associated to the particle");
658  return 0;
659  }
660 
661  return MCMatching::countMissingParticle(p, mcp, PDGcodes);
662  }
663 
664  double getHEREnergy(const Particle*)
665  {
666  static DBObjPtr<BeamParameters> beamParamsDB;
667  return (beamParamsDB->getHER()).E();
668  }
669 
670  double getLEREnergy(const Particle*)
671  {
672  static DBObjPtr<BeamParameters> beamParamsDB;
673  return (beamParamsDB->getLER()).E();
674  }
675 
676  double getCrossingAngleX(const Particle*)
677  {
678  // get the beam momenta from the DB
679  static DBObjPtr<BeamParameters> beamParamsDB;
680  B2Vector3D herVec = beamParamsDB->getHER().Vect();
681  B2Vector3D lerVec = beamParamsDB->getLER().Vect();
682 
683  // only looking at the horizontal (XZ plane) -> set y-coordinates to zero
684  herVec.SetY(0);
685  lerVec.SetY(0);
686 
687  //calculate the crossing angle
688  return herVec.Angle(-lerVec);
689  }
690 
691  double getCrossingAngleY(const Particle*)
692  {
693  // get the beam momenta from the DB
694  static DBObjPtr<BeamParameters> beamParamsDB;
695  B2Vector3D herVec = beamParamsDB->getHER().Vect();
696  B2Vector3D lerVec = beamParamsDB->getLER().Vect();
697 
698  // only looking at the vertical (YZ plane) -> set x-coordinates to zero
699  herVec.SetX(0);
700  lerVec.SetX(0);
701 
702  //calculate the crossing angle
703  return herVec.Angle(-lerVec);
704  }
705 
706 
707  double particleClusterMatchWeight(const Particle* particle)
708  {
709  /* Get the weight of the *cluster* mc match for the mcparticle matched to
710  * this particle.
711  *
712  * Note that for track-based particles this is different from the mc match
713  * of the particle (which it inherits from the mc match of the track)
714  */
715  const MCParticle* matchedToParticle = particle->getMCParticle();
716  if (!matchedToParticle) return realNaN;
717  int matchedToIndex = matchedToParticle->getArrayIndex();
718 
719  const ECLCluster* cluster = particle->getECLCluster();
720  if (!cluster) return realNaN;
721 
722  const auto mcps = cluster->getRelationsTo<MCParticle>();
723  for (unsigned int i = 0; i < mcps.size(); ++i)
724  if (mcps[i]->getArrayIndex() == matchedToIndex)
725  return mcps.weight(i);
726 
727  return realNaN;
728  }
729 
730  double particleClusterBestMCMatchWeight(const Particle* particle)
731  {
732  /* Get the weight of the best mc match of the cluster associated to
733  * this particle.
734  *
735  * Note for electrons (or any track-based particle) this may not be
736  * the same thing as the mc match of the particle (which is taken
737  * from the track).
738  *
739  * For photons (or any ECL-based particle) this will be the same as the
740  * mcMatchWeight
741  */
742  const ECLCluster* cluster = particle->getECLCluster();
743  if (!cluster) return realNaN;
744 
745  /* loop over all mcparticles related to this cluster, find the largest
746  * weight by std::sort-ing the doubles
747  */
748  auto mcps = cluster->getRelationsTo<MCParticle>();
749  if (mcps.size() == 0) return realNaN;
750 
751  std::vector<double> weights;
752  for (unsigned int i = 0; i < mcps.size(); ++i)
753  weights.emplace_back(mcps.weight(i));
754 
755  // sort descending by weight
756  std::sort(weights.begin(), weights.end());
757  std::reverse(weights.begin(), weights.end());
758  return weights[0];
759  }
760 
761  double particleClusterBestMCPDGCode(const Particle* particle)
762  {
763  /* Get the PDG code of the best mc match of the cluster associated to this
764  * particle.
765  *
766  * Note for electrons (or any track-based particle) this may not be the
767  * same thing as the mc match of the particle (which is taken from the track).
768  *
769  * For photons (or any ECL-based particle) this will be the same as the mcPDG
770  */
771  const ECLCluster* cluster = particle->getECLCluster();
772  if (!cluster) return realNaN;
773 
774  auto mcps = cluster->getRelationsTo<MCParticle>();
775  if (mcps.size() == 0) return realNaN;
776 
777  std::vector<std::pair<double, int>> weightsAndIndices;
778  for (unsigned int i = 0; i < mcps.size(); ++i)
779  weightsAndIndices.emplace_back(mcps.weight(i), i);
780 
781  // sort descending by weight
782  std::sort(
783  weightsAndIndices.begin(), weightsAndIndices.end(),
784  [](const std::pair<double, int>& l, const std::pair<double, int>& r) {
785  return l.first > r.first;
786  });
787  // cppcheck-suppress containerOutOfBounds
788  return mcps.object(weightsAndIndices[0].second)->getPDG();
789  }
790 
791  double particleClusterTotalMCMatchWeight(const Particle* particle)
792  {
793  const ECLCluster* cluster = particle->getECLCluster();
794  if (!cluster) return realNaN;
795 
796  auto mcps = cluster->getRelationsTo<MCParticle>();
797 
798  // if there are no relations to any MCParticles, we return 0!
799  double weightsum = 0;
800  for (unsigned int i = 0; i < mcps.size(); ++i)
801  weightsum += mcps.weight(i);
802 
803  return weightsum;
804  }
805 
806  double isBBCrossfeed(const Particle* particle)
807  {
808  if (particle == nullptr)
809  return std::numeric_limits<double>::quiet_NaN();
810 
811  int pdg = particle->getPDGCode();
812  if (abs(pdg) != 511 && abs(pdg) != 521 && abs(pdg) != 531)
813  return std::numeric_limits<double>::quiet_NaN();
814 
815  std::vector<const Particle*> daughters = particle->getFinalStateDaughters();
816  int nDaughters = daughters.size();
817  if (nDaughters <= 1)
818  return 0;
819  std::vector<int> mother_ids;
820 
821  for (int j = 0; j < nDaughters; ++j) {
822  const MCParticle* curMCParticle = daughters[j]->getMCParticle();
823  while (curMCParticle != nullptr) {
824  pdg = curMCParticle->getPDG();
825  if (abs(pdg) == 511 || abs(pdg) == 521 || abs(pdg) == 531) {
826  mother_ids.emplace_back(curMCParticle->getArrayIndex());
827  break;
828  }
829  const MCParticle* curMCMother = curMCParticle->getMother();
830  curMCParticle = curMCMother;
831  }
832  if (curMCParticle == nullptr) {
833  return std::numeric_limits<double>::quiet_NaN();
834  }
835  }
836 
837  std::set<int> distinctIDs = std::set(mother_ids.begin(), mother_ids.end());
838  if (distinctIDs.size() == 1)
839  return 0;
840  else
841  return 1;
842  }
843 
844  VARIABLE_GROUP("MC matching and MC truth");
845  REGISTER_VARIABLE("isSignal", isSignal,
846  "1.0 if Particle is correctly reconstructed (SIGNAL), 0.0 if not, and NaN if no related MCParticle could be found. \n"
847  "It behaves according to DecayStringGrammar.");
848  REGISTER_VARIABLE("isSignalAcceptWrongFSPs", isSignalAcceptWrongFSPs,
849  "1.0 if Particle is almost correctly reconstructed (SIGNAL), 0.0 if not, and NaN if no related MCParticle could be found.\n"
850  "Misidentification of charged FSP is allowed.");
851  REGISTER_VARIABLE("isPrimarySignal", isPrimarySignal,
852  "1.0 if Particle is correctly reconstructed (SIGNAL) and primary, 0.0 if not, and NaN if no related MCParticle could be found");
853  REGISTER_VARIABLE("isSignalAcceptBremsPhotons", isSignalAcceptBremsPhotons,
854  "1.0 if Particle is correctly reconstructed (SIGNAL), 0.0 if not, and NaN if no related MCParticle could be found.\n"
855  "Particles with gamma daughters attached through the bremsstrahlung recovery modules are allowed.");
856  REGISTER_VARIABLE("genMotherPDG", genMotherPDG,
857  "Check the PDG code of a particles MC mother particle");
858  REGISTER_VARIABLE("genMotherPDG(i)", genNthMotherPDG,
859  "Check the PDG code of a particles n-th MC mother particle by providing an argument. 0 is first mother, 1 is grandmother etc. :noindex:");
860 
861  REGISTER_VARIABLE("genMotherID", genMotherIndex,
862  "Check the array index of a particles generated mother");
863  REGISTER_VARIABLE("genMotherID(i)", genNthMotherIndex,
864  "Check the array index of a particle n-th MC mother particle by providing an argument. 0 is first mother, 1 is grandmother etc. :noindex:");
865  // genMotherPDG and genMotherID are overloaded (each are two C++ functions
866  // sharing one variable name) so one of the two needs to be made the indexed
867  // variable in sphinx
868  REGISTER_VARIABLE("isBBCrossfeed", isBBCrossfeed,
869  "Returns 1 for crossfeed in reconstruction of given B meson, 0 for no crossfeed and NaN for no true B meson or failed truthmatching.");
870  REGISTER_VARIABLE("genMotherP", genMotherP,
871  "Generated momentum of a particles MC mother particle", "GeV/c");
872  REGISTER_VARIABLE("genParticleID", genParticleIndex,
873  "Check the array index of a particle's related MCParticle");
874  REGISTER_VARIABLE("isSignalAcceptMissingNeutrino",
875  isSignalAcceptMissingNeutrino,
876  "Same as isSignal, but also accept missing neutrino");
877  REGISTER_VARIABLE("isSignalAcceptMissingMassive",
878  isSignalAcceptMissingMassive,
879  "Same as isSignal, but also accept missing massive particle");
880  REGISTER_VARIABLE("isSignalAcceptMissingGamma",
881  isSignalAcceptMissingGamma,
882  "Same as isSignal, but also accept missing gamma, such as B -> K* gamma, pi0 -> gamma gamma");
883  REGISTER_VARIABLE("isSignalAcceptMissing",
884  isSignalAcceptMissing,
885  "Same as isSignal, but also accept missing particle");
886  REGISTER_VARIABLE("isMisidentified", isMisidentified,
887  "Return 1 if the particle is misidentified: at least one of the final state particles has the wrong PDG code assignment (including wrong charge), 0 if PDG code is fine, and NaN if no related MCParticle could be found.");
888  REGISTER_VARIABLE("isWrongCharge", isWrongCharge,
889  "Return 1 if the charge of the particle is wrongly assigned, 0 if it's the correct charge, and NaN if no related MCParticle could be found.");
890  REGISTER_VARIABLE("isCloneTrack", isCloneTrack,
891  "Return 1 if the charged final state particle comes from a cloned track, 0 if not a clone. Returns NAN if neutral, composite, or MCParticle not found (like for data or if not MCMatched)");
892  REGISTER_VARIABLE("isOrHasCloneTrack", isOrHasCloneTrack,
893  "Return 1 if the particle is a clone track or has a clone track as a daughter, 0 otherwise.");
894  REGISTER_VARIABLE("mcPDG", particleMCMatchPDGCode,
895  "The PDG code of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).");
896  REGISTER_VARIABLE("mcErrors", particleMCErrors,
897  "The bit pattern indicating the quality of MC match (see MCMatching::MCErrorFlags)");
898  REGISTER_VARIABLE("mcMatchWeight", particleMCMatchWeight,
899  "The weight of the Particle -> MCParticle relation (only for the first Relation = largest weight).");
900  REGISTER_VARIABLE("nMCMatches", particleNumberOfMCMatch,
901  "The number of relations of this Particle to MCParticle.");
902  REGISTER_VARIABLE("mcDecayTime", particleMCMatchDecayTime,
903  "The decay time of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
904  "ns");
905  REGISTER_VARIABLE("mcLifeTime", particleMCMatchLifeTime,
906  "The life time of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
907  "ns");
908  REGISTER_VARIABLE("mcPX", particleMCMatchPX,
909  "The px of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
910  "GeV/c");
911  REGISTER_VARIABLE("mcPY", particleMCMatchPY,
912  "The py of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
913  "GeV/c");
914  REGISTER_VARIABLE("mcPZ", particleMCMatchPZ,
915  "The pz of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
916  "GeV/c");
917  REGISTER_VARIABLE("mcPT", particleMCMatchPT,
918  "The pt of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
919  "GeV/c");
920  REGISTER_VARIABLE("mcE", particleMCMatchE,
921  "The energy of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
922  "GeV");
923  REGISTER_VARIABLE("mcP", particleMCMatchP,
924  "The total momentum of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
925  "GeV/c");
926  REGISTER_VARIABLE("mcPhi", particleMCMatchPhi,
927  "The phi of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
928  "rad");
929  REGISTER_VARIABLE("mcTheta", particleMCMatchTheta,
930  "The theta of matched MCParticle, NaN if no match. Requires running matchMCTruth() on the reconstructed particles, or a particle list filled with generator particles (MCParticle objects).",
931  "rad");
932  REGISTER_VARIABLE("mcRecoilMass", particleMCRecoilMass,
933  "The mass recoiling against the particles attached as particle's daughters calculated using MC truth values.",
934  "GeV/:math:`\\text{c}^2`");
935  REGISTER_VARIABLE("mcCosThetaBetweenParticleAndNominalB",
936  particleMCCosThetaBetweenParticleAndNominalB,
937  "Cosine of the angle in CMS between momentum the particle and a nominal B particle. In this calculation, the momenta of all descendant neutrinos are subtracted from the B momentum.");
938 
939 
940  REGISTER_VARIABLE("mcSecPhysProc", mcParticleSecondaryPhysicsProcess,
941  "Returns the secondary physics process flag.");
942  REGISTER_VARIABLE("mcParticleStatus", mcParticleStatus,
943  "Returns status bits of related MCParticle or NaN if MCParticle relation is not set.");
944  REGISTER_VARIABLE("mcPrimary", particleMCPrimaryParticle,
945  "Returns 1 if Particle is related to primary MCParticle, 0 if Particle is related to non - primary MCParticle, "
946  "NaN if Particle is not related to MCParticle.");
947  REGISTER_VARIABLE("mcVirtual", particleMCVirtualParticle,
948  "Returns 1 if Particle is related to virtual MCParticle, 0 if Particle is related to non - virtual MCParticle, "
949  "NaN if Particle is not related to MCParticle.")
950  REGISTER_VARIABLE("mcInitial", particleMCInitialParticle,
951  "Returns 1 if Particle is related to initial MCParticle, 0 if Particle is related to non - initial MCParticle, "
952  "NaN if Particle is not related to MCParticle.")
953  REGISTER_VARIABLE("mcISR", particleMCISRParticle,
954  "Returns 1 if Particle is related to ISR MCParticle, 0 if Particle is related to non - ISR MCParticle, "
955  "NaN if Particle is not related to MCParticle.")
956  REGISTER_VARIABLE("mcFSR", particleMCFSRParticle,
957  "Returns 1 if Particle is related to FSR MCParticle, 0 if Particle is related to non - FSR MCParticle ,"
958  "NaN if Particle is not related to MCParticle.")
959  REGISTER_VARIABLE("mcPhotos", particleMCPhotosParticle,
960  "Returns 1 if Particle is related to Photos MCParticle, 0 if Particle is related to non - Photos MCParticle, "
961  "NaN if Particle is not related to MCParticle.")
962  REGISTER_VARIABLE("generatorEventWeight", generatorEventWeight,
963  "[Eventbased] Returns the event weight produced by the event generator")
964 
965  REGISTER_VARIABLE("genNStepsToDaughter(i)", genNStepsToDaughter,
966  "Returns number of steps to i-th daughter from the particle at generator level. "
967  "NaN if no MCParticle is associated to the particle or i-th daughter. "
968  "NaN if i-th daughter does not exist.");
969  REGISTER_VARIABLE("genNMissingDaughter(PDG)", genNMissingDaughter,
970  "Returns the number of missing daughters having assigned PDG codes. "
971  "NaN if no MCParticle is associated to the particle.");
972  REGISTER_VARIABLE("Eher", getHEREnergy, R"DOC(
973 [Eventbased] The nominal HER energy used by the generator.
974 
975 .. warning:: This variable does not make sense for data.
976 )DOC","GeV");
977  REGISTER_VARIABLE("Eler", getLEREnergy, R"DOC(
978 [Eventbased] The nominal LER energy used by the generator.
979 
980 .. warning:: This variable does not make sense for data.
981 )DOC","GeV");
982  REGISTER_VARIABLE("XAngle", getCrossingAngleX, R"DOC(
983 [Eventbased] The nominal beam crossing angle in the x-z plane from generator level beam kinematics.
984 
985 .. warning:: This variable does not make sense for data.
986 )DOC","rad");
987  REGISTER_VARIABLE("YAngle", getCrossingAngleY, R"DOC(
988 [Eventbased] The nominal beam crossing angle in the y-z plane from generator level beam kinematics.
989 
990 .. warning:: This variable does not make sense for data.
991 )DOC","rad");
992 
993  VARIABLE_GROUP("Generated tau decay information");
994  REGISTER_VARIABLE("tauPlusMCMode", tauPlusMcMode,
995  "[Eventbased] Decay ID for the positive tau lepton in a tau pair generated event.");
996  REGISTER_VARIABLE("tauMinusMCMode", tauMinusMcMode,
997  "[Eventbased] Decay ID for the negative tau lepton in a tau pair generated event.");
998  REGISTER_VARIABLE("tauPlusMCProng", tauPlusMcProng,
999  "[Eventbased] Prong for the positive tau lepton in a tau pair generated event.");
1000  REGISTER_VARIABLE("tauMinusMCProng", tauMinusMcProng,
1001  "[Eventbased] Prong for the negative tau lepton in a tau pair generated event.");
1002 
1003  VARIABLE_GROUP("MC particle seen in subdetectors");
1004  REGISTER_VARIABLE("isReconstructible", isReconstructible,
1005  "Checks charged particles were seen in the SVD and neutrals in the ECL, returns 1.0 if so, 0.0 if not, NaN for composite particles or if no related MCParticle could be found. Useful for generator studies, not for reconstructed particles.");
1006  REGISTER_VARIABLE("seenInPXD", seenInPXD,
1007  "Returns 1.0 if the MC particle was seen in the PXD, 0.0 if not, NaN for composite particles or if no related MCParticle could be found. Useful for generator studies, not for reconstructed particles.");
1008  REGISTER_VARIABLE("seenInSVD", seenInSVD,
1009  "Returns 1.0 if the MC particle was seen in the SVD, 0.0 if not, NaN for composite particles or if no related MCParticle could be found. Useful for generator studies, not for reconstructed particles.");
1010  REGISTER_VARIABLE("seenInCDC", seenInCDC,
1011  "Returns 1.0 if the MC particle was seen in the CDC, 0.0 if not, NaN for composite particles or if no related MCParticle could be found. Useful for generator studies, not for reconstructed particles.");
1012  REGISTER_VARIABLE("seenInTOP", seenInTOP,
1013  "Returns 1.0 if the MC particle was seen in the TOP, 0.0 if not, NaN for composite particles or if no related MCParticle could be found. Useful for generator studies, not for reconstructed particles.");
1014  REGISTER_VARIABLE("seenInECL", seenInECL,
1015  "Returns 1.0 if the MC particle was seen in the ECL, 0.0 if not, NaN for composite particles or if no related MCParticle could be found. Useful for generator studies, not for reconstructed particles.");
1016  REGISTER_VARIABLE("seenInARICH", seenInARICH,
1017  "Returns 1.0 if the MC particle was seen in the ARICH, 0.0 if not, NaN for composite particles or if no related MCParticle could be found. Useful for generator studies, not for reconstructed particles.");
1018  REGISTER_VARIABLE("seenInKLM", seenInKLM,
1019  "Returns 1.0 if the MC particle was seen in the KLM, 0.0 if not, NaN for composite particles or if no related MCParticle could be found. Useful for generator studies, not for reconstructed particles.");
1020 
1021  VARIABLE_GROUP("MC Matching for ECLClusters");
1022  REGISTER_VARIABLE("clusterMCMatchWeight", particleClusterMatchWeight,
1023  "Returns the weight of the ECLCluster -> MCParticle relation for the MCParticle matched to the particle. "
1024  "Returns NaN if: no cluster is related to the particle, the particle is not MC matched, or if there are no mcmatches for the cluster. "
1025  "Returns -1 if the cluster *was* matched to particles, but not the match of the particle provided.");
1026  REGISTER_VARIABLE("clusterBestMCMatchWeight", particleClusterBestMCMatchWeight,
1027  "Returns the weight of the ECLCluster -> MCParticle relation for the relation with the largest weight.");
1028  REGISTER_VARIABLE("clusterBestMCPDG", particleClusterBestMCPDGCode,
1029  "Returns the PDG code of the MCParticle for the ECLCluster -> MCParticle relation with the largest weight.");
1030  REGISTER_VARIABLE("clusterTotalMCMatchWeight", particleClusterTotalMCMatchWeight,
1031  "Returns the sum of all weights of the ECLCluster -> MCParticles relations.");
1032 
1033  }
1035 }
void SetX(DataType x)
set X/1st-coordinate
Definition: B2Vector3.h:447
void SetY(DataType y)
set Y/2nd-coordinate
Definition: B2Vector3.h:449
static const ParticleSet chargedStableSet
set of charged stable particles
Definition: Const.h:499
@ c_IsFSRPhoton
bit 7: Particle is from finial state radiation
Definition: MCParticle.h:63
@ c_Initial
bit 5: Particle is initial such as e+ or e- and not going to Geant4
Definition: MCParticle.h:59
@ c_IsPHOTOSPhoton
bit 8: Particle is an radiative photon from PHOTOS
Definition: MCParticle.h:65
@ c_PrimaryParticle
bit 0: Particle is primary particle.
Definition: MCParticle.h:49
@ c_IsVirtual
bit 4: Particle is virtual and not going to Geant4.
Definition: MCParticle.h:57
@ c_IsISRPhoton
bit 6: Particle is from initial state radiation
Definition: MCParticle.h:61
const MCParticle * getMCParticle() const
Returns the pointer to the MCParticle object that was used to create this Particle (ParticleType == c...
Definition: Particle.cc:934
static const ReferenceFrame & GetCurrent()
Get current rest frame.
int getArrayIndex() const
Returns this object's array index (in StoreArray), or -1 if not found.
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:502
Abstract base class for different kinds of events.
Definition: ClusterUtils.h:23
static int countMissingParticle(const Belle2::Particle *particle, const Belle2::MCParticle *mcParticle, const std::vector< int > &daughterPDG)
Count the number of missing daughters of the 'particle'.
Definition: MCMatching.cc:558
@ c_Correct
This Particle and all its daughters are perfectly reconstructed.
Definition: MCMatching.h:34
@ c_MisID
One of the charged final state particles is mis-identified.
Definition: MCMatching.h:42
static int getMCErrors(const Belle2::Particle *particle, const Belle2::MCParticle *mcParticle=nullptr)
Returns quality indicator of the match as a bit pattern where the individual bits indicate the the ty...
Definition: MCMatching.cc:279
static void fillGenMothers(const Belle2::MCParticle *mcP, std::vector< int > &genMCPMothers)
Fills vector with array (1-based) indices of all generator ancestors of given MCParticle.
Definition: MCMatching.cc:61