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