Belle II Software  light-2205-abys
FlavorTaggingVariables.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/FlavorTaggingVariables.h>
11 
12 #include <analysis/variables/MCTruthVariables.h>
13 #include <analysis/variables/KLMClusterVariables.h>
14 
15 #include <analysis/ClusterUtility/ClusterUtils.h>
16 
17 #include <analysis/utility/MCMatching.h>
18 #include <analysis/utility/PCmsLabTransform.h>
19 
20 // framework - DataStore
21 #include <framework/datastore/StoreObjPtr.h>
22 
23 // dataobjects
24 #include <analysis/dataobjects/Particle.h>
25 #include <analysis/dataobjects/RestOfEvent.h>
26 #include <analysis/dataobjects/ParticleList.h>
27 #include <analysis/dataobjects/FlavorTaggerInfo.h>
28 #include <analysis/ContinuumSuppression/Thrust.h>
29 
30 #include <mdst/dataobjects/MCParticle.h>
31 #include <mdst/dataobjects/Track.h>
32 #include <mdst/dataobjects/ECLCluster.h>
33 #include <mdst/dataobjects/KLMCluster.h>
34 #include <mdst/dataobjects/PIDLikelihood.h>
35 
36 // framework aux
37 #include <framework/gearbox/Const.h>
38 #include <framework/logging/Logger.h>
39 
40 #include <Math/Vector3D.h>
41 #include <Math/Vector4D.h>
42 #include <framework/geometry/B2Vector3.h>
43 
44 #include <algorithm>
45 #include <cmath>
46 
47 using namespace std;
48 
49 namespace Belle2 {
54  namespace Variable {
55 
56  static const double realNaN = std::numeric_limits<double>::quiet_NaN();
57  // ############################################## FlavorTagger Variables ###############################################
58 
59  // Track Level Variables ---------------------------------------------------------------------------------------------------
60 
61  double momentumMissingTagSide(const Particle*)
62  {
63  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
64  if (!roe.isValid()) return 0;
65 
66  ROOT::Math::PxPyPzEVector roeCMSVec;
67 
68  const auto& roeChargedParticles = roe->getChargedParticles();
69  for (auto roeChargedParticle : roeChargedParticles) {
70  roeCMSVec += PCmsLabTransform::labToCms(roeChargedParticle->get4Vector());
71  }
72 
73  double missMom = -roeCMSVec.P();
74  return missMom ;
75  }
76 
77  Manager::FunctionPtr momentumMissingTagSideWithMask(const std::vector<std::string>& arguments)
78  {
79  std::string maskName;
80  if (arguments.size() == 0)
81  maskName = RestOfEvent::c_defaultMaskName;
82  else if (arguments.size() == 1)
83  maskName = arguments[0];
84  else
85  B2FATAL("At most 1 argument (name of mask) accepted.");
86 
87  auto func = [maskName](const Particle*) -> double {
88  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
89  if (!roe.isValid()) return 0;
90 
91  ROOT::Math::PxPyPzEVector roeCMSVec;
92 
93  const auto& roeChargedParticles = roe->getChargedParticles(maskName);
94  for (auto roeChargedParticle : roeChargedParticles)
95  {
96  roeCMSVec += PCmsLabTransform::labToCms(roeChargedParticle->get4Vector());
97  }
98  double missMom = -roeCMSVec.P();
99  return missMom ;
100  };
101  return func;
102  }
103 
104  double cosTPTO(const Particle* part)
105  {
106  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
107  if (!roe.isValid()) return 0;
108 
109  std::vector<ROOT::Math::XYZVector> p3_cms_roe;
110  static const double P_MAX(3.2);
111 
112  // Charged tracks
113  //
114  const auto& roeTracks = roe->getChargedParticles();
115  for (auto& roeChargedParticle : roeTracks) {
116  // TODO: Add helix and KVF with IpProfile once available. Port from L163-199 of:
117  // /belle/b20090127_0910/src/anal/ekpcontsuppress/src/ksfwmoments.cc
118  ROOT::Math::PxPyPzEVector p_cms = PCmsLabTransform::labToCms(roeChargedParticle->get4Vector());
119  if (p_cms != p_cms) continue;
120  if (p_cms.P() > P_MAX) continue;
121  p3_cms_roe.push_back(p_cms.Vect());
122  }
123 
124  // ECLCluster->Gamma
125  const auto& roePhotons = roe->getPhotons();
126  for (auto& roePhoton : roePhotons) {
127  if (roePhoton->getECLClusterEHypothesisBit() == ECLCluster::EHypothesisBit::c_nPhotons) {
128  ROOT::Math::PxPyPzEVector p_lab = roePhoton->get4Vector();
129  if (p_lab != p_lab) continue;
130  if (p_lab.P() < 0.05) continue;
131  ROOT::Math::PxPyPzEVector p_cms = PCmsLabTransform::labToCms(p_lab);
132  if (p_cms != p_cms) continue;
133  if (p_cms.P() > P_MAX) continue;
134  p3_cms_roe.push_back(p_cms.Vect());
135  }
136  }
137 
138  const auto& roeKlongs = roe->getHadrons();
139  for (auto& roeKlong : roeKlongs) {
140  if (nKLMClusterTrackMatches(roeKlong) == 0 && !(roeKlong->getKLMCluster()->getAssociatedEclClusterFlag())) {
141  ROOT::Math::PxPyPzEVector p_lab = roeKlong->get4Vector();
142  if (p_lab != p_lab) continue;
143  if (p_lab.P() < 0.05) continue;
144  ROOT::Math::PxPyPzEVector p_cms = PCmsLabTransform::labToCms(p_lab);
145  if (p_cms != p_cms) continue;
146  if (p_cms.P() > P_MAX) continue;
147  p3_cms_roe.push_back(p_cms.Vect());
148  }
149  }
150 
151  const B2Vector3D thrustO = Thrust::calculateThrust(p3_cms_roe);
152  const B2Vector3D pAxis = PCmsLabTransform::labToCms(part->get4Vector()).Vect();
153 
154  double result = 0 ;
155  if (pAxis == pAxis) result = abs(cos(pAxis.Angle(thrustO)));
156 
157  return result;
158  }
159 
160  Manager::FunctionPtr cosTPTOWithMask(const std::vector<std::string>& arguments)
161  {
162  std::string maskName;
163  if (arguments.size() == 0)
164  maskName = RestOfEvent::c_defaultMaskName;
165  else if (arguments.size() == 1)
166  maskName = arguments[0];
167  else
168  B2FATAL("At most 1 argument (name of mask) accepted.");
169 
170  auto func = [maskName](const Particle * particle) -> double {
171  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
172  if (!roe.isValid()) return 0;
173 
174  std::vector<ROOT::Math::XYZVector> p3_cms_roe;
175  // static const double P_MAX(3.2);
176 
177  // Charged tracks
178  const auto& roeTracks = roe->getChargedParticles(maskName);
179  for (auto& roeChargedParticle : roeTracks)
180  {
181  // TODO: Add helix and KVF with IpProfile once available. Port from L163-199 of:
182  // /belle/b20090127_0910/src/anal/ekpcontsuppress/src/ksfwmoments.cc
183  ROOT::Math::PxPyPzEVector p_cms = PCmsLabTransform::labToCms(roeChargedParticle->get4Vector());
184  if (p_cms != p_cms) continue;
185  // if (p_cms.P() > P_MAX) continue; // Should not be added without any description.
186  p3_cms_roe.push_back(p_cms.Vect());
187  }
188 
189  // ECLCluster->Gamma
190  const auto& roePhotons = roe->getPhotons(maskName);
191  for (auto& roePhoton : roePhotons)
192  {
193  if (roePhoton->getECLClusterEHypothesisBit() == ECLCluster::EHypothesisBit::c_nPhotons) {
194  ROOT::Math::PxPyPzEVector p_lab = roePhoton->get4Vector();
195  if (p_lab != p_lab) continue;
196  // if (p_lab.P() < 0.05) continue; // Should not be added without any description.
197  ROOT::Math::PxPyPzEVector p_cms = PCmsLabTransform::labToCms(p_lab);
198  if (p_cms != p_cms) continue;
199  // if (p_cms.P() > P_MAX) continue; // Should not be added without any description.
200  p3_cms_roe.push_back(p_cms.Vect());
201  }
202  }
203 
204  // KLMCluster
205  const auto& roeKlongs = roe->getHadrons(maskName);
206  for (auto& roeKlong : roeKlongs)
207  {
208  if (nKLMClusterTrackMatches(roeKlong) == 0 && !(roeKlong->getKLMCluster()->getAssociatedEclClusterFlag())) {
209  ROOT::Math::PxPyPzEVector p_lab = roeKlong->get4Vector();
210  if (p_lab != p_lab) continue;
211  // if (p_lab.P() < 0.05) continue; // Should not be added without any description.
212  ROOT::Math::PxPyPzEVector p_cms = PCmsLabTransform::labToCms(p_lab);
213  if (p_cms != p_cms) continue;
214  // if (p_cms.P() > P_MAX) continue; // Should not be added without any description.
215  p3_cms_roe.push_back(p_cms.Vect());
216  }
217  }
218 
219  const B2Vector3D thrustO = Thrust::calculateThrust(p3_cms_roe);
220  const B2Vector3D pAxis = PCmsLabTransform::labToCms(particle->get4Vector()).Vect();
221 
222  double result = 0 ;
223  if (pAxis == pAxis)
224  result = abs(cos(pAxis.Angle(thrustO))); // abs??
225 
226  return result;
227 
228  };
229  return func;
230  }
231 
232  int lambdaFlavor(const Particle* particle)
233  {
234  if (particle->getPDGCode() == Const::Lambda.getPDGCode()) return 1; //Lambda0
235  else if (particle->getPDGCode() == Const::antiLambda.getPDGCode()) return -1; //Anti-Lambda0
236  else return 0;
237  }
238 
239  bool isLambda(const Particle* particle)
240  {
241  const MCParticle* mcparticle = particle->getMCParticle();
242  if (!mcparticle) return false;
243  return (abs(mcparticle->getPDG()) == Const::Lambda.getPDGCode());
244  }
245 
246  double lambdaZError(const Particle* particle)
247  {
248  //This is a simplistic hack. But I see no other way to get that information.
249  //Should be removed if worthless
250  TMatrixFSym ErrorPositionMatrix = particle->getVertexErrorMatrix();
251  return ErrorPositionMatrix[2][2];
252  }
253 
254  double momentumOfSecondDaughter(const Particle* part)
255  {
256  if (!part->getDaughter(1)) return 0.0;
257  return part->getDaughter(1)->getP();
258  }
259 
260  double momentumOfSecondDaughterCMS(const Particle* part)
261  {
262  if (!part->getDaughter(1)) return 0.0;
263  ROOT::Math::PxPyPzEVector vec = PCmsLabTransform::labToCms(part->getDaughter(1)->get4Vector());
264  return vec.P();
265  }
266 
267  double chargeTimesKaonLiklihood(const Particle*)
268  {
269  StoreObjPtr<ParticleList> KaonList("K+:inRoe");
270  if (!KaonList.isValid()) return 0;
271 
272  double maximumKaonid = 0;
273  double maximum_charge = 0;
274  for (unsigned int i = 0; i < KaonList->getListSize(); ++i) {
275  const Particle* p = KaonList->getParticle(i);
276  double Kid = p->getRelatedTo<PIDLikelihood>()->getProbability(Const::kaon, Const::pion);
277  if (Kid > maximumKaonid) {
278  maximumKaonid = Kid;
279  maximum_charge = p->getCharge();
280  }
281  }
282  return maximumKaonid * maximum_charge;
283  }
284 
285  double transverseMomentumOfChargeTracksInRoe(const Particle* part)
286  {
287  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
288  if (!roe.isValid()) return 0;
289 
290  double sum = 0.0;
291 
292  for (const auto& track : roe->getChargedParticles()) {
293  if (part->isCopyOf(track, true)) continue;
294  sum += track->getMomentum().Perp2();
295  }
296 
297  return sum;
298 
299  }
300 
301  Manager::FunctionPtr transverseMomentumOfChargeTracksInRoeWithMask(const std::vector<std::string>& arguments)
302  {
303  std::string maskName;
304  if (arguments.size() == 0)
305  maskName = RestOfEvent::c_defaultMaskName;
306  else if (arguments.size() == 1)
307  maskName = arguments[0];
308  else
309  B2FATAL("At most 1 argument (name of mask) accepted.");
310 
311  auto func = [maskName](const Particle * particle) -> double {
312  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
313  if (!roe.isValid()) return 0;
314 
315  double sum = 0.0;
316 
317  for (const auto& track : roe->getChargedParticles(maskName))
318  {
319  if (particle->isCopyOf(track, true)) continue;
320  sum += track->getMomentum().Rho();
321  }
322 
323  return sum;
324  };
325  return func;
326  }
327 
328  Manager::FunctionPtr transverseMomentumSquaredOfChargeTracksInRoeWithMask(const std::vector<std::string>& arguments)
329  {
330  std::string maskName;
331  if (arguments.size() == 0)
332  maskName = RestOfEvent::c_defaultMaskName;
333  else if (arguments.size() == 1)
334  maskName = arguments[0];
335  else
336  B2FATAL("At most 1 argument (name of mask) accepted.");
337 
338  auto func = [maskName](const Particle * particle) -> double {
339  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
340  if (!roe.isValid()) return 0;
341 
342  double sum = 0.0;
343 
344  for (const auto& track : roe->getChargedParticles(maskName))
345  {
346  if (particle->isCopyOf(track, true)) continue;
347  sum += track->getMomentum().Perp2();
348  }
349 
350  return sum;
351  };
352  return func;
353  }
354 
355  int NumberOfKShortsInRoe(const Particle* particle)
356  {
357  StoreObjPtr<ParticleList> KShortList("K_S0:inRoe");
358  if (!KShortList.isValid())
359  B2FATAL("NumberOfKShortsInRoe cannot be calculated because the required particleList K_S0:inRoe could not be found or is not valid");
360 
361  int flag = 0;
362  for (unsigned int i = 0; i < KShortList->getListSize(); ++i) {
363  if (!particle->overlapsWith(KShortList->getParticle(i)))
364  ++flag;
365  }
366  return flag;
367  }
368 
369 // Event Level Variables --------------------------------------------------------------------------------------------
370 
371  bool isInElectronOrMuonCat(const Particle* particle)
372  {
373  // check muons
374  StoreObjPtr<ParticleList> MuonList("mu+:inRoe");
375  const Track* trackTargetMuon = nullptr;
376  if (MuonList.isValid()) {
377  double maximumProbMuon = 0;
378  for (unsigned int i = 0; i < MuonList->getListSize(); ++i) {
379  Particle* pMuon = MuonList->getParticle(i);
380  double probMuon = pMuon->getExtraInfo("isRightTrack(Muon)");
381  if (probMuon > maximumProbMuon) {
382  maximumProbMuon = probMuon;
383  trackTargetMuon = pMuon->getTrack();
384  }
385  }
386  }
387  if (particle->getTrack() == trackTargetMuon)
388  return true;
389 
390 
391  // check electrons
392  StoreObjPtr<ParticleList> ElectronList("e+:inRoe");
393  const Track* trackTargetElectron = nullptr;
394  if (ElectronList.isValid()) {
395  double maximumProbElectron = 0;
396  for (unsigned int i = 0; i < ElectronList->getListSize(); ++i) {
397  Particle* pElectron = ElectronList->getParticle(i);
398  double probElectron = pElectron->getExtraInfo("isRightTrack(Electron)");
399  if (probElectron > maximumProbElectron) {
400  maximumProbElectron = probElectron;
401  trackTargetElectron = pElectron->getTrack();
402  }
403  }
404  }
405  if (particle->getTrack() == trackTargetElectron)
406  return true;
407 
408  return false;
409  }
410 
411  // helper function to get flavour of MC B0
412  static int getB0flavourMC(const MCParticle* mcParticle)
413  {
414  while (mcParticle) {
415  if (mcParticle->getPDG() == 511) {
416  return 1;
417  } else if (mcParticle->getPDG() == -511) {
418  return -1;
419  }
420  mcParticle = mcParticle->getMother();
421  }
422  return 0; //no B found
423  }
424 
425 // Target Variables --------------------------------------------------------------------------------------------------
426 
427  bool isMajorityInRestOfEventFromB0(const Particle*)
428  {
429  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
430  if (!roe.isValid()) return 0;
431 
432  int vote = 0;
433  for (auto& track : roe->getChargedParticles()) {
434  const MCParticle* mcParticle = track->getMCParticle();
435  vote += getB0flavourMC(mcParticle);
436  }
437 
438  return vote > 0;
439  }
440 
441  Manager::FunctionPtr isMajorityInRestOfEventFromB0WithMask(const std::vector<std::string>& arguments)
442  {
443  std::string maskName;
444  if (arguments.size() == 0)
445  maskName = RestOfEvent::c_defaultMaskName;
446  else if (arguments.size() == 1)
447  maskName = arguments[0];
448  else
449  B2FATAL("At most 1 argument (name of mask) accepted.");
450 
451  auto func = [maskName](const Particle*) -> bool {
452  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
453  if (!roe.isValid()) return 0;
454 
455  int vote = 0;
456  for (auto& track : roe->getChargedParticles(maskName))
457  {
458  const MCParticle* mcParticle = track->getMCParticle();
459  vote += getB0flavourMC(mcParticle);
460  }
461 
462  return vote > 0;
463 
464  };
465  return func;
466  }
467 
468  bool isMajorityInRestOfEventFromB0bar(const Particle*)
469  {
470  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
471  if (!roe.isValid()) return 0;
472 
473  int vote = 0;
474  for (auto& track : roe->getChargedParticles()) {
475  const MCParticle* mcParticle = track->getMCParticle();
476  vote += getB0flavourMC(mcParticle);
477  }
478 
479  return vote < 0;
480  }
481 
482  Manager::FunctionPtr isMajorityInRestOfEventFromB0barWithMask(const std::vector<std::string>& arguments)
483  {
484  std::string maskName;
485  if (arguments.size() == 0)
486  maskName = RestOfEvent::c_defaultMaskName;
487  else if (arguments.size() == 1)
488  maskName = arguments[0];
489  else
490  B2FATAL("At most 1 argument (name of mask) accepted.");
491 
492  auto func = [maskName](const Particle*) -> bool {
493  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
494  if (!roe.isValid()) return 0;
495 
496  int vote = 0;
497  for (auto& track : roe->getChargedParticles(maskName))
498  {
499  const MCParticle* mcParticle = track->getMCParticle();
500  vote += getB0flavourMC(mcParticle);
501  }
502 
503  return vote < 0;
504 
505  };
506  return func;
507  }
508 
509  bool hasRestOfEventTracks(const Particle* part)
510  {
511  const RestOfEvent* roe = part->getRelatedTo<RestOfEvent>();
512  return (roe && roe-> getNTracks() > 0);
513  }
514 
515  Manager::FunctionPtr hasRestOfEventTracksWithMask(const std::vector<std::string>& arguments)
516  {
517  std::string maskName;
518  if (arguments.size() == 0)
519  maskName = RestOfEvent::c_defaultMaskName;
520  else if (arguments.size() == 1)
521  maskName = arguments[0];
522  else
523  B2FATAL("At most 1 argument (name of mask) accepted.");
524 
525  auto func = [maskName](const Particle*) -> bool {
526  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
527  if (!roe.isValid()) return 0;
528 
529  return roe->getNTracks(maskName) > 0;
530  };
531  return func;
532  }
533 
534  int isRelatedRestOfEventB0Flavor(const Particle* particle)
535  {
536  const RestOfEvent* roe = particle->getRelatedTo<RestOfEvent>();
537  if (!roe) return 0;
538 
539  const MCParticle* BcpMC = particle->getMCParticle();
540  if (!BcpMC) return 0;
541  if (Variable::isSignal(particle) <= 0) return 0;
542 
543  const MCParticle* Y4S = BcpMC->getMother();
544  if (!Y4S) return 0;
545 
546  int BtagFlavor = 0;
547  int BcpFlavor = 0;
548 
549  for (auto& roeChargedParticle : roe->getChargedParticles()) {
550  const MCParticle* mcParticle = roeChargedParticle->getMCParticle();
551  while (mcParticle) {
552  if (mcParticle->getMother() == Y4S) {
553  if (mcParticle == BcpMC) {
554  if (mcParticle->getPDG() > 0) BcpFlavor = 2;
555  else BcpFlavor = -2;
556  } else if (BtagFlavor == 0) {
557  if (abs(mcParticle->getPDG()) == 511 || abs(mcParticle->getPDG()) == 521) {
558  if (mcParticle->getPDG() > 0) BtagFlavor = 1;
559  else BtagFlavor = -1;
560  } else BtagFlavor = 5;
561  }
562  break;
563  }
564  mcParticle = mcParticle->getMother();
565  }
566  if (BcpFlavor != 0 || BtagFlavor == 5) break;
567  }
568 
569 
570  return (BcpFlavor != 0) ? BcpFlavor : BtagFlavor;
571  }
572 
573  Manager::FunctionPtr isRelatedRestOfEventB0FlavorWithMask(const std::vector<std::string>& arguments)
574  {
575  std::string maskName;
576  if (arguments.size() == 0)
577  maskName = RestOfEvent::c_defaultMaskName;
578  else if (arguments.size() == 1)
579  maskName = arguments[0];
580  else
581  B2FATAL("At most 1 argument (name of mask) accepted.");
582 
583  auto func = [maskName](const Particle * particle) -> int {
584  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
585  if (!roe.isValid()) return 0;
586 
587  const MCParticle* BcpMC = particle->getMCParticle();
588  if (!BcpMC) return 0;
589  if (Variable::isSignal(particle) <= 0) return 0;
590 
591  const MCParticle* Y4S = BcpMC->getMother();
592  if (!Y4S) return 0;
593 
594  int BtagFlavor = 0;
595  int BcpFlavor = 0;
596 
597  for (auto& roeChargedParticle : roe->getChargedParticles(maskName))
598  {
599  const MCParticle* mcParticle = roeChargedParticle->getMCParticle();
600  while (mcParticle) {
601  if (mcParticle->getMother() != Y4S) {
602  mcParticle = mcParticle->getMother();
603  continue;
604  }
605 
606  if (mcParticle == BcpMC) { // if mcParticle is associated with CP-side unfortunately
607  if (mcParticle->getPDG() > 0)
608  BcpFlavor = 2;
609  else
610  BcpFlavor = -2;
611  } else if (BtagFlavor == 0) { // only first mcParticle is checked.
612  if (abs(mcParticle->getPDG()) == 511 || abs(mcParticle->getPDG()) == 521) {
613  if (mcParticle->getPDG() > 0)
614  BtagFlavor = 1;
615  else
616  BtagFlavor = -1;
617  } else {
618  BtagFlavor = 5;
619  }
620  }
621  break;
622  }
623  if (BcpFlavor != 0 || BtagFlavor == 5) break;
624  }
625 
626  return (BcpFlavor != 0) ? BcpFlavor : BtagFlavor;
627  };
628  return func;
629 
630  }
631 
632  int isRestOfEventB0Flavor(const Particle*)
633  {
634  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
635  if (!roe.isValid()) return 0;
636 
637  const Particle* Bcp = roe->getRelated<Particle>();
638  return Variable::isRelatedRestOfEventB0Flavor(Bcp);
639  }
640 
641  int ancestorHasWhichFlavor(const Particle* particle)
642  {
643  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
644  if (!roe.isValid()) return 0;
645 
646  const MCParticle* BcpMC = roe->getRelated<Particle>()->getMCParticle();
647  const MCParticle* Y4S = BcpMC->getMother();
648  const MCParticle* mcParticle = particle->getMCParticle();
649 
650  int outputB0tagQ = 0;
651  while (mcParticle) {
652  if (mcParticle->getMother() == Y4S) {
653  if (mcParticle != BcpMC && abs(mcParticle->getPDG()) == 511) {
654  if (mcParticle->getPDG() == 511) outputB0tagQ = 1;
655  else outputB0tagQ = -1;
656  } else if (mcParticle == BcpMC) {
657  if (mcParticle->getPDG() == 511) outputB0tagQ = 2;
658  else outputB0tagQ = -2;
659  } else outputB0tagQ = 5;
660  break;
661  }
662  mcParticle = mcParticle->getMother();
663  }
664 
665  return outputB0tagQ;
666  }
667 
668  int B0mcErrors(const Particle*)
669  {
670  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
671  if (!roe.isValid()) return -1;
672 
673  const Particle* Bcp = roe->getRelated<Particle>();
674  const MCParticle* BcpMC = roe->getRelated<Particle>()->getMCParticle();
675  return MCMatching::getMCErrors(Bcp, BcpMC);
676  }
677 
678  int isRelatedRestOfEventMajorityB0Flavor(const Particle* part)
679  {
680  const RestOfEvent* roe = part->getRelatedTo<RestOfEvent>();
681  if (!roe) return -2;
682 
683  int q_MC = 0; //Flavor of B
684 
685  if (roe->getNTracks() > 0) {
686  for (auto& track : roe->getChargedParticles()) {
687  const MCParticle* mcParticle = track->getMCParticle();
688  q_MC += getB0flavourMC(mcParticle);
689  }
690  } else if (roe->getNECLClusters() > 0) {
691  for (auto& cluster : roe->getPhotons()) {
692  if (cluster->getECLClusterEHypothesisBit() != ECLCluster::EHypothesisBit::c_nPhotons) continue;
693  const MCParticle* mcParticle = cluster->getMCParticle();
694  q_MC += getB0flavourMC(mcParticle);
695  }
696  } else if (roe->getNKLMClusters() > 0) {
697  for (auto& klmcluster : roe->getHadrons()) {
698  const MCParticle* mcParticle = klmcluster->getMCParticle();
699  q_MC += getB0flavourMC(mcParticle);
700  }
701  }
702 
703  if (q_MC == 0)
704  return -2;
705  else
706  return (q_MC > 0);
707  }
708 
709  Manager::FunctionPtr isRelatedRestOfEventMajorityB0FlavorWithMask(const std::vector<std::string>& arguments)
710  {
711  std::string maskName;
712  if (arguments.size() == 0)
713  maskName = RestOfEvent::c_defaultMaskName;
714  else if (arguments.size() == 1)
715  maskName = arguments[0];
716  else
717  B2FATAL("At most 1 argument (name of mask) accepted.");
718 
719  auto func = [maskName](const Particle*) -> int {
720  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
721  if (!roe.isValid()) return -2;
722 
723  int q_MC = 0; //Flavor of B
724 
725  if (roe->getNTracks(maskName) > 0)
726  {
727  for (auto& track : roe->getChargedParticles(maskName)) {
728  const MCParticle* mcParticle = track->getMCParticle();
729  q_MC += getB0flavourMC(mcParticle);
730  }
731  } else if (roe->getNECLClusters(maskName) > 0) // only if there are no tracks
732  {
733  for (auto& cluster : roe->getPhotons(maskName)) {
734  if (cluster->getECLClusterEHypothesisBit() != ECLCluster::EHypothesisBit::c_nPhotons) continue;
735  const MCParticle* mcParticle = cluster->getMCParticle();
736  q_MC += getB0flavourMC(mcParticle);
737  }
738  } else if (roe->getNKLMClusters(maskName) > 0) // only if there are no tracks nor ecl-clusters
739  {
740  for (auto& klmcluster : roe->getHadrons(maskName)) {
741  const MCParticle* mcParticle = klmcluster->getMCParticle();
742  q_MC += getB0flavourMC(mcParticle);
743  }
744  }
745 
746  if (q_MC == 0)
747  return -2;
748  else
749  return int(q_MC > 0);
750 
751  };
752  return func;
753  }
754 
755  int isRestOfEventMajorityB0Flavor(const Particle*)
756  {
757  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
758  if (!roe.isValid()) return -2;//gRandom->Uniform(0, 1);
759 
760  const Particle* Bcp = roe->getRelated<Particle>();
761  return Variable::isRelatedRestOfEventMajorityB0Flavor(Bcp);
762  }
763 
764  double mcFlavorOfOtherB(const Particle* particle)
765  {
766 
767  if (std::abs(particle->getPDGCode()) != 511 && std::abs(particle->getPDGCode()) != 521) {
768  B2ERROR("MCFlavorOfOtherB: this variable works only for B mesons.\n"
769  "The given particle with PDG code " << particle->getPDGCode() <<
770  " is not a B-meson candidate (PDG code 511 or 521). ");
771  return realNaN;
772  }
773 
774  const MCParticle* mcParticle = particle->getMCParticle();
775  if (!mcParticle) return realNaN;
776 
777  const MCParticle* mcMother = mcParticle->getMother();
778  if (!mcMother) return realNaN;
779 
780  if (Variable::isSignal(particle) < 1.0) return 0;
781 
782  for (auto& upsilon4SDaughter : mcMother->getDaughters()) {
783  if (upsilon4SDaughter == mcParticle) continue;
784  return (upsilon4SDaughter->getPDG() > 0) ? 1 : -1;
785  }
786 
787  return 0;
788 
789  };
790 
791 // ######################################### Meta Variables ##############################################
792 
793 // Track and Event Level variables ------------------------------------------------------------------------
794 
795  Manager::FunctionPtr BtagToWBosonVariables(const std::vector<std::string>& arguments)
796  {
797 
798  std::string requestedVariable;
799  std::string maskName;
800  if (arguments.size() == 1) {
801  requestedVariable = arguments[0];
802  maskName = RestOfEvent::c_defaultMaskName;
803  } else if (arguments.size() == 2) {
804  requestedVariable = arguments[0];
805  maskName = arguments[1];
806  } else {
807  B2FATAL("Number of arguments must be 1 (requestedVariable) or 2 (requestedVariable, maskName).");
808  }
809 
810  const std::vector<string> availableVariables = {"recoilMass",
811  "recoilMassSqrd",
812  "pMissCMS",
813  "cosThetaMissCMS",
814  "EW90"
815  };
816 
817  if (std::find(availableVariables.begin(), availableVariables.end(), requestedVariable) == availableVariables.end()) {
818  B2FATAL("Wrong variable " << requestedVariable <<
819  " requested. The possibilities are recoilMass, recoilMassSqrd, pMissCMS, cosThetaMissCMS or EW90");
820  }
821 
822  auto func = [requestedVariable, maskName](const Particle * particle) -> double {
823  StoreObjPtr<RestOfEvent> roe("RestOfEvent");
824  if (!roe.isValid())
825  return 0;
826 
827  ROOT::Math::PxPyPzEVector momXChargedTracks; //Momentum of charged X tracks in lab-System
828  const auto& roeChargedParticles = roe->getChargedParticles(maskName);
829  for (auto& roeChargedParticle : roeChargedParticles)
830  {
831  if (roeChargedParticle->isCopyOf(particle, true)) continue;
832  momXChargedTracks += roeChargedParticle->get4Vector();
833  }
834 
835  ROOT::Math::PxPyPzEVector momXNeutralClusters = roe->get4VectorNeutralECLClusters(maskName); //Momentum of neutral X clusters in lab-System
836  const auto& klongs = roe->getHadrons(maskName);
837  for (auto& klong : klongs)
838  {
839  if (nKLMClusterTrackMatches(klong) == 0 && !(klong->getKLMCluster()->getAssociatedEclClusterFlag())) {
840  momXNeutralClusters += klong->get4Vector();
841  }
842  }
843 
844  ROOT::Math::PxPyPzEVector momX = PCmsLabTransform::labToCms(momXChargedTracks + momXNeutralClusters); //Total Momentum of the recoiling X in CMS-System
845  ROOT::Math::PxPyPzEVector momTarget = PCmsLabTransform::labToCms(particle->get4Vector()); //Momentum of Mu in CMS-System
846  ROOT::Math::PxPyPzEVector momMiss = -(momX + momTarget); //Momentum of Anti-v in CMS-System
847 
848  double output = 0.0;
849  if (requestedVariable == "recoilMass") output = momX.M();
850  if (requestedVariable == "recoilMassSqrd") output = momX.M2();
851  if (requestedVariable == "pMissCMS") output = momMiss.P();
852  if (requestedVariable == "cosThetaMissCMS") output = momTarget.Vect().Unit().Dot(momMiss.Vect().Unit());
853  if (requestedVariable == "EW90")
854  {
855 
856  ROOT::Math::PxPyPzEVector momW = momTarget + momMiss; //Momentum of the W-Boson in CMS
857  float E_W_90 = 0 ; // Energy of all charged and neutral clusters in the hemisphere of the W-Boson
858 
859  const auto& photons = roe->getPhotons(maskName);
860  for (auto& photon : photons) {
861  if (PCmsLabTransform::labToCms(photon->get4Vector()).Vect().Dot(momW.Vect()) > 0) {
862  E_W_90 += photon->getECLClusterEnergy();
863  }
864  }
865  for (auto& roeChargedParticle : roeChargedParticles) {
866  if (roeChargedParticle->isCopyOf(particle, true))
867  continue;
868 
869  for (const ECLCluster& chargedCluster : roeChargedParticle->getTrack()->getRelationsWith<ECLCluster>()) {
870  // ignore everything except the nPhotons hypothesis
871  if (!chargedCluster.hasHypothesis(ECLCluster::EHypothesisBit::c_nPhotons))
872  continue;
873  float iEnergy = chargedCluster.getEnergy(ECLCluster::EHypothesisBit::c_nPhotons);
874  if (iEnergy == iEnergy) {
875  if (PCmsLabTransform::labToCms(ClusterUtils().Get4MomentumFromCluster(&chargedCluster,
876  ECLCluster::EHypothesisBit::c_nPhotons)).Vect().Dot(momW.Vect()) > 0)
877  E_W_90 += iEnergy;
878  }
879  }
880  }
881 
882  output = E_W_90;
883  }
884 
885  return output;
886  };
887  return func;
888  }
889 
890  Manager::FunctionPtr KaonPionVariables(const std::vector<std::string>& arguments)
891  {
892  if (arguments.size() != 1)
893  B2FATAL("Wrong number of arguments (1 required) for meta function KaonPionVariables");
894 
895 
896  auto requestedVariable = arguments[0];
897  auto func = [requestedVariable](const Particle * particle) -> double {
898  // StoreObjPtr<ParticleList> KaonList("K+:ROE");
899  StoreObjPtr<ParticleList> SlowPionList("pi+:inRoe");
900 
901 
902  if ((requestedVariable != "HaveOpositeCharges") && (requestedVariable != "cosKaonPion"))
903  B2FATAL("Wrong variable " << requestedVariable << " requested. The possibilities are cosKaonPion or HaveOpositeCharges");
904 
905 
906  ROOT::Math::PxPyPzEVector momTargetSlowPion;
907  double chargeTargetSlowPion = 0;
908  if (SlowPionList.isValid())
909  {
910  double maximumProbSlowPion = 0;
911  for (unsigned int i = 0; i < SlowPionList->getListSize(); ++i) {
912  Particle* pSlowPion = SlowPionList->getParticle(i);
913  if (!pSlowPion) continue;
914  if (!pSlowPion->hasExtraInfo("isRightCategory(SlowPion)")) continue;
915 
916  double probSlowPion = pSlowPion->getExtraInfo("isRightCategory(SlowPion)");
917  if (probSlowPion > maximumProbSlowPion) {
918  maximumProbSlowPion = probSlowPion;
919  chargeTargetSlowPion = pSlowPion->getCharge();
920  momTargetSlowPion = PCmsLabTransform::labToCms(pSlowPion->get4Vector());
921  }
922  }
923  }
924 
925  double output = 0.0;
926 
927  double chargeTargetKaon = particle->getCharge();
928  if (requestedVariable == "HaveOpositeCharges")
929  {
930  if (chargeTargetKaon * chargeTargetSlowPion == -1)
931  output = 1;
932  }
933  //TODO: when momTargetSlowPion == momTargetSlowPion fail?
934  else if (requestedVariable == "cosKaonPion")
935  {
936  ROOT::Math::PxPyPzEVector momTargetKaon = PCmsLabTransform::labToCms(particle->get4Vector());
937  if (momTargetKaon == momTargetKaon && momTargetSlowPion == momTargetSlowPion)
938  output = momTargetKaon.Vect().Unit().Dot(momTargetSlowPion.Vect().Unit());
939  }
940 
941  return output;
942  };
943  return func;
944  }
945 
946  Manager::FunctionPtr FSCVariables(const std::vector<std::string>& arguments)
947  {
948  if (arguments.size() != 1)
949  B2FATAL("Wrong number of arguments (1 required) for meta function FSCVariables");
950 
951 
952  auto requestedVariable = arguments[0];
953  auto func = [requestedVariable](const Particle * particle) -> double {
954  StoreObjPtr<ParticleList> FastParticleList("pi+:inRoe");
955  if (!FastParticleList.isValid()) return 0;
956 
957 
958  if ((requestedVariable != "pFastCMS") && (requestedVariable != "cosSlowFast") && (requestedVariable != "cosTPTOFast") && (requestedVariable != "SlowFastHaveOpositeCharges"))
959  B2FATAL("Wrong variable " << requestedVariable << " requested. The possibilities are pFastCMS, cosSlowFast, cosTPTOFast or SlowFastHaveOpositeCharges");
960 
961 
962  double maximumProbFastest = 0;
963  ROOT::Math::PxPyPzEVector momFastParticle; //Momentum of Fast Pion in CMS-System
964  Particle* TargetFastParticle = nullptr;
965  for (unsigned int i = 0; i < FastParticleList->getListSize(); ++i)
966  {
967  Particle* particlei = FastParticleList->getParticle(i);
968  if (!particlei) continue;
969 
970  ROOT::Math::PxPyPzEVector momParticlei = PCmsLabTransform::labToCms(particlei->get4Vector());
971  if (momParticlei != momParticlei) continue;
972 
973  double probFastest = momParticlei.P();
974  if (probFastest > maximumProbFastest) {
975  maximumProbFastest = probFastest;
976  TargetFastParticle = particlei;
977  momFastParticle = momParticlei;
978  }
979  }
980 
981  // if nothing found
982  if (!TargetFastParticle) return 0;
983 
984 
985  double output = 0.0;
986 
987  if (requestedVariable == "cosTPTOFast")
988  output = std::get<double>(Variable::Manager::Instance().getVariable("cosTPTO")->function(TargetFastParticle));
989 
990  ROOT::Math::PxPyPzEVector momSlowPion = PCmsLabTransform::labToCms(particle->get4Vector()); //Momentum of Slow Pion in CMS-System
991  if (momSlowPion == momSlowPion) // FIXME
992  {
993  if (requestedVariable == "cosSlowFast") {
994  output = momSlowPion.Vect().Unit().Dot(momFastParticle.Vect().Unit());
995  } else if (requestedVariable == "SlowFastHaveOpositeCharges") {
996  if (particle->getCharge()*TargetFastParticle->getCharge() == -1) {
997  output = 1;
998  }
999  } else {
1000  output = momFastParticle.P();
1001  }
1002  }
1003 
1004  return output;
1005  };
1006  return func;
1007  }
1008 
1009 
1010 // Target Variables ----------------------------------------------------------------------------------------------
1011 
1012  // Lists used in target variables
1013  const std::vector<int> charmMesons = { 411, 421, 10411, 10421, 413, 423, 10413, 10423, 20413, 20423, 415, 425, 431, 10431, 433, 10433, 20433, 435};
1014 
1015  const std::vector<int> charmBaryons = { 4122, 4222, 4212, 4112, 4224, 4214, 4114, 4232, 4132, 4322, 4312, 4324, 4314, 4332, 4334, 4412, 4422,
1016  4414, 4424, 4432, 4434, 4444
1017  };
1018 
1019  const std::vector<int> qqbarMesons = {// light qqbar
1020  111, 9000111, 100111, 10111, 200111, 113, 10113, 20113, 9000113, 100113, 9010113, 9020113, 30113, 9030113, 9040113,
1021  115, 10115, 100115, 9000115, 117, 9000117, 9010117, 119,
1022  // ssbar Mesons
1023  221, 331, 9000221, 9010221, 100221, 10221, 100331, 9020221, 10331, 200221, 9030221, 9040221, 9050221, 9060221, 9070221, 223, 333, 10223, 20223,
1024  10333, 20333, 100223, 9000223, 9010223, 30223, 100333, 225, 9000225, 335, 9010225, 9020225, 10225, 9030225, 10335, 9040225, 100225, 100335,
1025  9050225, 9060225, 9070225, 227, 337, 229, 9000339, 9000229,
1026  // ccbar Mesons
1027  441, 10441, 100441, 443, 10443, 20443, 100443, 30443, 9000443, 9010443, 9020443, 445, 9000445
1028  };
1029 
1030  const std::vector<int> flavorConservingMesons = {// Excited light mesons that can decay into hadrons conserving flavor
1031  9000211, 100211, 10211, 200211, 213, 10213, 20213, 9000213, 100213, 9010213, 9020213, 30213, 9030213, 9040213,
1032  215, 10215, 100215, 9000215, 217, 9000217, 9010217, 219,
1033  // Excited K Mesons that hadronize conserving flavor
1034  30343, 10311, 10321, 100311, 100321, 200311, 200321, 9000311, 9000321, 313, 323, 10313, 10323, 20313, 20323, 100313, 100323,
1035  9000313, 9000323, 30313, 30323, 315, 325, 9000315, 9000325, 10315, 10325, 20315, 20325, 100315, 100325, 9010315,
1036  9010325, 317, 327, 9010317, 9010327, 319, 329, 9000319, 9000329
1037  };
1038 
1039  const std::vector<std::string> availableForIsRightTrack = { "Electron", // 0
1040  "IntermediateElectron", // 1
1041  "Muon", // 2
1042  "IntermediateMuon", // 3
1043  "KinLepton", // 4
1044  "IntermediateKinLepton",// 5
1045  "Kaon", // 6
1046  "SlowPion", // 7
1047  "FastHadron", // 8
1048  "Lambda", // 9
1049  "mcAssociated" // 10
1050  };
1051 
1052  const std::vector<std::string> availableForIsRightCategory = { "Electron", // 0
1053  "IntermediateElectron", // 1
1054  "Muon", // 2
1055  "IntermediateMuon", // 3
1056  "KinLepton", // 4
1057  "IntermediateKinLepton",// 5
1058  "Kaon", // 6
1059  "SlowPion", // 7
1060  "FastHadron", // 8
1061  "KaonPion", // 9
1062  "MaximumPstar", // 10
1063  "FSC", // 11
1064  "Lambda", // 12
1065  "mcAssociated" // 13
1066  };
1067 
1068 
1069 
1070  Manager::FunctionPtr isRightTrack(const std::vector<std::string>& arguments)
1071  {
1072  if (arguments.size() != 1) {
1073  B2FATAL("Wrong number of arguments (1 required) for meta function isRightTrack");
1074  }
1075 
1076  auto particleName = arguments[0];
1077 
1078  unsigned index = std::find(availableForIsRightTrack.begin(), availableForIsRightTrack.end(), particleName)
1079  - availableForIsRightTrack.begin();
1080  if (index == availableForIsRightTrack.size()) {
1081  B2FATAL("isRightTrack: Not available category " << particleName <<
1082  ". The possibilities are Electron, IntermediateElectron, Muon, IntermediateMuon, KinLepton, IntermediateKinLepton, Kaon, SlowPion, FastHadron and Lambda");
1083  }
1084 
1085  auto func = [index](const Particle * particle) -> int {
1086 
1087  const MCParticle* mcParticle = particle->getMCParticle();
1088  if (!mcParticle) return -2;
1089 
1090  int mcPDG = abs(mcParticle->getPDG());
1091 
1092  // ---------------------------- Mothers and Grandmothers ----------------------------------
1093  std::vector<int> mothersPDG;
1094  std::vector<const MCParticle*> mothersPointers;
1095 
1096  const MCParticle* mcMother = mcParticle->getMother();
1097  while (mcMother)
1098  {
1099  mothersPDG.push_back(abs(mcMother->getPDG()));
1100  mothersPointers.push_back(mcMother);
1101  if (abs(mcMother->getPDG()) == 511) break;
1102  mcMother = mcMother->getMother();
1103  }
1104 
1105  if (mothersPDG.size() == 0) return -2;
1106 
1107  //has associated mothers up to a B meson
1108  if (index == 10) return 1;
1109 
1110  // ---------------- Is D Meson in the decay chain --------------------------------------
1111 
1112  bool isCharmedMesonInChain = false;
1113  for (auto& iMCMotherPDG : mothersPDG)
1114  {
1115  if (std::find(charmMesons.begin(), charmMesons.end(), iMCMotherPDG) != charmMesons.end()) {
1116  isCharmedMesonInChain = true;
1117  break;
1118  }
1119  }
1120 
1121  // ---------------- Is Charmed Baryon in the decay chain --------------------------------
1122 
1123  bool isCharmedBaryonInChain = false;
1124  for (auto& iMCMotherPDG : mothersPDG)
1125  {
1126  if (std::find(charmBaryons.begin(), charmBaryons.end(), iMCMotherPDG) != charmBaryons.end()) {
1127  isCharmedBaryonInChain = true;
1128  break;
1129  }
1130  }
1131 
1132  // ---------------- Is neutral qqbar Meson in the decay chain --------------------------------
1133 
1134  bool isQQbarMesonInChain = false;
1135  for (auto& iMCMotherPDG : mothersPDG)
1136  {
1137  if (std::find(qqbarMesons.begin(), qqbarMesons.end(), iMCMotherPDG) != qqbarMesons.end()) {
1138  isQQbarMesonInChain = true;
1139  break;
1140  }
1141  }
1142 
1143  // -------------- Is the Hadron a descendant of a Meson that conserves flavor --------------------------
1144 
1145  bool isB0DaughterConservingFlavor = false;
1146  if (std::find(flavorConservingMesons.begin(), flavorConservingMesons.end(),
1147  mothersPDG.rbegin()[1]) != flavorConservingMesons.end())
1148  {
1149  isB0DaughterConservingFlavor = true;
1150  }
1151 
1152  // ----------------------------- Is the Hadron a single daughter of a tau ----- --------------------------
1153 
1154  bool isHadronSingleTauDaughter = false;
1155  if (mothersPDG.size() > 1 && mothersPDG.rbegin()[1] == 15)
1156  {
1157  int numberOfChargedDaughters = 0;
1158  for (auto& tauDaughter : mothersPointers.rbegin()[1]->getDaughters()) {
1159  if (tauDaughter->getCharge() != 0)
1160  numberOfChargedDaughters += 1;
1161  }
1162  if (numberOfChargedDaughters == 1)
1163  isHadronSingleTauDaughter = true;
1164  }
1165 
1166  if (index == 0 // Electron
1167  && mcPDG == Const::electron.getPDGCode() && mothersPDG[0] == 511)
1168  {
1169  return 1;
1170  } else if (index == 1 // IntermediateElectron
1171  && mcPDG == Const::electron.getPDGCode() && mothersPDG.size() > 1 && isQQbarMesonInChain == false)
1172  {
1173  return 1;
1174  } else if (index == 2 // Muon
1175  && mcPDG == Const::muon.getPDGCode() && mothersPDG[0] == 511)
1176  {
1177  return 1;
1178  } else if (index == 3 // IntermediateMuon
1179  && mcPDG == Const::muon.getPDGCode() && mothersPDG.size() > 1 && isQQbarMesonInChain == false)
1180  {
1181  return 1;
1182  } else if (index == 4 // KinLepton
1183  && (mcPDG == Const::muon.getPDGCode() || mcPDG == Const::electron.getPDGCode()) && mothersPDG[0] == 511)
1184  {
1185  return 1;
1186  } else if (index == 5 //IntermediateKinLepton
1187  && (mcPDG == Const::muon.getPDGCode() || mcPDG == Const::electron.getPDGCode()) && mothersPDG.size() > 1
1188  && isQQbarMesonInChain == false)
1189  {
1190  return 1;
1191  } else if (index == 6 // Kaon
1192  && mcPDG == Const::kaon.getPDGCode() && isQQbarMesonInChain == false
1193  && (isCharmedMesonInChain == true || isCharmedBaryonInChain == true))
1194  {
1195  return 1;
1196  } else if (index == 7 // SlowPion
1197  && mcPDG == Const::pion.getPDGCode() && mothersPDG.size() > 1 && mothersPDG[0] == 413 && mothersPDG[1] == 511)
1198  {
1199  return 1;
1200  } else if (index == 8 // FastHadron
1201  && (mcPDG == Const::pion.getPDGCode() || mcPDG == Const::kaon.getPDGCode())
1202  && isQQbarMesonInChain == false
1203  && (mothersPDG[0] == 511
1204  || (mothersPDG.rbegin()[0] == 511 && (isB0DaughterConservingFlavor == true || isHadronSingleTauDaughter == true))))
1205  {
1206  return 1;
1207  } else if (index == 9 // Lambda
1208  && mcPDG == Const::Lambda.getPDGCode() && isCharmedBaryonInChain == true)
1209  {
1210  return 1;
1211  } else
1212  {
1213  return 0;
1214  }
1215  };
1216  return func;
1217  }
1218 
1219  Manager::FunctionPtr isRightCategory(const std::vector<std::string>& arguments)
1220  {
1221  if (arguments.size() != 1) {
1222  B2FATAL("Wrong number of arguments (1 required) for meta function isRightCategory");
1223  }
1224 
1225  auto particleName = arguments[0];
1226 
1227  unsigned index = find(availableForIsRightCategory.begin(), availableForIsRightCategory.end(), particleName)
1228  - availableForIsRightCategory.begin();
1229  if (index == availableForIsRightCategory.size()) {
1230  B2FATAL("isRightCategory: Not available category " << particleName <<
1231  ". The possibilities are Electron, IntermediateElectron, Muon, IntermediateMuon, KinLepton, IntermediateKinLepton, Kaon, SlowPion, FastHadron, KaonPion, MaximumPstar, FSC and Lambda");
1232  }
1233 
1234  auto func = [index](const Particle * particle) -> int {
1235 
1236  Particle* nullParticle = nullptr;
1237  double qTarget = particle->getCharge();
1238  double qMC = Variable::isRestOfEventB0Flavor(nullParticle);
1239 
1240  const MCParticle* mcParticle = particle->getMCParticle();
1241  if (!mcParticle) return -2;
1242 
1243  int mcPDG = abs(mcParticle->getPDG());
1244 
1245  // ---------------------------- Mothers and Grandmothers ---------------------------------
1246  std::vector<int> mothersPDG;
1247  std::vector<const MCParticle*> mothersPointers;
1248 
1249  const MCParticle* mcMother = mcParticle->getMother();
1250  while (mcMother)
1251  {
1252  mothersPDG.push_back(abs(mcMother->getPDG()));
1253  mothersPointers.push_back(mcMother);
1254  if (abs(mcMother->getPDG()) == 511) break;
1255  mcMother = mcMother->getMother();
1256  }
1257 
1258  if (mothersPDG.size() == 0) return -2;
1259  //has associated mothers up to a B meson
1260  if (index == 13) return 1;
1261 
1262  // ---------------- Is D Meson in the decay chain --------------------------------------
1263 
1264  bool isCharmedMesonInChain = false;
1265  for (auto& iMCMotherPDG : mothersPDG)
1266  {
1267  if (std::find(charmMesons.begin(), charmMesons.end(), iMCMotherPDG) != charmMesons.end()) {
1268  isCharmedMesonInChain = true;
1269  break;
1270  }
1271  }
1272 
1273  // ---------------- Is Charmed Baryon in the decay chain --------------------------------
1274 
1275  bool isCharmedBaryonInChain = false;
1276  for (auto& iMCMotherPDG : mothersPDG)
1277  {
1278  if (std::find(charmBaryons.begin(), charmBaryons.end(), iMCMotherPDG) != charmBaryons.end()) {
1279  isCharmedBaryonInChain = true;
1280  break;
1281  }
1282  }
1283 
1284  // ---------------- Is neutral qqbar Meson in the decay chain --------------------------------
1285 
1286  bool isQQbarMesonInChain = false;
1287  for (auto& iMCMotherPDG : mothersPDG)
1288  {
1289  if (std::find(qqbarMesons.begin(), qqbarMesons.end(), iMCMotherPDG) != qqbarMesons.end()) {
1290  isQQbarMesonInChain = true;
1291  break;
1292  }
1293  }
1294 
1295  // -------------- Is the Hadron a descendant of a Meson that conserves flavor --------------------------
1296 
1297  bool isB0DaughterConservingFlavor = false;
1298  if (mothersPDG.size() > 1)
1299  {
1300  if (std::find(flavorConservingMesons.begin(), flavorConservingMesons.end(),
1301  mothersPDG.rbegin()[1]) != flavorConservingMesons.end()) {
1302  isB0DaughterConservingFlavor = true;
1303  }
1304  }
1305 
1306  // ----------------------------- Is the Hadron a single daughter of a tau ----- --------------------------
1307 
1308  bool isHadronSingleTauDaughter = false;
1309  if (mothersPDG.size() > 1 && mothersPDG.rbegin()[1] == 15)
1310  {
1311  int numberOfChargedDaughters = 0;
1312  for (auto& tauDaughter : mothersPointers.rbegin()[1]->getDaughters()) {
1313  if (tauDaughter->getCharge() != 0)
1314  numberOfChargedDaughters += 1;
1315  }
1316  if (numberOfChargedDaughters == 1)
1317  isHadronSingleTauDaughter = true;
1318  }
1319 
1320  // ---------------------------- For KaonPion Category ------------------------------------
1321  bool haveKaonPionSameMother = false;
1322  if (index == 9) // KaonPion
1323  {
1324  const MCParticle* mcSlowPionMother = nullptr;
1325  StoreObjPtr<ParticleList> SlowPionList("pi+:inRoe");
1326  Particle* targetSlowPion = nullptr;
1327  if (SlowPionList.isValid()) {
1328  double mcProbSlowPion = 0;
1329  for (unsigned int i = 0; i < SlowPionList->getListSize(); ++i) {
1330  Particle* pSlowPion = SlowPionList->getParticle(i);
1331  if (!pSlowPion) continue;
1332  if (pSlowPion->hasExtraInfo("isRightCategory(SlowPion)")) {
1333  double probSlowPion = pSlowPion->getExtraInfo("isRightCategory(SlowPion)");
1334  if (probSlowPion > mcProbSlowPion) {
1335  mcProbSlowPion = probSlowPion;
1336  targetSlowPion = pSlowPion;
1337  }
1338  }
1339  }
1340  if (targetSlowPion != nullptr) {
1341  const MCParticle* mcSlowPion = targetSlowPion ->getMCParticle();
1342  // SlowPion_q = targetSlowPion->getCharge();
1343  if (mcSlowPion != nullptr && mcSlowPion->getMother() != nullptr
1344  && abs(mcSlowPion->getPDG()) == Const::pion.getPDGCode() && abs(mcSlowPion->getMother()->getPDG()) == 413) {
1345  mcSlowPionMother = mcSlowPion->getMother();
1346  }
1347  }
1348  }
1349 
1350  if (std::find(mothersPointers.begin(), mothersPointers.end(), mcSlowPionMother) != mothersPointers.end())
1351  haveKaonPionSameMother = true;
1352 
1353  }
1354 
1355  // ---------------------------- For FastSlowCorrelated Category ----------------------------
1356  int FastParticlePDGMother = 0;
1357  double qFSC = 0;
1358  if (index == 11) // FSC
1359  {
1360  StoreObjPtr<ParticleList> FastParticleList("pi+:inRoe");
1361  Particle* targetFastParticle = nullptr;
1362  if (FastParticleList.isValid()) {
1363  double mcProbFastest = 0;
1364  for (unsigned int i = 0; i < FastParticleList->getListSize(); ++i) {
1365  Particle* particlei = FastParticleList->getParticle(i);
1366  if (!particlei) continue;
1367 
1368  ROOT::Math::PxPyPzEVector momParticlei = PCmsLabTransform::labToCms(particlei->get4Vector());
1369  if (momParticlei == momParticlei) {
1370  double probFastest = momParticlei.P();
1371  if (probFastest > mcProbFastest) {
1372  mcProbFastest = probFastest;
1373  targetFastParticle = particlei;
1374  }
1375  }
1376  }
1377  if (targetFastParticle != nullptr) {
1378  const MCParticle* mcFastParticle = targetFastParticle ->getMCParticle();
1379  // FastParticle_q = targetFastParticle->getCharge();
1380  if (mcFastParticle != nullptr && mcFastParticle->getMother() != nullptr) {
1381  FastParticlePDGMother = abs(mcFastParticle->getMother()->getPDG());
1382  qFSC = mcFastParticle->getCharge();
1383  }
1384  }
1385  }
1386  }
1387 
1388  // ------------------------------ Outputs -----------------------------------
1389  if (index == 0 // Electron
1390  && qTarget == qMC && mcPDG == Const::electron.getPDGCode() && mothersPDG[0] == 511)
1391  {
1392  return 1;
1393  } else if (index == 1 // IntermediateElectron
1394  && qTarget != qMC && mcPDG == Const::electron.getPDGCode() && mothersPDG.size() > 1
1395  && isQQbarMesonInChain == false)
1396  {
1397  return 1;
1398  } else if (index == 2 // Muon
1399  && qTarget == qMC && mcPDG == Const::muon.getPDGCode() && mothersPDG[0] == 511)
1400  {
1401  return 1;
1402  } else if (index == 3 // IntermediateMuon
1403  && qTarget != qMC && mcPDG == Const::muon.getPDGCode() && mothersPDG.size() > 1
1404  && isQQbarMesonInChain == false)
1405  {
1406  return 1;
1407  } else if (index == 4 // KinLepton
1408  && qTarget == qMC
1409  && (mcPDG == Const::electron.getPDGCode() || mcPDG == Const::muon.getPDGCode()) && mothersPDG[0] == 511)
1410  {
1411  return 1;
1412  } else if (index == 5 // IntermediateKinLepton
1413  && qTarget != qMC && (mcPDG == Const::electron.getPDGCode() || mcPDG == Const::muon.getPDGCode())
1414  && mothersPDG.size() > 1 && isQQbarMesonInChain == false)
1415  {
1416  return 1;
1417  } else if (index == 6// Kaon
1418  && qTarget == qMC && mcPDG == Const::kaon.getPDGCode() && isQQbarMesonInChain == false
1419  && (isCharmedMesonInChain == true || isCharmedBaryonInChain == true))
1420  {
1421  return 1;
1422  } else if (index == 7 // SlowPion
1423  && qTarget != qMC && mcPDG == Const::pion.getPDGCode()
1424  && mothersPDG.size() > 1 && mothersPDG[0] == 413 && mothersPDG[1] == 511)
1425  {
1426  return 1;
1427  } else if (index == 8 // FastHadron
1428  && qTarget == qMC && (mcPDG == Const::pion.getPDGCode() || mcPDG == Const::kaon.getPDGCode())
1429  && isQQbarMesonInChain == false
1430  && (mothersPDG[0] == 511 || (mothersPDG.rbegin()[0] == 511
1431  && (isB0DaughterConservingFlavor == true || isHadronSingleTauDaughter == true))))
1432  {
1433  return 1;
1434  } else if (index == 9 // KaonPion
1435  && qTarget == qMC && mcPDG == Const::kaon.getPDGCode() && haveKaonPionSameMother == true)
1436  {
1437  return 1;
1438  } else if (index == 10 && qTarget == qMC) // MaximumPstar
1439  {
1440  return 1;
1441  } else if (index == 11 // FSC
1442  && qTarget != qMC && mothersPDG.size() > 1 && qFSC == qMC
1443  && mcPDG == Const::pion.getPDGCode() && FastParticlePDGMother == 511 && isQQbarMesonInChain == false)
1444  {
1445  return 1;
1446  } else if (index == 12 // Lambda
1447  && (particle->getPDGCode() / abs(particle->getPDGCode())) != qMC
1448  && mcPDG == Const::Lambda.getPDGCode() && isCharmedBaryonInChain == true)
1449  {
1450  return 1;
1451  } else
1452  {
1453  return 0;
1454  }
1455  };
1456 
1457  return func;
1458  }
1459 
1460  Manager::FunctionPtr hasHighestProbInCat(const std::vector<std::string>& arguments)
1461  {
1462  if (arguments.size() != 2) {
1463  B2FATAL("Wrong number of arguments (2 required) for meta function hasHighestProbInCat");
1464  }
1465 
1466  auto particleListName = arguments[0];
1467  auto extraInfoName = arguments[1];
1468 
1469  bool isAvailable = false;
1470  for (const auto& name : availableForIsRightTrack) {
1471  if (extraInfoName == "isRightTrack(" + name + ")") {
1472  isAvailable = true;
1473  break;
1474  }
1475  }
1476  for (const auto& name : availableForIsRightCategory) {
1477  if (extraInfoName == "isRightCategory(" + name + ")") {
1478  isAvailable = true;
1479  break;
1480  }
1481  }
1482  if (extraInfoName == "isRightTrack(MaximumPstar)")
1483  isAvailable = true;
1484 
1485 
1486  if (!isAvailable) {
1487  string strAvailableForIsRightTrack;
1488  for (const auto& name : availableForIsRightTrack)
1489  strAvailableForIsRightTrack += name + " ";
1490  string strAvailableForIsRightCategory;
1491  for (const auto& name : availableForIsRightCategory)
1492  strAvailableForIsRightCategory += name + " ";
1493 
1494  B2FATAL("hasHighestProbInCat: Not available category" << extraInfoName <<
1495  ". The possibilities for isRightTrack() are " << endl << strAvailableForIsRightTrack << " MaximumPstar" << endl <<
1496  "The possibilities for isRightCategory() are " << endl << strAvailableForIsRightCategory);
1497  }
1498 
1499  auto func = [particleListName, extraInfoName](const Particle * particle) -> bool {
1500  StoreObjPtr<ParticleList> ListOfParticles(particleListName);
1501  if (!ListOfParticles.isValid()) return 0;
1502 
1503  double maximumProb = 0;
1504  for (unsigned int i = 0; i < ListOfParticles->getListSize(); ++i)
1505  {
1506  const Particle* particlei = ListOfParticles->getParticle(i);
1507  if (!particlei) continue;
1508 
1509  double prob = 0;
1510  if (extraInfoName == "isRightTrack(MaximumPstar)") {
1511  ROOT::Math::PxPyPzEVector momParticlei = PCmsLabTransform::labToCms(particlei->get4Vector());
1512  if (momParticlei == momParticlei) {
1513  prob = momParticlei.P();
1514  }
1515  } else {
1516  if (particlei->hasExtraInfo(extraInfoName)) {
1517  prob = particlei->getExtraInfo(extraInfoName);
1518  }
1519  }
1520  if (prob > maximumProb) {
1521  maximumProb = prob;
1522  }
1523 
1524  }
1525 
1526  bool output = false;
1527  if ((extraInfoName == "isRightTrack(MaximumPstar)") && (PCmsLabTransform::labToCms(particle->get4Vector()).P() == maximumProb))
1528  {
1529  output = true;
1530  } else if (extraInfoName != "isRightTrack(MaximumPstar)" && particle->hasExtraInfo(extraInfoName))
1531  {
1532  if (particle->getExtraInfo(extraInfoName) == maximumProb) output = true;
1533  }
1534 
1535  return output;
1536  };
1537  return func;
1538  }
1539 
1540  Manager::FunctionPtr HighestProbInCat(const std::vector<std::string>& arguments)
1541  {
1542  if (arguments.size() != 2) {
1543  B2FATAL("Wrong number of arguments (2 required) for meta function HighestProbInCat");
1544  }
1545 
1546  auto particleListName = arguments[0];
1547  auto extraInfoName = arguments[1];
1548 
1549  bool isAvailable = false;
1550  for (const auto& name : availableForIsRightTrack) {
1551  if (extraInfoName == "isRightTrack(" + name + ")") {
1552  isAvailable = true;
1553  break;
1554  }
1555  }
1556  for (const auto& name : availableForIsRightCategory) {
1557  if (extraInfoName == "isRightCategory(" + name + ")") {
1558  isAvailable = true;
1559  break;
1560  }
1561  }
1562  if (extraInfoName == "isRightTrack(MaximumPstar)")
1563  isAvailable = true;
1564 
1565 
1566  if (!isAvailable) {
1567  string strAvailableForIsRightTrack;
1568  for (const auto& name : availableForIsRightTrack)
1569  strAvailableForIsRightTrack += name + " ";
1570  string strAvailableForIsRightCategory;
1571  for (const auto& name : availableForIsRightCategory)
1572  strAvailableForIsRightCategory += name + " ";
1573 
1574  B2FATAL("HighestProbInCat: Not available category" << extraInfoName <<
1575  ". The possibilities for isRightTrack() are " << endl << strAvailableForIsRightTrack << " MaximumPstar" << endl <<
1576  "The possibilities for isRightCategory() are " << endl << strAvailableForIsRightCategory);
1577  }
1578 
1579  auto func = [particleListName, extraInfoName](const Particle*) -> double {
1580  StoreObjPtr<ParticleList> ListOfParticles(particleListName);
1581  if (!ListOfParticles.isValid()) return 0;
1582 
1583  double maximumProb = 0;
1584  for (unsigned int i = 0; i < ListOfParticles->getListSize(); ++i)
1585  {
1586  const Particle* particlei = ListOfParticles->getParticle(i);
1587  if (!particlei) continue;
1588 
1589  double prob = 0;
1590  if (extraInfoName == "isRightTrack(MaximumPstar)") {
1591  ROOT::Math::PxPyPzEVector momParticlei = PCmsLabTransform::labToCms(particlei->get4Vector());
1592  if (momParticlei == momParticlei) {
1593  prob = momParticlei.P();
1594  }
1595  } else {
1596  if (particlei->hasExtraInfo(extraInfoName)) {
1597  prob = particlei->getExtraInfo(extraInfoName);
1598  }
1599  }
1600  maximumProb = max(maximumProb, prob);
1601  }
1602 
1603  return maximumProb;
1604  };
1605  return func;
1606  }
1607 
1608 
1609  // List of available extrainfos used in QpOf, weightedQpOf and variableOfTarget.
1610  std::vector<std::string> availableExtraInfos = { "isRightTrack(Electron)", // 0
1611  "isRightTrack(IntermediateElectron)", // 1
1612  "isRightTrack(Muon)", // 2
1613  "isRightTrack(IntermediateMuon)", // 3
1614  "isRightTrack(KinLepton)", // 4
1615  "isRightTrack(IntermediateKinLepton)",// 5
1616  "isRightTrack(Kaon)", // 6
1617  "isRightTrack(SlowPion)", // 7
1618  "isRightTrack(FastHadron)", // 8
1619  "isRightTrack(MaximumPstar)", // 9
1620  "isRightTrack(Lambda)", // 10
1621  "isRightCategory(Electron)", // 11
1622  "isRightCategory(IntermediateElectron)", // 12
1623  "isRightCategory(Muon)", // 13
1624  "isRightCategory(IntermediateMuon)", // 14
1625  "isRightCategory(KinLepton)", // 15
1626  "isRightCategory(IntermediateKinLepton)",// 16
1627  "isRightCategory(Kaon)", // 17
1628  "isRightCategory(SlowPion)", // 18
1629  "isRightCategory(FastHadron)", // 19
1630  "isRightCategory(MaximumPstar)", // 20
1631  "isRightCategory(Lambda)", // 21
1632  "isRightCategory(KaonPion)", // 22
1633  "isRightCategory(FSC)", // 23
1634  };
1635 
1636  Manager::FunctionPtr QpOf(const std::vector<std::string>& arguments)
1637  {
1638  if (arguments.size() != 3) {
1639  B2FATAL("Wrong number of arguments (3 required) for meta function QpOf");
1640  }
1641 
1642  auto particleListName = arguments[0];
1643  auto outputExtraInfo = arguments[1];
1644  auto rankingExtraInfo = arguments[2];
1645 
1646  unsigned indexRanking = find(availableExtraInfos.begin(), availableExtraInfos.end(),
1647  rankingExtraInfo) - availableExtraInfos.begin();
1648  unsigned indexOutput = find(availableExtraInfos.begin(), availableExtraInfos.end(),
1649  outputExtraInfo) - availableExtraInfos.begin();
1650 
1651  if (indexRanking == availableExtraInfos.size() or indexOutput == availableExtraInfos.size()) {
1652  string strAvailableForIsRightTrack;
1653  for (const auto& name : availableForIsRightTrack)
1654  strAvailableForIsRightTrack += name + " ";
1655  string strAvailableForIsRightCategory;
1656  for (const auto& name : availableForIsRightCategory)
1657  strAvailableForIsRightCategory += name + " ";
1658 
1659  B2FATAL("QpOf: Not available category " << rankingExtraInfo <<
1660  ". The possibilities for isRightTrack() are " << endl << strAvailableForIsRightTrack << " MaximumPstar" << endl <<
1661  "The possibilities for isRightCategory() are " << endl << strAvailableForIsRightCategory);
1662  }
1663 
1664  auto func = [particleListName, indexOutput, indexRanking](const Particle*) -> double {
1665  StoreObjPtr<ParticleList> ListOfParticles(particleListName);
1666  if (!ListOfParticles.isValid()) return 0;
1667 
1668  Particle* target = nullptr; //Particle selected as target
1669  double maximumTargetProb = 0; //Probability of being the target track from the track level
1670  for (unsigned int i = 0; i < ListOfParticles->getListSize(); ++i)
1671  {
1672  Particle* particlei = ListOfParticles->getParticle(i);
1673  if (!particlei) continue;
1674 
1675  double target_prob = 0;
1676  if (indexRanking == 9 || indexRanking == 20) { // MaximumPstar
1677  ROOT::Math::PxPyPzEVector momParticlei = PCmsLabTransform::labToCms(particlei->get4Vector());
1678  if (momParticlei == momParticlei) {
1679  target_prob = momParticlei.P();
1680  }
1681  } else {
1682  if (particlei->hasExtraInfo(availableExtraInfos[indexRanking])) {
1683  target_prob = particlei->getExtraInfo(availableExtraInfos[indexRanking]);
1684  }
1685  }
1686 
1687  if (target_prob > maximumTargetProb) {
1688  maximumTargetProb = target_prob;
1689  target = particlei;
1690  }
1691  }
1692 
1693  // nothing found
1694  if (!target) return 0;
1695 
1696  double qTarget = 0; //Flavor of the track selected as target
1697  // Get the flavor of the track selected as target
1698  if (indexRanking == 10 || indexRanking == 21) // Lambda
1699  {
1700  qTarget = (-1) * target->getPDGCode() / abs(target->getPDGCode());
1701  // IntermediateElectron IntermediateMuon IntermediateKinLepton SlowPion
1702  } else if (indexRanking == 1 || indexRanking == 3 || indexRanking == 5 || indexRanking == 7 ||
1703  indexRanking == 12 || indexRanking == 14 || indexRanking == 16 || indexRanking == 18)
1704  {
1705  qTarget = (-1) * target->getCharge();
1706  } else
1707  {
1708  qTarget = target->getCharge();
1709  }
1710 
1711  //Get the probability of being right classified flavor from event level
1712  double prob = target->getExtraInfo(availableExtraInfos[indexOutput]);
1713 
1714  //float r = abs(2 * prob - 1); //Definition of the dilution factor */
1715  //return 0.5 * (qTarget * r + 1);
1716  return qTarget * prob;
1717  };
1718  return func;
1719  }
1720 
1721  Manager::FunctionPtr weightedQpOf(const std::vector<std::string>& arguments)
1722  {
1723  if (arguments.size() != 3) {
1724  B2FATAL("Wrong number of arguments (3 required) for meta function weightedQpOf");
1725  }
1726 
1727  //used by simple_flavor_tagger
1728 
1729  auto particleListName = arguments[0];
1730  auto outputExtraInfo = arguments[1];
1731  auto rankingExtraInfo = arguments[2];
1732 
1733 
1734  unsigned indexRanking = find(availableExtraInfos.begin(), availableExtraInfos.end(),
1735  rankingExtraInfo) - availableExtraInfos.begin();
1736  unsigned indexOutput = find(availableExtraInfos.begin(), availableExtraInfos.end(),
1737  outputExtraInfo) - availableExtraInfos.begin();
1738 
1739 
1740  if (indexRanking == availableExtraInfos.size() or indexOutput == availableExtraInfos.size()) {
1741  string strAvailableForIsRightTrack;
1742  for (const auto& name : availableForIsRightTrack)
1743  strAvailableForIsRightTrack += name + " ";
1744  string strAvailableForIsRightCategory;
1745  for (const auto& name : availableForIsRightCategory)
1746  strAvailableForIsRightCategory += name + " ";
1747 
1748  B2FATAL("weightedQpOf: Not available category " << rankingExtraInfo <<
1749  ". The possibilities for isRightTrack() are " << endl << strAvailableForIsRightTrack << " MaximumPstar" << endl <<
1750  "The possibilities for isRightCategory() are " << endl << strAvailableForIsRightCategory);
1751  }
1752 
1753 
1754  auto func = [particleListName, indexOutput, indexRanking, rankingExtraInfo](const Particle*) -> double {
1755 
1756  StoreObjPtr<ParticleList> ListOfParticles(particleListName);
1757  if (!ListOfParticles) return 0;
1758  if (ListOfParticles->getListSize() == 0) return 0;
1759 
1760 
1761  auto compare = [rankingExtraInfo](const Particle * part1, const Particle * part2)-> bool {
1762  double info1 = 0;
1763  double info2 = 0;
1764  if (part1->hasExtraInfo(rankingExtraInfo)) info1 = part1->getExtraInfo(rankingExtraInfo);
1765  if (part2->hasExtraInfo(rankingExtraInfo)) info2 = part2->getExtraInfo(rankingExtraInfo);
1766  return (info1 > info2);
1767  };
1768 
1769  auto compareMomentum = [rankingExtraInfo](const Particle * part1, const Particle * part2)-> bool {
1770  double info1 = PCmsLabTransform::labToCms(part1->get4Vector()).P();
1771  double info2 = PCmsLabTransform::labToCms(part2->get4Vector()).P();
1772  return (info1 > info2);
1773  };
1774 
1775  std::vector<const Particle*> ParticleVector(ListOfParticles->getListSize());
1776  for (unsigned int i = 0; i < ListOfParticles->getListSize(); ++i)
1777  {
1778  ParticleVector[i] = ListOfParticles->getParticle(i);
1779  }
1780 
1781  if (indexRanking == 9 || indexRanking == 20)
1782  std::sort(ParticleVector.begin(), ParticleVector.end(), compareMomentum); // MaximumPstar
1783  else
1784  std::sort(ParticleVector.begin(), ParticleVector.end(), compare);
1785 
1786 
1787  double final_value = 0.0;
1788  if (ParticleVector.size() != 0) final_value = 1.0;
1789 
1790  //Loop over K+ vector until 3 or empty
1791  int Limit = min(3, int(ParticleVector.size()));
1792  double val1 = 1.0;
1793  double val2 = 1.0;
1794  for (int i = 0; i < Limit; ++i)
1795  {
1796  if (ParticleVector[i]->hasExtraInfo(availableExtraInfos[indexOutput])) {
1797  double flavor = 0.0;
1798  if (indexRanking == 10 || indexRanking == 21) { // Lambda
1799  flavor = - copysign(1, ParticleVector[i]->getPDGCode());
1800  // IntermediateElectron IntermediateMuon IntermediateKinLepton SlowPion
1801  } else if (indexRanking == 1 || indexRanking == 3 || indexRanking == 5 || indexRanking == 7 ||
1802  indexRanking == 12 || indexRanking == 14 || indexRanking == 16 || indexRanking == 18) {
1803  flavor = - ParticleVector[i]->getCharge();
1804  } else {
1805  flavor = + ParticleVector[i]->getCharge();
1806  }
1807 
1808  double p = ParticleVector[i]->getExtraInfo(availableExtraInfos[indexOutput]);
1809  // B2INFO("Right Track:" << ParticleVector[i]->getExtraInfo(availableExtraInfos[indexRanking]));
1810  // B2INFO("Right Cat:" << ParticleVector[i]->getExtraInfo(availableExtraInfos[indexOutput]));
1811  double qp = (flavor * p);
1812  val1 *= 1 + qp;
1813  val2 *= 1 - qp;
1814  }
1815  }
1816  final_value = (val1 - val2) / (val1 + val2);
1817 
1818  return final_value;
1819  };
1820  return func;
1821  }
1822 
1823  Manager::FunctionPtr variableOfTarget(const std::vector<std::string>& arguments)
1824  {
1825 
1826  if (arguments.size() != 3)
1827  B2FATAL("Wrong number of arguments (3 required) for meta function variableOfTarget");
1828 
1829  std::string particleListName = arguments[0];
1830  std::string inputVariable = arguments[1];
1831  std::string rankingExtraInfo = arguments[2];
1832 
1833  int indexRanking = -1;
1834 
1835  for (unsigned i = 0; i < availableExtraInfos.size(); ++i) {
1836  if (rankingExtraInfo == availableExtraInfos[i]) {indexRanking = i; break;}
1837  }
1838 
1839  if (indexRanking == -1) {
1840  string strAvailableForIsRightTrack;
1841  for (const auto& name : availableForIsRightTrack)
1842  strAvailableForIsRightTrack += name + " ";
1843  string strAvailableForIsRightCategory;
1844  for (const auto& name : availableForIsRightCategory)
1845  strAvailableForIsRightCategory += name + " ";
1846 
1847  B2FATAL("variableOfTarget: Not available category " << rankingExtraInfo <<
1848  ". The possibilities for isRightTrack() are " << endl << strAvailableForIsRightTrack << " MaximumPstar" << endl <<
1849  "The possibilities for isRightCategory() are " << endl << strAvailableForIsRightCategory);
1850  }
1851 
1852  const Variable::Manager::Var* var = Manager::Instance().getVariable(inputVariable);
1853  auto func = [particleListName, var, indexRanking](const Particle*) -> double {
1854  StoreObjPtr<ParticleList> ListOfParticles(particleListName);
1855  if (!ListOfParticles.isValid()) return realNaN;
1856 
1857  Particle* target = nullptr; //Particle selected as target
1858 
1859  double maximumTargetProb = 0; //Probability of being the target track from the track level
1860  for (unsigned int i = 0; i < ListOfParticles->getListSize(); ++i)
1861  {
1862  Particle* particlei = ListOfParticles->getParticle(i);
1863  if (!particlei) continue;
1864 
1865  double target_prob = 0;
1866  if (indexRanking == 9 || indexRanking == 20) { // MaximumPstar
1867  ROOT::Math::PxPyPzEVector momParticlei = PCmsLabTransform::labToCms(particlei->get4Vector());
1868  if (momParticlei == momParticlei) {
1869  target_prob = momParticlei.P();
1870  }
1871  } else {
1872  if (particlei->hasExtraInfo(availableExtraInfos[indexRanking])) {
1873  target_prob = particlei->getExtraInfo(availableExtraInfos[indexRanking]);
1874  }
1875  }
1876  if (target_prob > maximumTargetProb) {
1877  maximumTargetProb = target_prob;
1878  target = particlei;
1879  }
1880  }
1881 
1882  // no target found
1883  if (!target) return realNaN;
1884 
1885  if (std::holds_alternative<double>(var->function(target)))
1886  {
1887  return std::get<double>(var->function(target));
1888  } else if (std::holds_alternative<int>(var->function(target)))
1889  {
1890  return std::get<int>(var->function(target));
1891  } else if (std::holds_alternative<bool>(var->function(target)))
1892  {
1893  return std::get<bool>(var->function(target));
1894  } else return realNaN;
1895  };
1896  return func;
1897  }
1898 
1899  Manager::FunctionPtr hasTrueTarget(const std::vector<std::string>& arguments)
1900  {
1901  if (arguments.size() != 1) {
1902  B2FATAL("Wrong number of arguments (1 required) for meta function hasTrueTarget");
1903  }
1904 
1905  auto categoryName = arguments[0];
1906 
1907  bool isAvailable = false;
1908  for (const auto& name : availableForIsRightCategory) {
1909  if (categoryName == name) {
1910  isAvailable = true;
1911  break;
1912  }
1913  }
1914  if (categoryName == "mcAssociated")
1915  isAvailable = false;
1916 
1917  if (!isAvailable) {
1918  string strAvailableForIsRightCategory;
1919  for (const auto& name : availableForIsRightCategory) {
1920  if (name == "mcAssociated") continue;
1921  strAvailableForIsRightCategory += name + " ";
1922  }
1923  B2FATAL("hasTrueTarget: Not available category" << categoryName <<
1924  ". The possibilities for the category name are " << endl << strAvailableForIsRightCategory);
1925  }
1926 
1927  auto func = [categoryName](const Particle*) -> double {
1928  std::string particleListName;
1929  std::string trackTargetName = categoryName;
1930 
1931  if (categoryName == "Electron" || categoryName == "IntermediateElectron") particleListName = "e+:inRoe";
1932  else if (categoryName == "Muon" || categoryName == "IntermediateMuon" || categoryName == "KinLepton" || categoryName == "IntermediateKinLepton") particleListName = "mu+:inRoe";
1933  else if (categoryName == "Kaon" || categoryName == "KaonPion") {particleListName = "K+:inRoe"; trackTargetName = "Kaon";}
1934  else if (categoryName == "Lambda") particleListName = "Lambda0:inRoe";
1935  else particleListName = "pi+:inRoe";
1936 
1937  if (categoryName == "FSC") trackTargetName = "SlowPion";
1938 
1939  StoreObjPtr<ParticleList> ListOfParticles(particleListName);
1940  if (!ListOfParticles.isValid()) return realNaN;
1941 
1942  Variable::Manager& manager = Variable::Manager::Instance();
1943 
1944  bool particlesHaveMCAssociated = false;
1945  int nTargets = 0;
1946  for (unsigned int i = 0; i < ListOfParticles->getListSize(); ++i)
1947  {
1948  Particle* iParticle = ListOfParticles->getParticle(i);
1949  if (!iParticle) continue;
1950 
1951  if (categoryName == "MaximumPstar") {
1952  bool targetFlag = std::get<bool>(manager.getVariable("hasHighestProbInCat(pi+:inRoe, isRightTrack(MaximumPstar))")->function(
1953  iParticle));
1954  if (targetFlag) {
1955  particlesHaveMCAssociated = true;
1956  ++nTargets;
1957  }
1958  } else {
1959  int targetFlag = std::get<int>(manager.getVariable("isRightTrack(" + trackTargetName + ")")->function(iParticle));
1960  if (targetFlag != -2) particlesHaveMCAssociated = true;
1961  if (targetFlag == 1) ++nTargets;
1962  }
1963  }
1964 
1965  if (!particlesHaveMCAssociated) return realNaN;
1966  return (nTargets > 0);
1967  };
1968  return func;
1969  }
1970 
1971  Manager::FunctionPtr isTrueCategory(const std::vector<std::string>& arguments)
1972  {
1973  if (arguments.size() != 1) {
1974  B2FATAL("Wrong number of arguments (1 required) for meta function isTrueCategory");
1975  }
1976  auto categoryName = arguments[0];
1977 
1978  bool isAvailable = false;
1979  for (const auto& name : availableForIsRightCategory) {
1980  if (categoryName == name) {
1981  isAvailable = true;
1982  break;
1983  }
1984  }
1985  if (categoryName == "mcAssociated")
1986  isAvailable = false;
1987 
1988  if (!isAvailable) {
1989  string strAvailableForIsRightCategory;
1990  for (const auto& name : availableForIsRightCategory) {
1991  if (name == "mcAssociated") continue;
1992  strAvailableForIsRightCategory += name + " ";
1993  }
1994  B2FATAL("isTrueCategory: Not available category" << categoryName <<
1995  ". The possibilities for the category name are " << endl << strAvailableForIsRightCategory);
1996  }
1997 
1998  auto func = [categoryName](const Particle*) -> double {
1999  std::string particleListName;
2000  std::string trackTargetName = categoryName;
2001 
2002  if (categoryName == "Electron" || categoryName == "IntermediateElectron") particleListName = "e+:inRoe";
2003  else if (categoryName == "Muon" || categoryName == "IntermediateMuon" || categoryName == "KinLepton" || categoryName == "IntermediateKinLepton") particleListName = "mu+:inRoe";
2004  else if (categoryName == "Kaon" || categoryName == "KaonPion") {particleListName = "K+:inRoe"; trackTargetName = "Kaon";}
2005  else if (categoryName == "Lambda") particleListName = "Lambda0:inRoe";
2006  else particleListName = "pi+:inRoe";
2007 
2008  if (categoryName == "FSC") trackTargetName = "SlowPion";
2009 
2010  StoreObjPtr<ParticleList> ListOfParticles(particleListName);
2011  if (!ListOfParticles.isValid()) return realNaN;
2012 
2013  std::vector<Particle*> targetParticles;
2014  std::vector<Particle*> targetParticlesCategory;
2015  Variable::Manager& manager = Variable::Manager::Instance();
2016 
2017 
2018  double output = 0;
2019  int nTargets = 0;
2020  for (unsigned int i = 0; i < ListOfParticles->getListSize(); ++i)
2021  {
2022  Particle* iParticle = ListOfParticles->getParticle(i);
2023  if (!iParticle) continue;
2024 
2025  if (categoryName == "MaximumPstar") {
2026  if (std::get<bool>(manager.getVariable("hasHighestProbInCat(pi+:inRoe, isRightTrack(MaximumPstar))")->function(iParticle)))
2027  targetParticles.push_back(iParticle);
2028  } else if (std::get<int>(manager.getVariable("isRightTrack(" + trackTargetName + ")")->function(iParticle))) {
2029  targetParticles.push_back(iParticle);
2030  }
2031  }
2032 
2033  for (const auto& targetParticle : targetParticles)
2034  {
2035  int isTargetOfRightCategory = std::get<int>(manager.getVariable("isRightCategory(" + categoryName + ")")->function(
2036  targetParticle));
2037  if (isTargetOfRightCategory == 1) {
2038  output = 1;
2039  nTargets += 1; targetParticlesCategory.push_back(targetParticle);
2040  } else if (isTargetOfRightCategory == -2 && output != 1)
2041  output = realNaN;
2042  }
2043 
2044  /* if (nTargets > 1) {
2045  B2INFO("The Category " << categoryName << " has " << std::to_string(nTargets) << " target tracks.");
2046  for (auto& iTargetParticlesCategory : targetParticlesCategory) {
2047  const MCParticle* MCp = iTargetParticlesCategory->getMCParticle();
2048 
2049  RelationVector<Particle> mcRelations = MCp->getRelationsFrom<Particle>();
2050  if (mcRelations.size() > 1) B2WARNING("MCparticle is related to two particles");
2051 
2052  B2INFO("MCParticle has pdgCode = " << MCp->getPDG() << ", MCMother has pdgCode = " << MCp-> getMother()->getPDG() << " and " <<
2053  MCp-> getMother()->getNDaughters() << " daughters.");
2054 
2055  for (auto& iDaughter : MCp->getMother()->getDaughters()) B2INFO("iDaughter PDGCode = " << iDaughter->getPDG());
2056  }
2057  }*/
2058 
2059  return output;
2060  };
2061  return func;
2062  }
2063 
2064  Manager::FunctionPtr qrOutput(const std::vector<std::string>& arguments)
2065  {
2066  if (arguments.size() != 1)
2067  B2FATAL("Wrong number of arguments for meta function qrOutput");
2068 
2069  std::string combinerMethod = arguments[0];
2070  auto func = [combinerMethod](const Particle * particle) -> double {
2071 
2072  double output = realNaN;
2073  auto* flavorTaggerInfo = particle->getRelatedTo<FlavorTaggerInfo>();
2074 
2075  if (flavorTaggerInfo && flavorTaggerInfo->getUseModeFlavorTagger() == "Expert")
2076  output = flavorTaggerInfo->getMethodMap(combinerMethod)->getQrCombined();
2077 
2078  return output;
2079  };
2080  return func;
2081  }
2082 
2083  Manager::FunctionPtr qOutput(const std::vector<std::string>& arguments)
2084  {
2085  if (arguments.size() != 1)
2086  B2FATAL("Wrong number of arguments for meta function qOutput");
2087 
2088  std::string combinerMethod = arguments[0];
2089  auto func = [combinerMethod](const Particle * particle) -> double {
2090 
2091  double output = realNaN;
2092  auto* flavorTaggerInfo = particle->getRelatedTo<FlavorTaggerInfo>();
2093 
2094  if (flavorTaggerInfo && flavorTaggerInfo->getUseModeFlavorTagger() == "Expert")
2095  output = TMath::Sign(1, flavorTaggerInfo->getMethodMap(combinerMethod)->getQrCombined());
2096 
2097  return output;
2098  };
2099  return func;
2100  }
2101 
2102  Manager::FunctionPtr rBinBelle(const std::vector<std::string>& arguments)
2103  {
2104  if (arguments.size() != 1)
2105  B2FATAL("Wrong number of arguments for meta function rBinBelle");
2106 
2107 
2108  std::string combinerMethod = arguments[0];
2109  auto func = [combinerMethod](const Particle * particle) -> int {
2110 
2111  int output = 0;
2112  auto* flavorTaggerInfo = particle->getRelatedTo<FlavorTaggerInfo>();
2113 
2114  if (flavorTaggerInfo && flavorTaggerInfo->getUseModeFlavorTagger() == "Expert")
2115  {
2116  double r = std::abs(flavorTaggerInfo->getMethodMap(combinerMethod)->getQrCombined());
2117  if (r < 0.1) output = 0;
2118  if (r > 0.1 && r < 0.25) output = 1;
2119  if (r > 0.25 && r < 0.5) output = 2;
2120  if (r > 0.5 && r < 0.625) output = 3;
2121  if (r > 0.625 && r < 0.75) output = 4;
2122  if (r > 0.75 && r < 0.875) output = 5;
2123  if (r > 0.875 && r < 1.10) output = 6;
2124  }
2125 
2126  return output;
2127  };
2128  return func;
2129  }
2130 
2131  Manager::FunctionPtr qpCategory(const std::vector<std::string>& arguments)
2132  {
2133  if (arguments.size() != 1)
2134  B2FATAL("Wrong number of arguments for meta function qpCategory");
2135 
2136  std::string categoryName = arguments[0];
2137  auto func = [categoryName](const Particle * particle) -> double {
2138 
2139  double output = realNaN;
2140  auto* flavorTaggerInfo = particle->getRelatedTo<FlavorTaggerInfo>();
2141 
2142  if (flavorTaggerInfo && flavorTaggerInfo->getUseModeFlavorTagger() == "Expert")
2143  {
2144  std::map<std::string, float> iQpCategories = flavorTaggerInfo->getMethodMap("FBDT")->getQpCategory();
2145  if (iQpCategories.find(categoryName) != iQpCategories.end()) output = iQpCategories.at(categoryName);
2146  else if (iQpCategories.size() != 0) B2FATAL("qpCategory: Category with name " << categoryName
2147  << " not found. Check the official category names or if this category is included in the flavor tagger categories list.");
2148  }
2149  return output;
2150  };
2151  return func;
2152  }
2153 
2154  Manager::FunctionPtr isTrueFTCategory(const std::vector<std::string>& arguments)
2155  {
2156  if (arguments.size() != 1)
2157  B2FATAL("Wrong number of arguments for meta function isTrueFTCategory");
2158 
2159  std::string categoryName = arguments[0];
2160  auto func = [categoryName](const Particle * particle) -> double {
2161 
2162  double output = realNaN;
2163  auto* flavorTaggerInfo = particle->getRelatedTo<FlavorTaggerInfo>();
2164 
2165  if (flavorTaggerInfo && flavorTaggerInfo->getUseModeFlavorTagger() == "Expert")
2166  {
2167  std::map<std::string, float> iIsTrueCategories = flavorTaggerInfo->getMethodMap("FBDT")->getIsTrueCategory();
2168  if (iIsTrueCategories.find(categoryName) != iIsTrueCategories.end()) output = iIsTrueCategories.at(categoryName);
2169  else if (iIsTrueCategories.size() != 0) B2FATAL("isTrueFTCategory: Category with name " << categoryName
2170  << " not found. Check the official category names or if this category is included in the flavor tagger categories list.");
2171  }
2172 
2173  return output;
2174  };
2175  return func;
2176  }
2177 
2178  Manager::FunctionPtr hasTrueTargets(const std::vector<std::string>& arguments)
2179  {
2180  if (arguments.size() != 1)
2181  B2FATAL("Wrong number of arguments for meta function hasTrueTargets");
2182 
2183  std::string categoryName = arguments[0];
2184  auto func = [categoryName](const Particle * particle) -> double {
2185 
2186  double output = realNaN;
2187  auto* flavorTaggerInfo = particle->getRelatedTo<FlavorTaggerInfo>();
2188 
2189  if (flavorTaggerInfo && flavorTaggerInfo->getUseModeFlavorTagger() == "Expert")
2190  {
2191  std::map<std::string, float> iHasTrueTargets = flavorTaggerInfo->getMethodMap("FBDT")->getHasTrueTarget();
2192  if (iHasTrueTargets.find(categoryName) != iHasTrueTargets.end()) output = iHasTrueTargets.at(categoryName);
2193  else if (iHasTrueTargets.size() != 0) B2FATAL("hasTrueTargets: Category with name " << categoryName
2194  << " not found. Check the official category names or if this category is included in the flavor tagger categories list.");
2195  }
2196 
2197  return output;
2198  };
2199  return func;
2200  }
2201 
2202  VARIABLE_GROUP("Flavor Tagger Expert Variables");
2203 
2204  REGISTER_VARIABLE("pMissTag", momentumMissingTagSide, R"DOC(
2205 [Expert] Calculates the missing momentum for a given particle on the tag side.
2206 :noindex:
2207 )DOC","GeV/c");
2208  REGISTER_METAVARIABLE("pMissTag(maskName)", momentumMissingTagSideWithMask,
2209  "[Expert] Calculates the missing momentum for a given particle on the tag side. The unit of the missing momentum is ``GeV/c`` ",
2210  Manager::VariableDataType::c_double);
2211  REGISTER_VARIABLE("cosTPTO" , cosTPTO, R"DOC(
2212 [Expert] Returns cosine of angle between thrust axis of given particle and thrust axis of ROE.
2213 :noindex:
2214 )DOC");
2215  REGISTER_METAVARIABLE("cosTPTO(maskName)", cosTPTOWithMask,
2216  "[Expert] Returns cosine of angle between thrust axis of given particle and thrust axis of ROE.",
2217  Manager::VariableDataType::c_double);
2218  REGISTER_VARIABLE("lambdaFlavor", lambdaFlavor,
2219  "[Expert] Returns 1.0 if particle is ``Lambda0``, -1.0 in case of ``anti-Lambda0``, 0.0 otherwise.");
2220  REGISTER_VARIABLE("isLambda", isLambda, "[Expert] Returns 1.0 if particle is truth-matched to ``Lambda0``, 0.0 otherwise.");
2221  REGISTER_VARIABLE("lambdaZError", lambdaZError, "[Expert] Returns the variance of the z-component of the decay vertex.",":math:`\\text{cm}^2`");
2222  REGISTER_VARIABLE("momentumOfSecondDaughter", momentumOfSecondDaughter,
2223  "[Expert] Returns the momentum of second daughter if exists, 0. otherwise.","GeV/c");
2224  REGISTER_VARIABLE("momentumOfSecondDaughterCMS", momentumOfSecondDaughterCMS,
2225  "[Expert] Returns the momentum of the second daughter in the centre-of-mass system, 0. if this daughter doesn't exist.","GeV/c");
2226  REGISTER_VARIABLE("chargeTimesKaonLiklihood", chargeTimesKaonLiklihood,
2227  "[Expert] Returns ``q*(highest PID_Likelihood for Kaons)``, 0. otherwise.");
2228  REGISTER_VARIABLE("ptTracksRoe", transverseMomentumOfChargeTracksInRoe, R"DOC(
2229 [Expert] Returns the transverse momentum of all charged tracks of the ROE related to the given particle, 0.0 if particle has no related ROE.
2230 :noindex:
2231 )DOC","GeV/c");
2232  REGISTER_METAVARIABLE("ptTracksRoe(maskName)", transverseMomentumOfChargeTracksInRoeWithMask,
2233  "[Expert] Returns the transverse momentum of all charged tracks of the ROE related to the given particle, 0.0 if particle has no related ROE. The unit of the momentum is ``GeV/c`` ",
2234  Manager::VariableDataType::c_double);
2235  REGISTER_METAVARIABLE("pt2TracksRoe(maskName)", transverseMomentumSquaredOfChargeTracksInRoeWithMask,
2236  "[Expert] Returns the transverse momentum squared of all charged tracks of the ROE related to the given particle, 0.0 if particle has no related ROE. The unit of the momentum squared is :math:`[\\text{GeV}/\\text{c}]^2` ",
2237  Manager::VariableDataType::c_double);
2238  REGISTER_VARIABLE("NumberOfKShortsInRoe", NumberOfKShortsInRoe,
2239  "[Expert] Returns the number of ``K_S0`` in the rest of event. The particle list ``K_S0:inRoe`` has to be filled beforehand.");
2240 
2241  REGISTER_VARIABLE("isInElectronOrMuonCat", isInElectronOrMuonCat,
2242  "[Expert] Returns 1.0 if the particle has been selected as target in the Muon or Electron Category, 0.0 otherwise.");
2243 
2244  REGISTER_VARIABLE("isMajorityInRestOfEventFromB0", isMajorityInRestOfEventFromB0, R"DOC(
2245 [Eventbased][Expert] Checks if the majority of the tracks in the current RestOfEvent are from a ``B0``.
2246 :noindex:
2247 )DOC");
2248  REGISTER_METAVARIABLE("isMajorityInRestOfEventFromB0(maskName)", isMajorityInRestOfEventFromB0WithMask,
2249  "[Eventbased][Expert] Checks if the majority of the tracks in the current RestOfEvent are from a ``B0``.",
2250  Manager::VariableDataType::c_bool);
2251  REGISTER_VARIABLE("isMajorityInRestOfEventFromB0bar", isMajorityInRestOfEventFromB0bar, R"DOC(
2252 [Eventbased][Expert] Check if the majority of the tracks in the current RestOfEvent are from a ``anti-B0``.
2253 :noindex:
2254 )DOC");
2255  REGISTER_METAVARIABLE("isMajorityInRestOfEventFromB0bar(maskName)", isMajorityInRestOfEventFromB0barWithMask,
2256  "[Eventbased][Expert] Check if the majority of the tracks in the current RestOfEvent are from a ``anti-B0``.",
2257  Manager::VariableDataType::c_bool);
2258  REGISTER_VARIABLE("hasRestOfEventTracks", hasRestOfEventTracks, R"DOC(
2259 [Expert] Returns the number of tracks in the RestOfEvent related to the given Particle. -2 if the RestOfEvent is empty.
2260 :noindex:
2261 )DOC");
2262  REGISTER_METAVARIABLE("hasRestOfEventTracks(maskName)", hasRestOfEventTracksWithMask,
2263  "[Expert] Returns the number of tracks in the RestOfEvent related to the given Particle. -2 if the RestOfEvent is empty.",
2264  Manager::VariableDataType::c_bool);
2265 
2266  REGISTER_VARIABLE("qrCombined", isRestOfEventB0Flavor, R"DOC(
2267 [Eventbased][Expert] Returns -1 (1) if current RestOfEvent is related to a ``anti-B0`` (``B0``).
2268 The ``MCError`` bit of Breco has to be 0, 1, 2, 16 or 1024.
2269 The output of the variable is 0 otherwise.
2270 If one particle in the RestOfEvent is found to belong to the reconstructed ``B0``, the output is -2(2) for a ``anti-B0`` (``B0``) on the reconstructed side.
2271 )DOC");
2272  REGISTER_VARIABLE("ancestorHasWhichFlavor", ancestorHasWhichFlavor,
2273  "[Expert] Checks the decay chain of the given particle upwards up to the ``Upsilon(4S)`` resonance and outputs 0 (1) if an ancestor is found to be a ``anti-B0`` (``B0``), if not -2.");
2274  REGISTER_VARIABLE("B0mcErrors", B0mcErrors, "[Expert] Returns MC-matching flag, see :b2:var:`mcErrors` for the particle, e.g. ``B0`` .");
2275  REGISTER_VARIABLE("isRelatedRestOfEventMajorityB0Flavor", isRelatedRestOfEventMajorityB0Flavor, R"DOC(
2276 [Expert] Returns 0 (1) if the majority of tracks and clusters of the RestOfEvent related to the given Particle are related to a ``anti-B0`` (``B0``).
2277 :noindex:
2278 )DOC");
2279  REGISTER_METAVARIABLE("isRelatedRestOfEventMajorityB0Flavor(maskName)", isRelatedRestOfEventMajorityB0FlavorWithMask,
2280  "[Expert] Returns 0 (1) if the majority of tracks and clusters of the RestOfEvent related to the given Particle are related to a ``anti-B0`` (``B0``).",
2281  Manager::VariableDataType::c_int);
2282  REGISTER_VARIABLE("isRestOfEventMajorityB0Flavor", isRestOfEventMajorityB0Flavor,
2283  "[Expert] Returns 0 (1) if the majority of tracks and clusters of the current RestOfEvent are related to a ``anti-B0`` (``B0``).");
2284  REGISTER_VARIABLE("mcFlavorOfOtherB", mcFlavorOfOtherB, R"DOC(
2285 [Expert] Returns the MC flavor (+1 or -1) of the accompanying tag-side B meson if the given particle is a correctly truth-matched B candidate, 0 otherwise.
2286 In other words, this variable checks the generated flavor of the other generated ``Upsilon(4S)`` daughter.
2287 )DOC");
2288 
2289 
2290  REGISTER_METAVARIABLE("BtagToWBosonVariables(requestedVariable[, maskName])", BtagToWBosonVariables, R"DOC(
2291 [Eventbased][Expert] Returns values of FlavorTagging-specific kinematical variables assuming a semileptonic decay with the given particle as target.
2292 The input values of ``requestedVariable`` can be the following: recoilMass in GeV/c^2 , pMissCMS in ``GeV/c``, cosThetaMissCMS and EW90.
2293 )DOC", Manager::VariableDataType::c_double);
2294  REGISTER_METAVARIABLE("KaonPionVariables(requestedVariable)" , KaonPionVariables , R"DOC(
2295 [Expert] Returns values of FlavorTagging-specific kinematical variables for ``KaonPion`` category.
2296 The input values of ``requestedVariable`` can be the following: cosKaonPion, HaveOpositeCharges.
2297 )DOC", Manager::VariableDataType::c_double);
2298  REGISTER_METAVARIABLE("FSCVariables(requestedVariable)", FSCVariables, R"DOC(
2299 [Eventbased][Expert] Returns values of FlavorTagging-specific kinematical variables for ``FastSlowCorrelated`` category.
2300 The input values of ``requestedVariable`` can be the following: pFastCMS in ``GeV/c``, cosSlowFast, SlowFastHaveOpositeCharges, or cosTPTOFast.
2301 )DOC", Manager::VariableDataType::c_double);
2302  REGISTER_METAVARIABLE("hasHighestProbInCat(particleListName, extraInfoName)", hasHighestProbInCat, R"DOC(
2303 [Expert] Returns 1.0 if the given Particle is classified as target track, i.e. if it has the highest target track probability in particleListName.
2304 The probability is accessed via ``extraInfoName``, which can have the following input values:
2305 
2306 * isRightTrack(Electron),
2307 * isRightTrack(IntermediateElectron),
2308 * isRightTrack(Muon),
2309 * isRightTrack(IntermediateMuon),
2310 * isRightTrack(KinLepton),
2311 * isRightTrack(IntermediateKinLepton),
2312 * isRightTrack(Kaon),
2313 * isRightTrack(SlowPion),
2314 * isRightTrack(FastHadron),
2315 * isRightTrack(MaximumPstar),
2316 * isRightTrack(Lambda),
2317 * isRightCategory(Electron),
2318 * isRightCategory(IntermediateElectron),
2319 * isRightCategory(Muon),
2320 * isRightCategory(IntermediateMuon),
2321 * isRightCategory(KinLepton),
2322 * isRightCategory(IntermediateKinLepton),
2323 * isRightCategory(Kaon),
2324 * isRightCategory(SlowPion),
2325 * isRightCategory(FastHadron),
2326 * isRightCategory(MaximumPstar),
2327 * isRightCategory(Lambda),
2328 * isRightCategory(KaonPion),
2329 * isRightCategory(FSC).
2330 
2331 )DOC", Manager::VariableDataType::c_bool);
2332  REGISTER_METAVARIABLE("HighestProbInCat(particleListName, extraInfoName)", HighestProbInCat,
2333  "[Expert] Returns the highest target track probability value for the given category, for allowed input values for ``extraInfoName`` see :b2:var:`hasHighestProbInCat`.", Manager::VariableDataType::c_double);
2334 
2335  REGISTER_METAVARIABLE("isRightTrack(particleName)", isRightTrack, R"DOC(
2336 [Expert] Returns 1.0 if the given particle was really from a B-meson depending on category provided in ``particleName`` argument, 0.0 otherwise.
2337 Allowed input values for ``particleName`` argument in this variable are the following:
2338 
2339 * Electron,
2340 * IntermediateElectron,
2341 * Muon,
2342 * IntermediateMuon,
2343 * KinLepton,
2344 * IntermediateKinLepton,
2345 * Kaon,
2346 * SlowPion,
2347 * FastHadron,
2348 * Lambda,
2349 * mcAssociated.
2350 
2351 )DOC", Manager::VariableDataType::c_int);
2352  REGISTER_METAVARIABLE("isRightCategory(particleName)", isRightCategory, R"DOC(
2353 [Expert] Returns 1.0 if the class track by ``particleName`` category has the same flavor as the MC target track, 0.0 otherwise.
2354 Allowed input values for ``particleName`` argument in this variable are the following:
2355 
2356 * Electron,
2357 * IntermediateElectron,
2358 * Muon,
2359 * IntermediateMuon,
2360 * KinLepton,
2361 * IntermediateKinLepton
2362 * Kaon,
2363 * SlowPion,
2364 * FastHadron,
2365 * KaonPion,
2366 * MaximumPstar,
2367 * FSC,
2368 * Lambda,
2369 * mcAssociated.
2370 
2371 )DOC", Manager::VariableDataType::c_int);
2372  REGISTER_METAVARIABLE("QpOf(particleListName, outputExtraInfo, rankingExtraInfo)", QpOf, R"DOC(
2373 [Eventbased][Expert] Returns the :math:`q*p` value for a given particle list provided as the 1st argument,
2374 where :math:`p` is the probability of a category stored as extraInfo, provided as the 2nd argument,
2375 allowed values are same as in :b2:var:`hasHighestProbInCat`.
2376 The particle is selected after ranking according to a flavor tagging extraInfo, provided as the 3rd argument,
2377 allowed values are same as in :b2:var:`hasHighestProbInCat`.
2378 )DOC", Manager::VariableDataType::c_double);
2379  REGISTER_METAVARIABLE("weightedQpOf(particleListName, outputExtraInfo, rankingExtraInfo)", weightedQpOf, R"DOC(
2380 [Eventbased][Expert] Returns the weighted :math:`q*p` value for a given particle list, provided as the 1st argument,
2381 where :math:`p` is the probability of a category stored as extraInfo, provided in the 2nd argument,
2382 allowed values are same as in :b2:var:`hasHighestProbInCat`.
2383 The particles in the list are ranked according to a flavor tagging extraInfo, provided as the 3rd argument,
2384 allowed values are same as in :b2:var:`hasHighestProbInCat`.
2385 The values for the three top particles is combined into an effective (weighted) output.
2386 )DOC", Manager::VariableDataType::c_double);
2387  REGISTER_METAVARIABLE("variableOfTarget(particleListName, inputVariable, rankingExtraInfo)", variableOfTarget, R"DOC(
2388 [Eventbased][Expert] Returns the value of an input variable provided as the 2nd argument for a particle selected from the given list provided as the 1st argument.
2389 The particles are ranked according to a flavor tagging extraInfo, provided as the 2nd argument,
2390 allowed values are same as in :b2:var:`hasHighestProbInCat`.
2391 )DOC", Manager::VariableDataType::c_double);
2392 
2393  REGISTER_METAVARIABLE("hasTrueTarget(categoryName)", hasTrueTarget,
2394  "[Expert] Returns 1 if the given category has a target, 0 otherwise.", Manager::VariableDataType::c_double);
2395  REGISTER_METAVARIABLE("isTrueCategory(categoryName)", isTrueCategory,
2396  "[Expert] Returns 1 if the given category tags the B0 MC flavor correctly, 0 otherwise.", Manager::VariableDataType::c_double);
2397 
2398  REGISTER_METAVARIABLE("qpCategory(categoryName)", qpCategory, R"DOC(
2399 [Expert] Returns the output :math:`q` (charge of target track) times :math:`p` (probability that this is the right category) of the category with the given name.
2400 The allowed categories are the official Flavor Tagger Category Names.
2401 )DOC", Manager::VariableDataType::c_double);
2402  REGISTER_METAVARIABLE("isTrueFTCategory(categoryName)", isTrueFTCategory, R"DOC(
2403 [Expert] Returns 1 if the target particle (checking the decay chain) of the category with the given name is found in the MC particles,
2404 and if it provides the right flavor. The allowed categories are the official Flavor Tagger Category Names.
2405 )DOC", Manager::VariableDataType::c_double);
2406  REGISTER_METAVARIABLE("hasTrueTargets(categoryName)", hasTrueTargets, R"DOC(
2407 [Expert] Returns 1 if target particles (checking only the decay chain) of the category with the given name is found in the MC particles.
2408 The allowed categories are the official Flavor Tagger Category Names.
2409 )DOC", Manager::VariableDataType::c_double);
2410 
2411  VARIABLE_GROUP("Flavor Tagger Analysis Variables")
2412 
2413  REGISTER_METAVARIABLE("rBinBelle(combinerMethod)", rBinBelle, R"DOC(
2414 Returns the corresponding :math:`r` (dilution) bin according to the Belle binning for the given ``combinerMethod``.
2415 The available methods are 'FBDT' and 'FANN' (category-based combiners), and 'DNN' (DNN tagger output).
2416 The return values and the corresponding dilution ranges are the following:
2417 
2418 * 0: :math:`0.000 < r < 0.100`;
2419 * 1: :math:`0.100 < r < 0.250`;
2420 * 2: :math:`0.250 < r < 0.500`;
2421 * 3: :math:`0.500 < r < 0.625`;
2422 * 4: :math:`0.625 < r < 0.750`;
2423 * 5: :math:`0.750 < r < 0.875`;
2424 * 6: :math:`0.875 < r < 1.000`.
2425 
2426 .. warning:: You have to run the Flavor Tagger for this variable to be meaningful.
2427 .. seealso:: :ref:`FlavorTagger` and :func:`flavorTagger.flavorTagger`.
2428 )DOC", Manager::VariableDataType::c_int);
2429  REGISTER_METAVARIABLE("qrOutput(combinerMethod)", qrOutput, R"DOC(
2430 Returns the output of the flavorTagger, flavor tag :math:`q` times the dilution factor :math:`r`, for the given combiner method.
2431 The available methods are 'FBDT' and 'FANN' (category-based combiners), and 'DNN' (DNN tagger output).
2432 
2433 .. warning:: You have to run the Flavor Tagger for this variable to be meaningful.
2434 .. seealso:: :ref:`FlavorTagger` and :func:`flavorTagger.flavorTagger`.
2435 )DOC", Manager::VariableDataType::c_double);
2436  REGISTER_METAVARIABLE("qOutput(combinerMethod)", qOutput, R"DOC(
2437 Returns the flavor tag :math:`q` output of the flavorTagger for the given combinerMethod.
2438 The available methods are 'FBDT' and 'FANN' (category-based combiners), and 'DNN' (DNN tagger output).
2439 
2440 .. warning:: You have to run the Flavor Tagger for this variable to be meaningful.
2441 .. seealso:: :ref:`FlavorTagger` and :func:`flavorTagger.flavorTagger`.
2442 )DOC", Manager::VariableDataType::c_double);
2443  REGISTER_VARIABLE("isRelatedRestOfEventB0Flavor", isRelatedRestOfEventB0Flavor, R"DOC(
2444 Returns -1 (1) if the RestOfEvent related to the given particle is related to a ``anti-B0`` (``B0``).
2445 The ``MCError`` bit of Breco has to be 0, 1, 2, 16 or 1024.
2446 The output of the variable is 0 otherwise.
2447 If one particle in the RestOfEvent is found to belong to the reconstructed ``B0``, the output is -2(2) for a ``anti-B0`` (``B0``) on the reconstructed side.
2448 )DOC");
2449  }
2451 }
const MCParticle * getMCParticle() const
Returns the pointer to the MCParticle object that was used to create this Particle (ParticleType == c...
Definition: Particle.cc:934
T * getRelated(const std::string &name="", const std::string &namedRelation="") const
Get the object to or from which this object has a relation.
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:502
MCParticle * getMother() const
Returns a pointer to the mother particle.
Definition: MCParticle.h:600
std::string particleName(int pdgCode)
Returns the name of a particle with given pdg code.
Definition: EvtPDLUtil.cc:20
Abstract base class for different kinds of events.
Definition: ClusterUtils.h:23