Belle II Software  light-2212-foldex
Variables.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/Variables.h>
11 
12 // include VariableManager
13 #include <analysis/VariableManager/Manager.h>
14 
15 #include <analysis/utility/PCmsLabTransform.h>
16 #include <analysis/utility/ReferenceFrame.h>
17 #include <analysis/utility/MCMatching.h>
18 #include <analysis/ClusterUtility/ClusterUtils.h>
19 
20 // framework - DataStore
21 #include <framework/datastore/StoreArray.h>
22 #include <framework/datastore/StoreObjPtr.h>
23 
24 // dataobjects
25 #include <analysis/dataobjects/Particle.h>
26 #include <analysis/dataobjects/ParticleList.h>
27 #include <framework/dataobjects/EventExtraInfo.h>
28 #include <analysis/dataobjects/EventShapeContainer.h>
29 
30 #include <mdst/dataobjects/MCParticle.h>
31 #include <mdst/dataobjects/Track.h>
32 #include <mdst/dataobjects/ECLCluster.h>
33 #include <mdst/dataobjects/V0.h>
34 
35 #include <mdst/dbobjects/BeamSpot.h>
36 
37 // framework aux
38 #include <framework/logging/Logger.h>
39 #include <framework/geometry/B2Vector3.h>
40 #include <framework/geometry/BFieldManager.h>
41 #include <framework/gearbox/Const.h>
42 #include <framework/utilities/Conversion.h>
43 
44 #include <Math/Vector4D.h>
45 #include <TRandom.h>
46 #include <TVectorF.h>
47 
48 #include <iostream>
49 #include <cmath>
50 
51 
52 using namespace std;
53 
54 namespace Belle2 {
59  namespace Variable {
60 
61 // momentum -------------------------------------------
62 
63  double particleP(const Particle* part)
64  {
65  const auto& frame = ReferenceFrame::GetCurrent();
66  return frame.getMomentum(part).P();
67  }
68 
69  double particleE(const Particle* part)
70  {
71  const auto& frame = ReferenceFrame::GetCurrent();
72  return frame.getMomentum(part).E();
73  }
74 
75  double particleClusterEUncertainty(const Particle* part)
76  {
77  const ECLCluster* cluster = part->getECLCluster();
78  if (cluster) {
79  const auto EPhiThetaCov = cluster->getCovarianceMatrix3x3();
80  return std::sqrt(EPhiThetaCov[0][0]);
81  }
82  return std::numeric_limits<double>::quiet_NaN();
83  }
84 
85  double particlePx(const Particle* part)
86  {
87  const auto& frame = ReferenceFrame::GetCurrent();
88  return frame.getMomentum(part).Px();
89  }
90 
91  double particlePy(const Particle* part)
92  {
93  const auto& frame = ReferenceFrame::GetCurrent();
94  return frame.getMomentum(part).Py();
95  }
96 
97  double particlePz(const Particle* part)
98  {
99  const auto& frame = ReferenceFrame::GetCurrent();
100  return frame.getMomentum(part).Pz();
101  }
102 
103  double particlePt(const Particle* part)
104  {
105  const auto& frame = ReferenceFrame::GetCurrent();
106  return frame.getMomentum(part).Pt();
107  }
108 
109  double covMatrixElement(const Particle* part, const std::vector<double>& element)
110  {
111  int elementI = int(std::lround(element[0]));
112  int elementJ = int(std::lround(element[1]));
113 
114  if (elementI < 0 || elementI > 6) {
115  B2WARNING("Requested particle's momentumVertex covariance matrix element is out of boundaries [0 - 6]:" << LogVar("i", elementI));
116  return std::numeric_limits<double>::quiet_NaN();
117  }
118  if (elementJ < 0 || elementJ > 6) {
119  B2WARNING("Requested particle's momentumVertex covariance matrix element is out of boundaries [0 - 6]:" << LogVar("j", elementJ));
120  return std::numeric_limits<double>::quiet_NaN();
121  }
122 
123  return part->getMomentumVertexErrorMatrix()(elementI, elementJ);
124  }
125 
126  double particleEUncertainty(const Particle* part)
127  {
128  const auto& frame = ReferenceFrame::GetCurrent();
129 
130  double errorSquared = frame.getMomentumErrorMatrix(part)(3, 3);
131 
132  if (errorSquared >= 0.0)
133  return sqrt(errorSquared);
134  else
135  return std::numeric_limits<double>::quiet_NaN();
136  }
137 
138  double particlePErr(const Particle* part)
139  {
140  TMatrixD jacobianRot(3, 3);
141  jacobianRot.Zero();
142 
143  double cosPhi = cos(particlePhi(part));
144  double sinPhi = sin(particlePhi(part));
145  double cosTheta = particleCosTheta(part);
146  double sinTheta = sin(acos(cosTheta));
147  double p = particleP(part);
148 
149  jacobianRot(0, 0) = sinTheta * cosPhi;
150  jacobianRot(0, 1) = sinTheta * sinPhi;
151  jacobianRot(1, 0) = cosTheta * cosPhi / p;
152  jacobianRot(1, 1) = cosTheta * sinPhi / p;
153  jacobianRot(0, 2) = cosTheta;
154  jacobianRot(2, 0) = -sinPhi / sinTheta / p;
155  jacobianRot(1, 2) = -sinTheta / p;
156  jacobianRot(2, 1) = cosPhi / sinTheta / p;
157 
158  const auto& frame = ReferenceFrame::GetCurrent();
159 
160  double errorSquared = frame.getMomentumErrorMatrix(part).GetSub(0, 2, 0, 2, " ").Similarity(jacobianRot)(0, 0);
161 
162  if (errorSquared >= 0.0)
163  return sqrt(errorSquared);
164  else
165  return std::numeric_limits<double>::quiet_NaN();
166  }
167 
168  double particlePxErr(const Particle* part)
169  {
170  const auto& frame = ReferenceFrame::GetCurrent();
171 
172  double errorSquared = frame.getMomentumErrorMatrix(part)(0, 0);
173 
174  if (errorSquared >= 0.0)
175  return sqrt(errorSquared);
176  else
177  return std::numeric_limits<double>::quiet_NaN();
178  }
179 
180  double particlePyErr(const Particle* part)
181  {
182  const auto& frame = ReferenceFrame::GetCurrent();
183  double errorSquared = frame.getMomentumErrorMatrix(part)(1, 1);
184 
185  if (errorSquared >= 0.0)
186  return sqrt(errorSquared);
187  else
188  return std::numeric_limits<double>::quiet_NaN();
189  }
190 
191  double particlePzErr(const Particle* part)
192  {
193  const auto& frame = ReferenceFrame::GetCurrent();
194  double errorSquared = frame.getMomentumErrorMatrix(part)(2, 2);
195 
196  if (errorSquared >= 0.0)
197  return sqrt(errorSquared);
198  else
199  return std::numeric_limits<double>::quiet_NaN();
200  }
201 
202  double particlePtErr(const Particle* part)
203  {
204  TMatrixD jacobianRot(3, 3);
205  jacobianRot.Zero();
206 
207  double px = particlePx(part);
208  double py = particlePy(part);
209  double pt = particlePt(part);
210 
211  jacobianRot(0, 0) = px / pt;
212  jacobianRot(0, 1) = py / pt;
213  jacobianRot(1, 0) = -py / (pt * pt);
214  jacobianRot(1, 1) = px / (pt * pt);
215  jacobianRot(2, 2) = 1;
216 
217  const auto& frame = ReferenceFrame::GetCurrent();
218  double errorSquared = frame.getMomentumErrorMatrix(part).GetSub(0, 2, 0, 2, " ").Similarity(jacobianRot)(0, 0);
219 
220  if (errorSquared >= 0.0)
221  return sqrt(errorSquared);
222  else
223  return std::numeric_limits<double>::quiet_NaN();
224  }
225 
226  double momentumDeviationChi2(const Particle* part)
227  {
228  double result = std::numeric_limits<double>::quiet_NaN();
229 
230  // check if error matrix is set
231  if (part->getPValue() < 0.0)
232  return result;
233 
234  // check if mc match exists
235  const MCParticle* mcp = part->getMCParticle();
236  if (mcp == nullptr)
237  return result;
238 
239  result = 0.0;
240  result += TMath::Power(part->getPx() - mcp->getMomentum().X(), 2.0) / part->getMomentumVertexErrorMatrix()(0, 0);
241  result += TMath::Power(part->getPy() - mcp->getMomentum().Y(), 2.0) / part->getMomentumVertexErrorMatrix()(1, 1);
242  result += TMath::Power(part->getPz() - mcp->getMomentum().Z(), 2.0) / part->getMomentumVertexErrorMatrix()(2, 2);
243 
244  return result;
245  }
246 
247  double particleTheta(const Particle* part)
248  {
249  const auto& frame = ReferenceFrame::GetCurrent();
250  return frame.getMomentum(part).Theta();
251  }
252 
253  double particleThetaErr(const Particle* part)
254  {
255  TMatrixD jacobianRot(3, 3);
256  jacobianRot.Zero();
257 
258  double cosPhi = cos(particlePhi(part));
259  double sinPhi = sin(particlePhi(part));
260  double cosTheta = particleCosTheta(part);
261  double sinTheta = sin(acos(cosTheta));
262  double p = particleP(part);
263 
264  jacobianRot(0, 0) = sinTheta * cosPhi;
265  jacobianRot(0, 1) = sinTheta * sinPhi;
266  jacobianRot(1, 0) = cosTheta * cosPhi / p;
267  jacobianRot(1, 1) = cosTheta * sinPhi / p;
268  jacobianRot(0, 2) = cosTheta;
269  jacobianRot(2, 0) = -sinPhi / sinTheta / p;
270  jacobianRot(1, 2) = -sinTheta / p;
271  jacobianRot(2, 1) = cosPhi / sinTheta / p;
272 
273  const auto& frame = ReferenceFrame::GetCurrent();
274  double errorSquared = frame.getMomentumErrorMatrix(part).GetSub(0, 2, 0, 2, " ").Similarity(jacobianRot)(1, 1);
275 
276  if (errorSquared >= 0.0)
277  return sqrt(errorSquared);
278  else
279  return std::numeric_limits<double>::quiet_NaN();
280  }
281 
282  double particleCosTheta(const Particle* part)
283  {
284  const auto& frame = ReferenceFrame::GetCurrent();
285  return cos(frame.getMomentum(part).Theta());
286  }
287 
288  double particleCosThetaErr(const Particle* part)
289  {
290  return fabs(particleThetaErr(part) * sin(particleTheta(part)));
291  }
292 
293  double particlePhi(const Particle* part)
294  {
295  const auto& frame = ReferenceFrame::GetCurrent();
296  return frame.getMomentum(part).Phi();
297  }
298 
299  double particlePhiErr(const Particle* part)
300  {
301  TMatrixD jacobianRot(3, 3);
302  jacobianRot.Zero();
303 
304  double px = particlePx(part);
305  double py = particlePy(part);
306  double pt = particlePt(part);
307 
308  jacobianRot(0, 0) = px / pt;
309  jacobianRot(0, 1) = py / pt;
310  jacobianRot(1, 0) = -py / (pt * pt);
311  jacobianRot(1, 1) = px / (pt * pt);
312  jacobianRot(2, 2) = 1;
313 
314  const auto& frame = ReferenceFrame::GetCurrent();
315  double errorSquared = frame.getMomentumErrorMatrix(part).GetSub(0, 2, 0, 2, " ").Similarity(jacobianRot)(1, 1);
316 
317  if (errorSquared >= 0.0)
318  return sqrt(errorSquared);
319  else
320  return std::numeric_limits<double>::quiet_NaN();
321  }
322 
323  double particleXp(const Particle* part)
324  {
325  PCmsLabTransform T;
326  ROOT::Math::PxPyPzEVector p4 = part -> get4Vector();
327  ROOT::Math::PxPyPzEVector p4CMS = T.rotateLabToCms() * p4;
328  float s = T.getCMSEnergy();
329  float M = part->getMass();
330  return p4CMS.P() / TMath::Sqrt(s * s / 4 - M * M);
331  }
332 
333  int particlePDGCode(const Particle* part)
334  {
335  return part->getPDGCode();
336  }
337 
338  double cosAngleBetweenMomentumAndVertexVectorInXYPlane(const Particle* part)
339  {
340  static DBObjPtr<BeamSpot> beamSpotDB;
341  double px = part->getPx();
342  double py = part->getPy();
343 
344  double xV = part->getX();
345  double yV = part->getY();
346 
347  double xIP = (beamSpotDB->getIPPosition()).X();
348  double yIP = (beamSpotDB->getIPPosition()).Y();
349 
350  double x = xV - xIP;
351  double y = yV - yIP;
352 
353  double cosangle = (px * x + py * y) / (sqrt(px * px + py * py) * sqrt(x * x + y * y));
354  return cosangle;
355  }
356 
357  double cosAngleBetweenMomentumAndVertexVector(const Particle* part)
358  {
359  static DBObjPtr<BeamSpot> beamSpotDB;
360  return cos((B2Vector3D(part->getVertex()) - beamSpotDB->getIPPosition()).Angle(B2Vector3D(part->getMomentum())));
361  }
362 
363  double cosThetaBetweenParticleAndNominalB(const Particle* part)
364  {
365 
366  int particlePDG = abs(part->getPDGCode());
367  if (particlePDG != 511 and particlePDG != 521)
368  B2FATAL("The Variables cosThetaBetweenParticleAndNominalB is only meant to be used on B mesons!");
369 
370  PCmsLabTransform T;
371  double e_Beam = T.getCMSEnergy() / 2.0; // GeV
372  double m_B = part->getPDGMass();
373  // if this is a continuum run, use an approximate Y(4S) CMS energy
374  if (e_Beam * e_Beam - m_B * m_B < 0) {
375  e_Beam = 1.0579400E+1 / 2.0; // GeV
376  }
377  double p_B = std::sqrt(e_Beam * e_Beam - m_B * m_B);
378 
379  ROOT::Math::PxPyPzEVector p = T.rotateLabToCms() * part->get4Vector();
380  double e_d = p.E();
381  double m_d = p.M();
382  double p_d = p.P();
383 
384  double theta_BY = (2 * e_Beam * e_d - m_B * m_B - m_d * m_d)
385  / (2 * p_B * p_d);
386  return theta_BY;
387  }
388 
389  double cosToThrustOfEvent(const Particle* part)
390  {
391  StoreObjPtr<EventShapeContainer> evtShape;
392  if (!evtShape) {
393  B2WARNING("Cannot find thrust of event information, did you forget to load the event shape calculation?");
394  return std::numeric_limits<float>::quiet_NaN();
395  }
396  PCmsLabTransform T;
397  B2Vector3D th = evtShape->getThrustAxis();
398  B2Vector3D particleMomentum = (T.rotateLabToCms() * part -> get4Vector()).Vect();
399  return std::cos(th.Angle(particleMomentum));
400  }
401 
402  double ImpactXY(const Particle* particle)
403  {
404  static DBObjPtr<BeamSpot> beamSpotDB;
405 
406  ROOT::Math::XYZVector mom = particle->getMomentum();
407 
408  ROOT::Math::XYZVector r = particle->getVertex() - ROOT::Math::XYZVector(beamSpotDB->getIPPosition());
409 
410  ROOT::Math::XYZVector Bfield = BFieldManager::getInstance().getFieldInTesla(ROOT::Math::XYZVector(beamSpotDB->getIPPosition()));
411 
412  ROOT::Math::XYZVector curvature = - Bfield * Const::speedOfLight * particle->getCharge(); //Curvature of the track
413  double T = TMath::Sqrt(mom.Perp2() - 2.0 * curvature.Dot(r.Cross(mom)) + curvature.Mag2() * r.Perp2());
414 
415  return TMath::Abs((-2 * r.Cross(mom).Z() + curvature.R() * r.Perp2()) / (T + mom.Rho()));
416  }
417 
418  double ArmenterosLongitudinalMomentumAsymmetry(const Particle* part)
419  {
420  const auto& frame = ReferenceFrame::GetCurrent();
421  int nDaughters = part -> getNDaughters();
422  if (nDaughters != 2)
423  B2FATAL("You are trying to use an Armenteros variable. The mother particle is required to have exactly two daughters");
424 
425  const auto& daughters = part -> getDaughters();
426  B2Vector3D motherMomentum = frame.getMomentum(part).Vect();
427  B2Vector3D daughter1Momentum = frame.getMomentum(daughters[0]).Vect();
428  B2Vector3D daughter2Momentum = frame.getMomentum(daughters[1]).Vect();
429 
430  int daughter1Charge = daughters[0] -> getCharge();
431  int daughter2Charge = daughters[1] -> getCharge();
432  double daughter1Ql = daughter1Momentum.Dot(motherMomentum) / motherMomentum.Mag();
433  double daughter2Ql = daughter2Momentum.Dot(motherMomentum) / motherMomentum.Mag();
434 
435  double Arm_alpha;
436  if (daughter2Charge > daughter1Charge)
437  Arm_alpha = (daughter2Ql - daughter1Ql) / (daughter2Ql + daughter1Ql);
438  else
439  Arm_alpha = (daughter1Ql - daughter2Ql) / (daughter1Ql + daughter2Ql);
440 
441  return Arm_alpha;
442  }
443 
444  double ArmenterosDaughter1Qt(const Particle* part)
445  {
446  const auto& frame = ReferenceFrame::GetCurrent();
447  int nDaughters = part -> getNDaughters();
448  if (nDaughters != 2)
449  B2FATAL("You are trying to use an Armenteros variable. The mother particle is required to have exactly two daughters.");
450 
451  const auto& daughters = part -> getDaughters();
452  B2Vector3D motherMomentum = frame.getMomentum(part).Vect();
453  B2Vector3D daughter1Momentum = frame.getMomentum(daughters[0]).Vect();
454  double qt = daughter1Momentum.Perp(motherMomentum);
455 
456  return qt;
457  }
458 
459  double ArmenterosDaughter2Qt(const Particle* part)
460  {
461  const auto& frame = ReferenceFrame::GetCurrent();
462  int nDaughters = part -> getNDaughters();
463  if (nDaughters != 2)
464  B2FATAL("You are trying to use an Armenteros variable. The mother particle is required to have exactly two daughters.");
465 
466  const auto& daughters = part -> getDaughters();
467  B2Vector3D motherMomentum = frame.getMomentum(part).Vect();
468  B2Vector3D daughter2Momentum = frame.getMomentum(daughters[1]).Vect();
469  double qt = daughter2Momentum.Perp(motherMomentum);
470 
471  return qt;
472  }
473 
474 
475 // mass ------------------------------------------------------------
476 
477  double particleMass(const Particle* part)
478  {
479  return part->getMass();
480  }
481 
482  double particleDMass(const Particle* part)
483  {
484  return part->getMass() - part->getPDGMass();
485  }
486 
487  double particleInvariantMassFromDaughters(const Particle* part)
488  {
489  const std::vector<Particle*> daughters = part->getDaughters();
490  if (daughters.size() > 0) {
491  ROOT::Math::PxPyPzEVector sum;
492  for (auto daughter : daughters)
493  sum += daughter->get4Vector();
494 
495  return sum.M();
496  } else {
497  return part->getMass(); // !
498  }
499  }
500 
501  double particleInvariantMassFromDaughtersDisplaced(const Particle* part)
502  {
503  ROOT::Math::XYZVector vertex = part->getVertex();
504  if (part->getParticleSource() != Particle::EParticleSourceObject::c_V0
505  && vertex.Rho() < 0.5) return particleInvariantMassFromDaughters(part);
506 
507  const std::vector<Particle*> daughters = part->getDaughters();
508  if (daughters.size() == 0) return particleInvariantMassFromDaughters(part);
509 
510  const double bField = BFieldManager::getFieldInTesla(vertex).Z();
511  ROOT::Math::PxPyPzMVector sum;
512  for (auto daughter : daughters) {
513  const TrackFitResult* tfr = daughter->getTrackFitResult();
514  if (!tfr) {
515  sum += daughter->get4Vector();
516  continue;
517  }
518  Helix helix = tfr->getHelix();
519  helix.passiveMoveBy(vertex);
520  double scalingFactor = daughter->getEffectiveMomentumScale();
521  double momX = scalingFactor * helix.getMomentumX(bField);
522  double momY = scalingFactor * helix.getMomentumY(bField);
523  double momZ = scalingFactor * helix.getMomentumZ(bField);
524  float mPDG = daughter->getPDGMass();
525  sum += ROOT::Math::PxPyPzMVector(momX, momY, momZ, mPDG);
526  }
527  return sum.M();
528  }
529 
530  double particleInvariantMassLambda(const Particle* part)
531  {
532  const std::vector<Particle*> daughters = part->getDaughters();
533  if (daughters.size() == 2) {
534  ROOT::Math::PxPyPzEVector dt1;
535  ROOT::Math::PxPyPzEVector dt2;
536  ROOT::Math::PxPyPzEVector dtsum;
537  double mpi = Const::pionMass;
538  double mpr = Const::protonMass;
539  dt1 = daughters[0]->get4Vector();
540  dt2 = daughters[1]->get4Vector();
541  double E1 = hypot(mpi, dt1.P());
542  double E2 = hypot(mpr, dt2.P());
543  dtsum = dt1 + dt2;
544  return sqrt((E1 + E2) * (E1 + E2) - dtsum.P() * dtsum.P());
545 
546  } else {
547  return part->getMass();
548  }
549  }
550 
551  double particleInvariantMassError(const Particle* part)
552  {
553  float invMass = part->getMass();
554  TMatrixFSym covarianceMatrix = part->getMomentumErrorMatrix();
555 
556  TVectorF jacobian(Particle::c_DimMomentum);
557  jacobian[0] = -1.0 * part->getPx() / invMass;
558  jacobian[1] = -1.0 * part->getPy() / invMass;
559  jacobian[2] = -1.0 * part->getPz() / invMass;
560  jacobian[3] = 1.0 * part->getEnergy() / invMass;
561 
562  double result = jacobian * (covarianceMatrix * jacobian);
563 
564  if (result < 0.0)
565  return std::numeric_limits<double>::quiet_NaN();
566 
567  return TMath::Sqrt(result);
568  }
569 
570  double particleInvariantMassSignificance(const Particle* part)
571  {
572  return particleDMass(part) / particleInvariantMassError(part);
573  }
574 
575  double particleMassSquared(const Particle* part)
576  {
577  ROOT::Math::PxPyPzEVector p4 = part->get4Vector();
578  return p4.M2();
579  }
580 
581  double b2bTheta(const Particle* part)
582  {
583  PCmsLabTransform T;
584  ROOT::Math::PxPyPzEVector pcms = T.rotateLabToCms() * part->get4Vector();
585  ROOT::Math::PxPyPzEVector b2bcms(-pcms.Px(), -pcms.Py(), -pcms.Pz(), pcms.E());
586  ROOT::Math::PxPyPzEVector b2blab = T.rotateCmsToLab() * b2bcms;
587  return b2blab.Theta();
588  }
589 
590  double b2bPhi(const Particle* part)
591  {
592  PCmsLabTransform T;
593  ROOT::Math::PxPyPzEVector pcms = T.rotateLabToCms() * part->get4Vector();
594  ROOT::Math::PxPyPzEVector b2bcms(-pcms.Px(), -pcms.Py(), -pcms.Pz(), pcms.E());
595  ROOT::Math::PxPyPzEVector b2blab = T.rotateCmsToLab() * b2bcms;
596  return b2blab.Phi();
597  }
598 
599  double b2bClusterTheta(const Particle* part)
600  {
601  // get associated ECLCluster
602  const ECLCluster* cluster = part->getECLCluster();
603  if (!cluster) return std::numeric_limits<float>::quiet_NaN();
604  const ECLCluster::EHypothesisBit clusterHypothesis = part->getECLClusterEHypothesisBit();
605 
606  // get 4 momentum from cluster
607  ClusterUtils clutls;
608  ROOT::Math::PxPyPzEVector p4Cluster = clutls.Get4MomentumFromCluster(cluster, clusterHypothesis);
609 
610  // find the vector that balances this in the CMS
611  PCmsLabTransform T;
612  ROOT::Math::PxPyPzEVector pcms = T.rotateLabToCms() * p4Cluster;
613  ROOT::Math::PxPyPzEVector b2bcms(-pcms.Px(), -pcms.Py(), -pcms.Pz(), pcms.E());
614  ROOT::Math::PxPyPzEVector b2blab = T.rotateCmsToLab() * b2bcms;
615  return b2blab.Theta();
616  }
617 
618  double b2bClusterPhi(const Particle* part)
619  {
620  // get associated ECLCluster
621  const ECLCluster* cluster = part->getECLCluster();
622  if (!cluster) return std::numeric_limits<float>::quiet_NaN();
623  const ECLCluster::EHypothesisBit clusterHypothesis = part->getECLClusterEHypothesisBit();
624 
625  // get 4 momentum from cluster
626  ClusterUtils clutls;
627  ROOT::Math::PxPyPzEVector p4Cluster = clutls.Get4MomentumFromCluster(cluster, clusterHypothesis);
628 
629  // find the vector that balances this in the CMS
630  PCmsLabTransform T;
631  ROOT::Math::PxPyPzEVector pcms = T.rotateLabToCms() * p4Cluster;
632  ROOT::Math::PxPyPzEVector b2bcms(-pcms.Px(), -pcms.Py(), -pcms.Pz(), pcms.E());
633  ROOT::Math::PxPyPzEVector b2blab = T.rotateCmsToLab() * b2bcms;
634  return b2blab.Phi();
635  }
636 
637 
638 // released energy --------------------------------------------------
639 
640  double particleQ(const Particle* part)
641  {
642  float m = part->getMass();
643  for (unsigned i = 0; i < part->getNDaughters(); i++) {
644  const Particle* child = part->getDaughter(i);
645  if (child)
646  m -= child->getMass();
647  }
648  return m;
649  }
650 
651  double particleDQ(const Particle* part)
652  {
653  float m = part->getMass() - part->getPDGMass();
654  for (unsigned i = 0; i < part->getNDaughters(); i++) {
655  const Particle* child = part->getDaughter(i);
656  if (child)
657  m -= (child->getMass() - child->getPDGMass());
658  }
659  return m;
660  }
661 
662 // Mbc and deltaE
663 
664  double particleMbc(const Particle* part)
665  {
666  PCmsLabTransform T;
667  ROOT::Math::PxPyPzEVector vec = T.rotateLabToCms() * part->get4Vector();
668  double E = T.getCMSEnergy() / 2;
669  double m2 = E * E - vec.P2();
670  double mbc = m2 >= 0 ? sqrt(m2) : std::numeric_limits<double>::quiet_NaN();
671  return mbc;
672  }
673 
674  double particleDeltaE(const Particle* part)
675  {
676  PCmsLabTransform T;
677  ROOT::Math::PxPyPzEVector vec = T.rotateLabToCms() * part->get4Vector();
678  return vec.E() - T.getCMSEnergy() / 2;
679  }
680 
681 // other ------------------------------------------------------------
682 
683 
684  void printParticleInternal(const Particle* p, int depth)
685  {
686  stringstream s("");
687  for (int i = 0; i < depth; i++) {
688  s << " ";
689  }
690  s << p->getPDGCode();
691  const MCParticle* mcp = p->getMCParticle();
692  if (mcp) {
693  unsigned int flags = MCMatching::getMCErrors(p, mcp);
694  s << " -> MC: " << mcp->getPDG() << ", mcErrors: " << flags << " ("
695  << MCMatching::explainFlags(flags) << ")";
696  s << ", mc-index " << mcp->getIndex();
697  } else {
698  s << " (no MC match)";
699  }
700  s << ", mdst-source " << p->getMdstSource();
701  B2INFO(s.str());
702  for (const auto* daughter : p->getDaughters()) {
703  printParticleInternal(daughter, depth + 1);
704  }
705  }
706 
707  bool printParticle(const Particle* p)
708  {
709  printParticleInternal(p, 0);
710  return 0;
711  }
712 
713 
714  double particleMCMomentumTransfer2(const Particle* part)
715  {
716  // for B meson MC particles only
717  const MCParticle* mcB = part->getMCParticle();
718 
719  if (!mcB)
720  return std::numeric_limits<double>::quiet_NaN();
721 
722  ROOT::Math::PxPyPzEVector pB = mcB->get4Vector();
723 
724  std::vector<MCParticle*> mcDaug = mcB->getDaughters();
725 
726  if (mcDaug.empty())
727  return std::numeric_limits<double>::quiet_NaN();
728 
729  // B -> X l nu
730  // q = pB - pX
731  ROOT::Math::PxPyPzEVector pX;
732 
733  for (auto mcTemp : mcDaug) {
734  if (abs(mcTemp->getPDG()) <= 16)
735  continue;
736 
737  pX += mcTemp->get4Vector();
738  }
739 
740  ROOT::Math::PxPyPzEVector q = pB - pX;
741 
742  return q.M2();
743  }
744 
745 // Recoil Kinematics related ---------------------------------------------
746  double recoilPx(const Particle* particle)
747  {
748  PCmsLabTransform T;
749 
750  // Initial state (e+e- momentum in LAB)
751  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
752 
753  // Use requested frame for final calculation
754  const auto& frame = ReferenceFrame::GetCurrent();
755  return frame.getMomentum(pIN - particle->get4Vector()).Px();
756  }
757 
758  double recoilPy(const Particle* particle)
759  {
760  PCmsLabTransform T;
761 
762  // Initial state (e+e- momentum in LAB)
763  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
764 
765  // Use requested frame for final calculation
766  const auto& frame = ReferenceFrame::GetCurrent();
767  return frame.getMomentum(pIN - particle->get4Vector()).Py();
768  }
769 
770  double recoilPz(const Particle* particle)
771  {
772  PCmsLabTransform T;
773 
774  // Initial state (e+e- momentum in LAB)
775  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
776 
777  // Use requested frame for final calculation
778  const auto& frame = ReferenceFrame::GetCurrent();
779  return frame.getMomentum(pIN - particle->get4Vector()).Pz();
780  }
781 
782  double recoilMomentum(const Particle* particle)
783  {
784  PCmsLabTransform T;
785 
786  // Initial state (e+e- momentum in LAB)
787  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
788 
789  // Use requested frame for final calculation
790  const auto& frame = ReferenceFrame::GetCurrent();
791  return frame.getMomentum(pIN - particle->get4Vector()).P();
792  }
793 
794  double recoilMomentumTheta(const Particle* particle)
795  {
796  PCmsLabTransform T;
797 
798  // Initial state (e+e- momentum in LAB)
799  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
800 
801  // Use requested frame for final calculation
802  const auto& frame = ReferenceFrame::GetCurrent();
803  return frame.getMomentum(pIN - particle->get4Vector()).Theta();
804  }
805 
806  double recoilMomentumPhi(const Particle* particle)
807  {
808  PCmsLabTransform T;
809 
810  // Initial state (e+e- momentum in LAB)
811  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
812 
813  // Use requested frame for final calculation
814  const auto& frame = ReferenceFrame::GetCurrent();
815  return frame.getMomentum(pIN - particle->get4Vector()).Phi();
816  }
817 
818  double recoilEnergy(const Particle* particle)
819  {
820  PCmsLabTransform T;
821 
822  // Initial state (e+e- momentum in LAB)
823  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
824 
825  // Use requested frame for final calculation
826  const auto& frame = ReferenceFrame::GetCurrent();
827  return frame.getMomentum(pIN - particle->get4Vector()).E();
828  }
829 
830  double recoilMass(const Particle* particle)
831  {
832  PCmsLabTransform T;
833 
834  // Initial state (e+e- momentum in LAB)
835  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
836 
837  // Use requested frame for final calculation
838  const auto& frame = ReferenceFrame::GetCurrent();
839  return frame.getMomentum(pIN - particle->get4Vector()).M();
840  }
841 
842  double recoilMassSquared(const Particle* particle)
843  {
844  PCmsLabTransform T;
845 
846  // Initial state (e+e- momentum in LAB)
847  ROOT::Math::PxPyPzEVector pIN = T.getBeamFourMomentum();
848 
849  // Use requested frame for final calculation
850  const auto& frame = ReferenceFrame::GetCurrent();
851  return frame.getMomentum(pIN - particle->get4Vector()).M2();
852  }
853 
854  double m2RecoilSignalSide(const Particle* part)
855  {
856  PCmsLabTransform T;
857  double beamEnergy = T.getCMSEnergy() / 2.;
858  if (part->getNDaughters() != 2) return std::numeric_limits<double>::quiet_NaN();
859  ROOT::Math::PxPyPzEVector tagVec = T.rotateLabToCms() * part->getDaughter(0)->get4Vector();
860  ROOT::Math::PxPyPzEVector sigVec = T.rotateLabToCms() * part->getDaughter(1)->get4Vector();
861  tagVec.SetE(-beamEnergy);
862  return (-tagVec - sigVec).M2();
863  }
864 
865  double recoilMCDecayType(const Particle* particle)
866  {
867  auto* mcp = particle->getMCParticle();
868 
869  if (!mcp)
870  return std::numeric_limits<double>::quiet_NaN();
871 
872  MCParticle* mcMother = mcp->getMother();
873 
874  if (!mcMother)
875  return std::numeric_limits<double>::quiet_NaN();
876 
877  std::vector<MCParticle*> daughters = mcMother->getDaughters();
878 
879  if (daughters.size() != 2)
880  return std::numeric_limits<double>::quiet_NaN();
881 
882  MCParticle* recoilMC = nullptr;
883  if (daughters[0]->getArrayIndex() == mcp->getArrayIndex())
884  recoilMC = daughters[1];
885  else
886  recoilMC = daughters[0];
887 
888  if (!recoilMC->hasStatus(MCParticle::c_PrimaryParticle))
889  return std::numeric_limits<double>::quiet_NaN();
890 
891  int decayType = 0;
892  checkMCParticleDecay(recoilMC, decayType, false);
893 
894  if (decayType == 0)
895  checkMCParticleDecay(recoilMC, decayType, true);
896 
897  return decayType;
898  }
899 
900  void checkMCParticleDecay(MCParticle* mcp, int& decayType, bool recursive)
901  {
902  int nHadronicParticles = 0;
903  int nPrimaryParticleDaughters = 0;
904  std::vector<MCParticle*> daughters = mcp->getDaughters();
905 
906  // Are any of the daughters primary particles? How many of them are hadrons?
907  for (auto& daughter : daughters) {
908  if (!daughter->hasStatus(MCParticle::c_PrimaryParticle))
909  continue;
910 
911  nPrimaryParticleDaughters++;
912  if (abs(daughter->getPDG()) > Const::photon.getPDGCode())
913  nHadronicParticles++;
914  }
915 
916  if (nPrimaryParticleDaughters > 1) {
917  for (auto& daughter : daughters) {
918  if (!daughter->hasStatus(MCParticle::c_PrimaryParticle))
919  continue;
920 
921  if (abs(daughter->getPDG()) == 12 or abs(daughter->getPDG()) == 14 or abs(daughter->getPDG()) == 16) {
922  if (!recursive) {
923  if (nHadronicParticles == 0) {
924  decayType = 1.0;
925  break;
926  } else {
927  decayType = 2.0;
928  break;
929  }
930  } else {
931  decayType = 3.0;
932  break;
933  }
934  }
935 
936  else if (recursive)
937  checkMCParticleDecay(daughter, decayType, recursive);
938  }
939  }
940  }
941 
942  int nRemainingTracksInEvent(const Particle* particle)
943  {
944 
945  StoreArray<Track> tracks;
946  int event_tracks = tracks.getEntries();
947 
948  int par_tracks = 0;
949  const auto& daughters = particle->getFinalStateDaughters();
950  for (const auto& daughter : daughters) {
951  int pdg = abs(daughter->getPDGCode());
952  if (pdg == Const::electron.getPDGCode() or pdg == Const::muon.getPDGCode() or pdg == Const::pion.getPDGCode()
953  or pdg == Const::kaon.getPDGCode() or pdg == Const::proton.getPDGCode())
954  par_tracks++;
955  }
956  return event_tracks - par_tracks;
957  }
958 
959  double trackMatchType(const Particle* particle)
960  {
961  // Particle does not contain a ECL Cluster
962  double result = std::numeric_limits<double>::quiet_NaN();
963 
964  const ECLCluster* cluster = particle->getECLCluster();
965  if (cluster) {
966  // No associated track is default
967  result = 0;
968  if (cluster->isTrack()) {
969  // There is a track match
970  result = 1.0;
971  }
972  }
973  return result;
974  }
975 
976 
977  bool False(const Particle*)
978  {
979  return 0;
980  }
981 
982  bool True(const Particle*)
983  {
984  return 1;
985  }
986 
987  double infinity(const Particle*)
988  {
989  double inf = std::numeric_limits<double>::infinity();
990  return inf;
991  }
992 
993  double random(const Particle*)
994  {
995  return gRandom->Uniform(0, 1);
996  }
997 
998  double eventRandom(const Particle*)
999  {
1000  std::string key = "__eventRandom";
1001  StoreObjPtr<EventExtraInfo> eventExtraInfo;
1002  if (not eventExtraInfo.isValid())
1003  eventExtraInfo.create();
1004  if (eventExtraInfo->hasExtraInfo(key)) {
1005  return eventExtraInfo->getExtraInfo(key);
1006  } else {
1007  double value = gRandom->Uniform(0, 1);
1008  eventExtraInfo->addExtraInfo(key, value);
1009  return value;
1010  }
1011  }
1012 
1013  Manager::FunctionPtr particleDistToClosestExtTrk(const std::vector<std::string>& arguments)
1014  {
1015  if (arguments.size() != 3 && arguments.size() != 4) {
1016  B2ERROR("Wrong number of arguments (3 or 4 required) for meta variable minET2ETDist");
1017  return nullptr;
1018  }
1019  bool useHighestProbMassForExt(true);
1020  if (arguments.size() == 4) {
1021  try {
1022  useHighestProbMassForExt = static_cast<bool>(Belle2::convertString<int>(arguments[3]));
1023  } catch (std::invalid_argument& e) {
1024  B2ERROR("Fourth (optional) argument of minET2ETDist must be an integer flag.");
1025  return nullptr;
1026  }
1027  }
1028 
1029  std::string detName = arguments[0];
1030  std::string detLayer = arguments[1];
1031  std::string referenceListName = arguments[2];
1032  std::string extraSuffix = (useHighestProbMassForExt) ? "__useHighestProbMassForExt" : "";
1033  // Distance to closets neighbour at this detector layer.
1034  std::string extraInfo = "distToClosestTrkAt" + detName + detLayer + "_VS_" + referenceListName + extraSuffix;
1035 
1036  auto func = [ = ](const Particle * part) -> double {
1037  auto dist = (part->hasExtraInfo(extraInfo)) ? part->getExtraInfo(extraInfo) : std::numeric_limits<float>::quiet_NaN();
1038  return dist;
1039  };
1040 
1041  return func;
1042  }
1043 
1044 // Track helix extrapolation-based isolation --------------------------------------------------
1045 
1046  Manager::FunctionPtr particleDistToClosestExtTrkVar(const std::vector<std::string>& arguments)
1047  {
1048  if (arguments.size() != 4) {
1049  B2ERROR("Wrong number of arguments (4 required) for meta variable minET2ETDistVar");
1050  return nullptr;
1051  }
1052 
1053  std::string detName = arguments[0];
1054  std::string detLayer = arguments[1];
1055  std::string referenceListName = arguments[2];
1056  std::string variableName = arguments[3];
1057  // Mdst array index of the particle that is closest to the particle in question.
1058  std::string extraInfo = "idxOfClosestPartAt" + detName + detLayer + "In_" + referenceListName;
1059 
1060  auto func = [ = ](const Particle * part) -> double {
1061 
1062  StoreObjPtr<ParticleList> refPartList(referenceListName);
1063  if (!refPartList.isValid())
1064  {
1065  B2FATAL("Invalid Listname " << referenceListName << " given to minET2ETDistVar!");
1066  }
1067 
1068  if (!part->hasExtraInfo(extraInfo))
1069  {
1070  return std::numeric_limits<float>::quiet_NaN();
1071  }
1072 
1073  const Variable::Manager::Var* var = Manager::Instance().getVariable(variableName);
1074  auto refPart = refPartList->getParticleWithMdstIdx(part->getExtraInfo(extraInfo));
1075 
1076  return std::get<double>(var->function(refPart));
1077  };
1078 
1079  return func;
1080  }
1081 
1082 
1083  Manager::FunctionPtr particleExtTrkIsoScoreVar(const std::vector<std::string>& arguments)
1084  {
1085 
1086  if (arguments.size() < 3) {
1087  B2ERROR("Wrong number of arguments (at least 3 required) for meta variable minET2ETIsoScore");
1088  return nullptr;
1089  }
1090 
1091  std::string referenceListName = arguments[0];
1092  bool useHighestProbMassForExt;
1093  try {
1094  useHighestProbMassForExt = static_cast<bool>(Belle2::convertString<int>(arguments[1]));
1095  } catch (std::invalid_argument& e) {
1096  B2ERROR("Second argument must be an integer flag.");
1097  return nullptr;
1098  }
1099  std::string extraSuffix = (useHighestProbMassForExt) ? "__useHighestProbMassForExt" : "";
1100 
1101  std::vector<std::string> detectorNames(arguments.begin() + 2, arguments.end());
1102 
1103  auto func = [ = ](const Particle * part) -> double {
1104 
1105  StoreObjPtr<ParticleList> refPartList(referenceListName);
1106  if (!refPartList.isValid())
1107  {
1108  B2FATAL("Invalid Listname " << referenceListName << " given to minET2ETIsoScore!");
1109  }
1110 
1111  double scoreSum(0.0);
1112  for (auto& detName : detectorNames)
1113  {
1114  std::string extraInfo = "trkIsoScore" + detName + "_VS_" + referenceListName + extraSuffix;
1115  if (!part->hasExtraInfo(extraInfo)) {
1116  return std::numeric_limits<float>::quiet_NaN();
1117  }
1118  auto scoreDet = part->getExtraInfo(extraInfo);
1119  if (std::isnan(scoreDet)) {
1120  return std::numeric_limits<float>::quiet_NaN();
1121  }
1122  scoreSum += scoreDet;
1123  }
1124 
1125  // Normalise sum of scores between [0, 1]
1126  auto minScore = 0.;
1127  auto maxScore = detectorNames.size();
1128  return (scoreSum - minScore) / (maxScore - minScore);
1129 
1130  };
1131 
1132  return func;
1133  }
1134 
1135  VARIABLE_GROUP("Kinematics");
1136  REGISTER_VARIABLE("p", particleP, "momentum magnitude", "GeV/c");
1137  REGISTER_VARIABLE("E", particleE, "energy", "GeV");
1138 
1139  REGISTER_VARIABLE("E_uncertainty", particleEUncertainty, R"DOC(energy uncertainty (:math:`\sqrt{\sigma^2}`))DOC", "GeV");
1140  REGISTER_VARIABLE("ECLClusterE_uncertainty", particleClusterEUncertainty,
1141  "energy uncertainty as given by the underlying ECL cluster", "GeV");
1142  REGISTER_VARIABLE("px", particlePx, "momentum component x", "GeV/c");
1143  REGISTER_VARIABLE("py", particlePy, "momentum component y", "GeV/c");
1144  REGISTER_VARIABLE("pz", particlePz, "momentum component z", "GeV/c");
1145  REGISTER_VARIABLE("pt", particlePt, "transverse momentum", "GeV/c");
1146  REGISTER_VARIABLE("xp", particleXp,
1147  "scaled momentum: the momentum of the particle in the CMS as a fraction of its maximum available momentum in the collision");
1148  REGISTER_VARIABLE("pErr", particlePErr, "error of momentum magnitude", "GeV/c");
1149  REGISTER_VARIABLE("pxErr", particlePxErr, "error of momentum component x", "GeV/c");
1150  REGISTER_VARIABLE("pyErr", particlePyErr, "error of momentum component y", "GeV/c");
1151  REGISTER_VARIABLE("pzErr", particlePzErr, "error of momentum component z", "GeV/c");
1152  REGISTER_VARIABLE("ptErr", particlePtErr, "error of transverse momentum", "GeV/c");
1153  REGISTER_VARIABLE("momVertCovM(i,j)", covMatrixElement,
1154  "returns the (i,j)-th element of the MomentumVertex Covariance Matrix (7x7).\n"
1155  "Order of elements in the covariance matrix is: px, py, pz, E, x, y, z.", "GeV/c, GeV/c, GeV/c, GeV, cm, cm, cm");
1156  REGISTER_VARIABLE("momDevChi2", momentumDeviationChi2, R"DOC(
1157 momentum deviation :math:`\chi^2` value calculated as :math:`\chi^2 = \sum_i (p_i - mc(p_i))^2/\sigma(p_i)^2`,
1158 where :math:`\sum` runs over i = px, py, pz and :math:`mc(p_i)` is the mc truth value and :math:`\sigma(p_i)` is the estimated error of i-th component of momentum vector
1159 )DOC");
1160  REGISTER_VARIABLE("theta", particleTheta, "polar angle", "rad");
1161  REGISTER_VARIABLE("thetaErr", particleThetaErr, "error of polar angle", "rad");
1162  REGISTER_VARIABLE("cosTheta", particleCosTheta, "momentum cosine of polar angle");
1163  REGISTER_VARIABLE("cosThetaErr", particleCosThetaErr, "error of momentum cosine of polar angle");
1164  REGISTER_VARIABLE("phi", particlePhi, "momentum azimuthal angle", "rad");
1165  REGISTER_VARIABLE("phiErr", particlePhiErr, "error of momentum azimuthal angle", "rad");
1166  REGISTER_VARIABLE("PDG", particlePDGCode, "PDG code");
1167  REGISTER_VARIABLE("cosAngleBetweenMomentumAndVertexVectorInXYPlane",
1168  cosAngleBetweenMomentumAndVertexVectorInXYPlane,
1169  "cosine of the angle between momentum and vertex vector (vector connecting ip and fitted vertex) of this particle in xy-plane");
1170  REGISTER_VARIABLE("cosAngleBetweenMomentumAndVertexVector",
1171  cosAngleBetweenMomentumAndVertexVector,
1172  "cosine of the angle between momentum and vertex vector (vector connecting ip and fitted vertex) of this particle");
1173  REGISTER_VARIABLE("cosThetaBetweenParticleAndNominalB",
1174  cosThetaBetweenParticleAndNominalB,
1175  "cosine of the angle in CMS between momentum the particle and a nominal B particle. It is somewhere between -1 and 1 if only a massless particle like a neutrino is missing in the reconstruction.");
1176  REGISTER_VARIABLE("cosToThrustOfEvent", cosToThrustOfEvent,
1177  "Returns the cosine of the angle between the particle and the thrust axis of the event, as calculate by the EventShapeCalculator module. buildEventShape() must be run before calling this variable");
1178 
1179  REGISTER_VARIABLE("ImpactXY" , ImpactXY , "The impact parameter of the given particle in the xy plane", "cm");
1180 
1181  REGISTER_VARIABLE("M", particleMass, R"DOC(
1182 The particle's mass.
1183 
1184 Note that this is context-dependent variable and can take different values depending on the situation. This should be the "best" value possible with the information provided.
1185 
1186 - If this particle is track- or cluster- based, then this is the value of the mass hypothesis.
1187 - If this particle is an MC particle then this is the mass of that particle.
1188 - If this particle is composite, then *initially* this takes the value of the invariant mass of the daughters.
1189 - If this particle is composite and a *mass or vertex fit* has been performed then this may be updated by the fit.
1190 
1191  * You will see a difference between this mass and the :b2:var:`InvM`.
1192  )DOC", "GeV/:math:`\\text{c}^2`");
1193  REGISTER_VARIABLE("dM", particleDMass, "mass minus nominal mass", "GeV/:math:`\\text{c}^2`");
1194  REGISTER_VARIABLE("Q", particleQ, "energy released in decay", "GeV");
1195  REGISTER_VARIABLE("dQ", particleDQ, ":b2:var:`Q` minus nominal energy released in decay", "GeV");
1196  REGISTER_VARIABLE("Mbc", particleMbc, "beam constrained mass", "GeV/:math:`\\text{c}^2`");
1197  REGISTER_VARIABLE("deltaE", particleDeltaE, "difference between :b2:var:`E` and half the center of mass energy", "GeV");
1198  REGISTER_VARIABLE("M2", particleMassSquared, "The particle's mass squared.", ":math:`[\\text{GeV}/\\text{c}^2]^2`");
1199 
1200  REGISTER_VARIABLE("InvM", particleInvariantMassFromDaughtersDisplaced,
1201  "invariant mass (determined from particle's daughter 4-momentum vectors). If this particle is V0 or decays at rho > 5 mm, its daughter 4-momentum vectors at fitted vertex are taken.\n"
1202  "If this particle has no daughters, defaults to :b2:var:`M`.", "GeV/:math:`\\text{c}^2`");
1203  REGISTER_VARIABLE("InvMLambda", particleInvariantMassLambda,
1204  "Invariant mass (determined from particle's daughter 4-momentum vectors), assuming the first daughter is a pion and the second daughter is a proton.\n"
1205  "If the particle has not 2 daughters, it returns just the mass value.", "GeV/:math:`\\text{c}^2`");
1206 
1207  REGISTER_VARIABLE("ErrM", particleInvariantMassError,
1208  "uncertainty of invariant mass", "GeV/:math:`\\text{c}^2`");
1209  REGISTER_VARIABLE("SigM", particleInvariantMassSignificance,
1210  "signed deviation of particle's invariant mass from its nominal mass in units of the uncertainty on the invariant mass (:b2:var:`dM`/:b2:var:`ErrM`)");
1211 
1212  REGISTER_VARIABLE("pxRecoil", recoilPx,
1213  "component x of 3-momentum recoiling against given Particle", "GeV/c");
1214  REGISTER_VARIABLE("pyRecoil", recoilPy,
1215  "component y of 3-momentum recoiling against given Particle", "GeV/c");
1216  REGISTER_VARIABLE("pzRecoil", recoilPz,
1217  "component z of 3-momentum recoiling against given Particle", "GeV/c");
1218 
1219  REGISTER_VARIABLE("pRecoil", recoilMomentum,
1220  "magnitude of 3 - momentum recoiling against given Particle", "GeV/c");
1221  REGISTER_VARIABLE("pRecoilTheta", recoilMomentumTheta,
1222  "Polar angle of a particle's missing momentum", "rad");
1223  REGISTER_VARIABLE("pRecoilPhi", recoilMomentumPhi,
1224  "Azimuthal angle of a particle's missing momentum", "rad");
1225  REGISTER_VARIABLE("eRecoil", recoilEnergy,
1226  "energy recoiling against given Particle", "GeV");
1227  REGISTER_VARIABLE("mRecoil", recoilMass,
1228  "Invariant mass of the system recoiling against given Particle", "GeV/:math:`\\text{c}^2`");
1229  REGISTER_VARIABLE("m2Recoil", recoilMassSquared,
1230  "invariant mass squared of the system recoiling against given Particle", ":math:`[\\text{GeV}/\\text{c}^2]^2`");
1231  REGISTER_VARIABLE("m2RecoilSignalSide", m2RecoilSignalSide,
1232  "Squared recoil mass of the signal side which is calculated in the CMS frame under the assumption that the signal and tag side are produced back to back and the tag side energy equals the beam energy. The variable must be applied to the Upsilon and the tag side must be the first, the signal side the second daughter",
1233  ":math:`[\\text{GeV}/\\text{c}^2]^2`");
1234 
1235  REGISTER_VARIABLE("b2bTheta", b2bTheta,
1236  "Polar angle in the lab system that is back-to-back to the particle in the CMS. Useful for low multiplicity studies.", "rad");
1237  REGISTER_VARIABLE("b2bPhi", b2bPhi,
1238  "Azimuthal angle in the lab system that is back-to-back to the particle in the CMS. Useful for low multiplicity studies.", "rad");
1239  REGISTER_VARIABLE("b2bClusterTheta", b2bClusterTheta,
1240  "Polar angle in the lab system that is back-to-back to the particle's associated ECLCluster in the CMS. Returns NAN if no cluster is found. Useful for low multiplicity studies.",
1241  "rad");
1242  REGISTER_VARIABLE("b2bClusterPhi", b2bClusterPhi,
1243  "Azimuthal angle in the lab system that is back-to-back to the particle's associated ECLCluster in the CMS. Returns NAN if no cluster is found. Useful for low multiplicity studies.",
1244  "rad");
1245  REGISTER_VARIABLE("ArmenterosLongitudinalMomentumAsymmetry", ArmenterosLongitudinalMomentumAsymmetry,
1246  "Longitudinal momentum asymmetry of V0's daughters.\n"
1247  "The mother (V0) is required to have exactly two daughters");
1248  REGISTER_VARIABLE("ArmenterosDaughter1Qt", ArmenterosDaughter1Qt,
1249  "Transverse momentum of the first daughter with respect to the V0 mother.\n"
1250  "The mother is required to have exactly two daughters", "GeV/c");
1251  REGISTER_VARIABLE("ArmenterosDaughter2Qt", ArmenterosDaughter2Qt,
1252  "Transverse momentum of the second daughter with respect to the V0 mother.\n"
1253  "The mother is required to have exactly two daughters", "GeV/c");
1254 
1255  VARIABLE_GROUP("Miscellaneous");
1256  REGISTER_VARIABLE("nRemainingTracksInEvent", nRemainingTracksInEvent,
1257  "Number of tracks in the event - Number of tracks( = charged FSPs) of particle.");
1258  REGISTER_VARIABLE("trackMatchType", trackMatchType, R"DOC(
1259 
1260  * -1 particle has no ECL cluster
1261  * 0 particle has no associated track
1262  * 1 there is a matched track called connected - region(CR) track match
1263  )DOC");
1264  MAKE_DEPRECATED("trackMatchType", true, "light-2012-minos", R"DOC(
1265  Use better variables like `trackNECLClusters`, `clusterTrackMatch`, and `nECLClusterTrackMatches`.)DOC");
1266 
1267  REGISTER_VARIABLE("decayTypeRecoil", recoilMCDecayType,
1268  "type of the particle decay(no related mcparticle = -1, hadronic = 0, direct leptonic = 1, direct semileptonic = 2,"
1269  "lower level leptonic = 3.");
1270 
1271  REGISTER_VARIABLE("printParticle", printParticle,
1272  "For debugging, print Particle and daughter PDG codes, plus MC match. Returns 0.");
1273  REGISTER_VARIABLE("mcMomTransfer2", particleMCMomentumTransfer2,
1274  "Return the true momentum transfer to lepton pair in a B(semi -) leptonic B meson decay.", "GeV/c");
1275  REGISTER_VARIABLE("False", False,
1276  "returns always 0, used for testing and debugging.");
1277  REGISTER_VARIABLE("True", True,
1278  "returns always 1, used for testing and debugging.");
1279  REGISTER_VARIABLE("infinity", infinity,
1280  "returns std::numeric_limits<double>::infinity()");
1281  REGISTER_VARIABLE("random", random,
1282  "return a random number between 0 and 1 for each candidate. Can be used, e.g. for picking a random"
1283  "candidate in the best candidate selection.");
1284  REGISTER_VARIABLE("eventRandom", eventRandom,
1285  "[Eventbased] Returns a random number between 0 and 1 for this event. Can be used, e.g. for applying an event prescale.");
1286  REGISTER_METAVARIABLE("minET2ETDist(detName, detLayer, referenceListName, useHighestProbMassForExt=1)", particleDistToClosestExtTrk,
1287  R"DOC(Returns the distance in [cm] between the particle and the nearest particle in the reference list at the given detector layer surface.
1288 The definition is based on the track helices extrapolation.
1289 
1290 * The first argument is the name of the detector to consider.
1291 * The second argument is the detector layer on whose surface we search for the nearest neighbour.
1292 * The third argument is the reference particle list name used to search for the nearest neighbour.
1293 * The fourth (optional) argument is an integer ("boolean") flag: if 1 (the default, if nothing is set), it is assumed the extrapolation was done with the most probable mass hypothesis for the track fit;
1294  if 0, it is assumed the mass hypothesis matching the particle lists' PDG was used.
1295 
1296 .. note::
1297  This variable requires to run the ``TrackIsoCalculator`` module first.
1298  Note that the choice of input parameters of this metafunction must correspond to the settings used to configure the module!
1299 )DOC",
1300  Manager::VariableDataType::c_double);
1301 
1302  REGISTER_METAVARIABLE("minET2ETDistVar(detName, detLayer, referenceListName, variableName)", particleDistToClosestExtTrkVar,
1303  R"DOC(Returns the value of the variable for the nearest neighbour to this particle as taken from the reference list at the given detector layer surface.
1304 , according to the distance definition of `minET2ETDist`.
1305 
1306 * The first argument is the name of the detector to consider.
1307 * The second argument is the detector layer on whose surface we search for the nearest neighbour.
1308 * The third argument is the reference particle list name used to search for the nearest neighbour.
1309 * The fourth argument is a variable name, e.g. `nCDCHits`.
1310 
1311 .. note::
1312  This variable requires to run the ``TrackIsoCalculator`` module first.
1313  Note that the choice of input parameters of this metafunction must correspond to the settings used to configure the module!
1314 )DOC",
1315  Manager::VariableDataType::c_double);
1316 
1317  REGISTER_METAVARIABLE("minET2ETIsoScore(referenceListName, useHighestProbMassForExt, detectorList)", particleExtTrkIsoScoreVar,
1318  R"DOC(Returns the particle's isolation score based on:
1319 
1320 * The number of detector layers where a close-enough neighbour to this particle is found, according to the distance definition of `minET2ETDist` and a set of thresholds defined in the ``TrackIsoCalculator`` module.
1321 * A set of per-detector weights quantifying the impact of each detector on the PID for this particle type.
1322 
1323 The score is normalised in [0, 1], where values closer to 1 indicates a well-isolated particle.
1324 
1325 .. note::
1326  The detector weights are considered for the score definition only if ``excludePIDDetWeights=false`` in the ``TrackIsoCalculator`` module configuration.
1327 
1328 * The first argument is the reference particle list name used to search for the nearest neighbour.
1329 * The second argument is an integer ("boolean") flag: if 1, it is assumed the extrapolation was done with the most probable mass hypothesis for the track fit;
1330  if 0, it is assumed the mass hypothesis matching the particle lists' PDG was used.
1331 * The remaining arguments are a comma-separated list of detector names. At least one must be chosen among {CDC, TOP, ARICH, ECL, KLM}.
1332 
1333 .. note::
1334  This variable requires to run the ``TrackIsoCalculator`` module first.
1335  Note that the choice of input parameters of this metafunction must correspond to the settings used to configure the module!
1336 )DOC",
1337  Manager::VariableDataType::c_double);
1338 
1339  }
1341 }
DataType Perp() const
The transverse component (R in cylindrical coordinate system).
Definition: B2Vector3.h:200
const MCParticle * getMCParticle() const
Returns the pointer to the MCParticle object that was used to create this Particle (ParticleType == c...
Definition: Particle.cc:972
Class to store variables with their name which were sent to the logging service.
#define MAKE_DEPRECATED(name, make_fatal, version, description)
Registers a variable as deprecated.
Definition: Manager.h:443
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:516
Abstract base class for different kinds of events.
Definition: ClusterUtils.h:23