Belle II Software  release-06-02-00
ParameterVariables.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 #include <analysis/variables/ParameterVariables.h>
10 #include <analysis/VariableManager/Manager.h>
11 #include <analysis/dataobjects/Particle.h>
12 #include <analysis/utility/PCmsLabTransform.h>
13 #include <analysis/utility/ReferenceFrame.h>
14 
15 #include <framework/logging/Logger.h>
16 #include <framework/datastore/StoreArray.h>
17 
18 #include <mdst/dataobjects/MCParticle.h>
19 
20 #include <mdst/dataobjects/Track.h>
21 #include <mdst/dataobjects/TrackFitResult.h>
22 
23 #include <TLorentzVector.h>
24 #include <TVectorF.h>
25 #include <TVector3.h>
26 
27 #include <cmath>
28 
29 
30 namespace Belle2 {
35  namespace Variable {
36 
37  bool almostContains(const std::vector<double>& vector, int value)
38  {
39  for (const auto& item : vector)
40  if (std::abs(value - item) < 1e-3)
41  return true;
42  return false;
43  }
44 
45  double RandomChoice(const Particle*, const std::vector<double>& choices)
46  {
47  int r = std::rand() % choices.size() + 1;
48  auto it = choices.begin();
49  std::advance(it, r);
50  return *it;
51  }
52 
53  double NumberOfMCParticlesInEvent(const Particle*, const std::vector<double>& pdgs)
54  {
55  StoreArray<MCParticle> mcParticles;
56  int counter = 0;
57  for (int i = 0; i < mcParticles.getEntries(); ++i) {
58  if (mcParticles[i]->getStatus(MCParticle::c_PrimaryParticle) and almostContains(pdgs, std::abs(mcParticles[i]->getPDG())))
59  counter++;
60  }
61  return counter;
62  }
63 
64  double isAncestorOf(const Particle* part, const std::vector<double>& daughterIDs)
65  {
66  if (part == nullptr)
67  return std::numeric_limits<float>::quiet_NaN();
68 
69  // If particle has no MC relation, MC chain doesn't exist
70  const MCParticle* mcpart = part->getMCParticle();
71  if (mcpart == nullptr)
72  return std::numeric_limits<float>::quiet_NaN();
73 
74  if (daughterIDs.empty())
75  B2FATAL("Wrong number of arguments for parameter function isAncestorOf. At least one needed!");
76 
77  // Get to the daughter of interest
78  const Particle* curParticle = part;
79  double isAncestor = 0.0;
80 
81  for (unsigned int i = 0; i < daughterIDs.size(); i++) {
82  int nCurDaughters = curParticle->getNDaughters();
83  if (nCurDaughters == 0)
84  B2FATAL("Assumed mother of particle at argument " << i << " has no daughters!");
85  if (daughterIDs[i] >= nCurDaughters)
86  B2FATAL("Assumed mother of particle at argument " << i << " has only " << nCurDaughters
87  << " daughters, but daughter at position " << daughterIDs[i] << " expected!");
88  const Particle* curDaughter = curParticle->getDaughter(daughterIDs[i]);
89  if (curDaughter == nullptr)
90  return std::numeric_limits<float>::quiet_NaN();
91  curParticle = curDaughter;
92  }
93 
94  // Daughter obtained, get MC particle of daughter
95  const MCParticle* finalMCDaughter = curParticle->getMCParticle();
96  if (finalMCDaughter == nullptr)
97  return std::numeric_limits<float>::quiet_NaN();
98 
99  // Go up the MC chain, check for ancestor
100  const MCParticle* curMCParticle = finalMCDaughter;
101 
102  while (curMCParticle != nullptr) {
103  const MCParticle* curMCMother = curMCParticle->getMother();
104  if (curMCMother == nullptr)
105  return 0.0;
106  else {
107  if (curMCMother->getArrayIndex() == mcpart->getArrayIndex()) {
108  isAncestor++;
109  break;
110  } else {
111  curMCParticle = curMCMother;
112  isAncestor++;
113  }
114  }
115  }
116  return isAncestor;
117  }
118 
119  double hasAncestor(const Particle* part, const std::vector<double>& args)
120  {
121  if (part == nullptr)
122  return std::numeric_limits<float>::quiet_NaN();
123 
124  // If particle has no MC relation, MC chain doesn't exist
125  const MCParticle* mcpart = part->getMCParticle();
126  if (mcpart == nullptr)
127  return std::numeric_limits<float>::quiet_NaN();
128 
129  int m_PDG, m_sign = 0;
130 
131  if (args.empty())
132  B2FATAL("Wrong number of arguments for variable hasAncestor!");
133  else if (args.size() == 1) {
134  if (args[0] == 0)
135  B2FATAL("PDG code in variable hasAncestor is 0!");
136  else
137  m_PDG = args[0];
138  } else if (args.size() == 2) {
139  if (args[0] == 0 or (args[1] != 0 and args[1] != 1))
140  B2FATAL("PDG code in variable hasAncestor is 0 or second argument is not 0 or 1!");
141  else {
142  m_PDG = args[0];
143  m_sign = args[1];
144  }
145  } else {
146  B2FATAL("Too many arguments for variable hasAncestor!");
147  }
148 
149  unsigned int nLevels = 0;
150 
151  const MCParticle* curMCParticle = mcpart;
152 
153  while (curMCParticle != nullptr) {
154  const MCParticle* curMCMother = curMCParticle->getMother();
155  if (curMCMother == nullptr)
156  return 0;
157 
158  int pdg = curMCMother->getPDG();
159  if (m_sign == 0)
160  pdg = abs(pdg);
161 
162  if (pdg == m_PDG) {
163  ++nLevels;
164  break;
165  } else {
166  ++nLevels;
167  curMCParticle = curMCMother;
168  }
169  }
170  return nLevels;
171  }
172 
173 
174  double daughterInvariantMass(const Particle* particle, const std::vector<double>& daughter_indexes)
175  {
176  if (!particle)
177  return std::numeric_limits<float>::quiet_NaN();
178 
179  TLorentzVector sum;
180  const auto& daughters = particle->getDaughters();
181  int nDaughters = static_cast<int>(daughters.size());
182 
183  for (auto& double_daughter : daughter_indexes) {
184  long daughter = std::lround(double_daughter);
185  if (daughter >= nDaughters)
186  return std::numeric_limits<float>::quiet_NaN();
187 
188  sum += daughters[daughter]->get4Vector();
189  }
190 
191  return sum.M();
192  }
193 
194  double daughterMCInvariantMass(const Particle* particle, const std::vector<double>& daughter_indexes)
195  {
196  if (!particle)
197  return std::numeric_limits<float>::quiet_NaN();
198 
199  TLorentzVector sum;
200  const auto& daughters = particle->getDaughters();
201  int nDaughters = static_cast<int>(daughters.size());
202 
203  for (auto& double_daughter : daughter_indexes) {
204  long daughter = std::lround(double_daughter);
205  if (daughter >= nDaughters)
206  return std::numeric_limits<float>::quiet_NaN();
207 
208  const MCParticle* mcdaughter = daughters[daughter]->getMCParticle();
209  if (!mcdaughter)
210  return std::numeric_limits<float>::quiet_NaN();
211 
212  sum += mcdaughter->get4Vector();
213  }
214 
215  return sum.M();
216  }
217 
218 
219  double massDifference(const Particle* particle, const std::vector<double>& daughters)
220  {
221  if (!particle)
222  return std::numeric_limits<float>::quiet_NaN();
223 
224  long daughter = std::lround(daughters[0]);
225  if (daughter >= static_cast<int>(particle->getNDaughters()))
226  return std::numeric_limits<float>::quiet_NaN();
227 
228  double motherMass = particle->getMass();
229  double daughterMass = particle->getDaughter(daughter)->getMass();
230 
231  return motherMass - daughterMass;
232  }
233 
234  double massDifferenceError(const Particle* particle, const std::vector<double>& daughters)
235  {
236  if (!particle)
237  return std::numeric_limits<float>::quiet_NaN();
238 
239  long daughter = std::lround(daughters[0]);
240  if (daughter >= static_cast<int>(particle->getNDaughters()))
241  return std::numeric_limits<float>::quiet_NaN();
242 
243  float result = 0.0;
244 
245  TLorentzVector thisDaughterMomentum = particle->getDaughter(daughter)->get4Vector();
246 
247  TMatrixFSym thisDaughterCovM(Particle::c_DimMomentum);
248  thisDaughterCovM = particle->getDaughter(daughter)->getMomentumErrorMatrix();
249  TMatrixFSym othrDaughterCovM(Particle::c_DimMomentum);
250 
251  for (int j = 0; j < int(particle->getNDaughters()); ++j) {
252  if (j == daughter)
253  continue;
254 
255  othrDaughterCovM += particle->getDaughter(j)->getMomentumErrorMatrix();
256  }
257 
258  TMatrixFSym covarianceMatrix(2 * Particle::c_DimMomentum);
259  covarianceMatrix.SetSub(0, thisDaughterCovM);
260  covarianceMatrix.SetSub(4, othrDaughterCovM);
261 
262  double motherMass = particle->getMass();
263  double daughterMass = particle->getDaughter(daughter)->getMass();
264 
265  TVectorF jacobian(2 * Particle::c_DimMomentum);
266  jacobian[0] = thisDaughterMomentum.Px() / daughterMass - particle->getPx() / motherMass;
267  jacobian[1] = thisDaughterMomentum.Py() / daughterMass - particle->getPy() / motherMass;
268  jacobian[2] = thisDaughterMomentum.Pz() / daughterMass - particle->getPz() / motherMass;
269  jacobian[3] = particle->getEnergy() / motherMass - thisDaughterMomentum.E() / daughterMass;
270  jacobian[4] = -1.0 * particle->getPx() / motherMass;
271  jacobian[5] = -1.0 * particle->getPy() / motherMass;
272  jacobian[6] = -1.0 * particle->getPz() / motherMass;
273  jacobian[7] = 1.0 * particle->getEnergy() / motherMass;
274 
275  result = jacobian * (covarianceMatrix * jacobian);
276 
277  if (result < 0.0)
278  result = 0.0;
279 
280  return TMath::Sqrt(result);
281  }
282 
283  double massDifferenceSignificance(const Particle* particle, const std::vector<double>& daughters)
284  {
285  if (!particle)
286  return std::numeric_limits<float>::quiet_NaN();
287 
288  long daughter = std::lround(daughters[0]);
289  if (daughter >= static_cast<int>(particle->getNDaughters()))
290  return std::numeric_limits<float>::quiet_NaN();
291 
292  double massDiff = massDifference(particle, daughters);
293  double massDiffErr = massDifferenceError(particle, daughters);
294 
295  double massDiffNominal = particle->getPDGMass() - particle->getDaughter(daughter)->getPDGMass();
296 
297  return (massDiff - massDiffNominal) / massDiffErr;
298  }
299 
300  // Decay Kinematics -------------------------------------------------------
301  double particleDecayAngle(const Particle* particle, const std::vector<double>& daughters)
302  {
303  if (!particle)
304  return std::numeric_limits<float>::quiet_NaN();
305 
306  PCmsLabTransform T;
307  TLorentzVector m = - T.getBeamFourMomentum();
308 
309  TLorentzVector motherMomentum = particle->get4Vector();
310  TVector3 motherBoost = -(motherMomentum.BoostVector());
311 
312  long daughter = std::lround(daughters[0]);
313  if (daughter >= static_cast<int>(particle->getNDaughters()))
314  return std::numeric_limits<float>::quiet_NaN();
315 
316  TLorentzVector daugMomentum = particle->getDaughter(daughter)->get4Vector();
317  daugMomentum.Boost(motherBoost);
318 
319  m.Boost(motherBoost);
320 
321  return daugMomentum.Angle(m.Vect());
322  }
323 
324  double pointingAngle(const Particle* particle, const std::vector<double>& daughters)
325  {
326  if (!particle)
327  return std::numeric_limits<float>::quiet_NaN();
328 
329  long daughter = std::lround(daughters[0]);
330  if (daughter >= static_cast<int>(particle->getNDaughters()))
331  return std::numeric_limits<float>::quiet_NaN();
332 
333  if (particle->getDaughter(daughter)->getNDaughters() < 2)
334  return std::numeric_limits<float>::quiet_NaN();
335 
336  TVector3 productionVertex = particle->getVertex();
337  TVector3 decayVertex = particle->getDaughter(daughter)->getVertex();
338 
339  TVector3 vertexDiffVector = decayVertex - productionVertex;
340 
341  const auto& frame = ReferenceFrame::GetCurrent();
342  TVector3 daughterMomentumVector = frame.getMomentum(particle->getDaughter(daughter)).Vect();
343 
344  return daughterMomentumVector.Angle(vertexDiffVector);
345  }
346 
347  double azimuthalAngleInDecayPlane(const Particle* particle, const std::vector<double>& daughters)
348  {
349  if (!particle)
350  return std::numeric_limits<float>::quiet_NaN();
351 
352  int nDaughters = static_cast<int>(particle->getNDaughters());
353 
354  long daughter1 = std::lround(daughters[0]);
355  long daughter2 = std::lround(daughters[1]);
356  if (daughter1 >= nDaughters || daughter2 >= nDaughters)
357  return std::numeric_limits<float>::quiet_NaN();
358 
359  PCmsLabTransform T;
360  TLorentzVector m = T.getBeamFourMomentum();
361  TLorentzVector p = particle->get4Vector();
362  TLorentzVector d1 = particle->getDaughter(daughter1)->get4Vector();
363  TLorentzVector d2 = particle->getDaughter(daughter2)->get4Vector();
364 
365  TLorentzVector l;
366  l.SetX(p.Py() * (d1.Pz() * d2.E() - d1.E() * d2.Pz()) + p.Pz() * (d1.E() * d2.Py() - d1.Py() * d2.E())
367  + p.E() * (d1.Py() * d2.Pz() - d1.Pz() * d2.Py()));
368  l.SetY(p.Px() * (d1.E() * d2.Pz() - d1.Pz() * d2.E()) + p.Pz() * (d1.Px() * d2.E() - d1.E() * d2.Px())
369  + p.E() * (d1.Pz() * d2.Px() - d1.Px() * d2.Pz()));
370  l.SetZ(p.Px() * (d1.Py() * d2.E() - d1.E() * d2.Py()) + p.Py() * (d1.E() * d2.Px() - d1.Px() * d2.E())
371  + p.E() * (d1.Px() * d2.Py() - d1.Py() * d2.Px()));
372  l.SetE(-(p.Px() * (d1.Pz() * d2.Py() - d1.Py() * d2.Pz()) + p.Py() * (d1.Px() * d2.Pz() - d1.Pz() * d2.Px())
373  + p.Pz() * (d1.Py() * d2.Px() - d1.Px() * d2.Py())));
374 
375  double m_times_p = m * p;
376  double m_times_l = m * l;
377  double m_times_d1 = m * d1;
378  double l_times_d1 = l * d1;
379  double d1_times_p = d1 * p;
380  double m_abs = TMath::Sqrt(pow(m_times_p / p.M(), 2) - m.M2());
381  double d1_abs = TMath::Sqrt(pow(d1_times_p / p.M(), 2) - d1.M2());
382  double cos_phi = -m_times_l / (m_abs * TMath::Sqrt(-l.M2()));
383  double m_parallel_abs = m_abs * TMath::Sqrt(1 - cos_phi * cos_phi);
384  double m_parallel_times_d1 = m_times_p * d1_times_p / p.M2() + m_times_l * l_times_d1 / l.M2() - m_times_d1;
385 
386  return TMath::ACos(-m_parallel_times_d1 / (m_parallel_abs * d1_abs));
387  }
388 
389  double Constant(const Particle*, const std::vector<double>& constant)
390  {
391  return constant[0];
392  }
393 
394 
395 
396  VARIABLE_GROUP("ParameterFunctions");
397  REGISTER_VARIABLE("NumberOfMCParticlesInEvent(pdgcode)", NumberOfMCParticlesInEvent , R"DOC(
398  Returns number of MC Particles (including anti-particles) with the given pdgcode in the event.
399 
400  Used in the FEI to determine to calculate reconstruction efficiencies.
401 
402  The variable is event-based and does not need a valid particle pointer as input.)DOC");
403  REGISTER_VARIABLE("isAncestorOf(i, j, ...)", isAncestorOf, R"DOC(
404  Returns a positive integer if daughter at position particle->daughter(i)->daughter(j)... is an ancestor of the related MC particle, 0 otherwise.
405 
406  Positive integer represents the number of steps needed to get from final MC daughter to ancestor.
407  If any particle or MCparticle is a nullptr, NaN is returned. If MC relations of any particle doesn't exist, -1.0 is returned.)DOC");
408  REGISTER_VARIABLE("hasAncestor(PDG, abs)", hasAncestor, R"DOC(
409 
410  Returns a positive integer if an ancestor with the given PDG code is found, 0 otherwise.
411 
412  The integer is the level where the ancestor was found, 1: first mother, 2: grandmother, etc.
413 
414  Second argument is optional, 1 means that the sign of the PDG code is taken into account, default is 0.
415 
416  If there is no MC relations found, -1 is returned. In case of nullptr particle, NaN is returned.)DOC");
417  REGISTER_VARIABLE("daughterInvariantMass(i, j, ...)", daughterInvariantMass , R"DOC(
418  Returns invariant mass of the given daughter particles. E.g.:
419 
420  * daughterInvariantMass(0, 1) returns the invariant mass of the first and second daughter.
421  * daughterInvariantMass(0, 1, 2) returns the invariant mass of the first, second and third daughter.
422 
423  Useful to identify intermediate resonances in a decay, which weren't reconstructed explicitly.
424 
425  Returns NaN if particle is nullptr or if the given daughter-index is out of bound (>= amount of daughters).)DOC");
426  REGISTER_VARIABLE("daughterMCInvariantMass(i, j, ...)", daughterMCInvariantMass ,
427  "Returns true invariant mass of the given daughter particles, same behaviour as daughterInvariantMass variable.");
428  REGISTER_VARIABLE("decayAngle(i)", particleDecayAngle,
429  "Angle in the mother's rest frame between the reverted CMS momentum vector and the direction of the i-th daughter");
430  REGISTER_VARIABLE("pointingAngle(i)", pointingAngle, R"DOC(
431  Angle between i-th daughter's momentum vector and vector connecting production and decay vertex of i-th daughter.
432  This makes only sense if the i-th daughter has itself daughter particles and therefore a properly defined vertex.)DOC");
433  REGISTER_VARIABLE("azimuthalAngleInDecayPlane(i, j)", azimuthalAngleInDecayPlane, R"DOC(
434  Azimuthal angle of i-th daughter in decay plane towards projection of particle momentum into decay plane.
435 
436  First we define the following symbols:
437 
438  * P: four-momentum vector of decaying particle in whose decay plane the azimuthal angle is measured
439  * M: "mother" of p, however not necessarily the direct mother but any higher state, here the CMS itself is chosen
440  * D1: daughter for which the azimuthal angle is supposed to be calculated
441  * D2: another daughter needed to span the decay plane
442  * L: normal to the decay plane (four-component vector)
443 
444  L can be defined via the following relation:
445 
446  .. math:: L^{\sigma} = \delta^{\sigma\nu} \epsilon_{\mu\nu\alpha\beta} P^{\mu}D1^{\alpha}D2^{\beta}
447 
448  The azimuthal angle is given by
449 
450  .. math:: \phi \equiv \cos^{-1} \left(\frac{-\vec{M_{\parallel}} \cdot \vec{D1}}{|\vec{M_{\parallel}}| \cdot |\vec{D1}|}\right)
451 
452  For a frame independent formulation the three component vectors need to be written via invariant four-momentum vectors.
453 
454  .. math::
455 
456  -\vec{M_{\parallel}} \cdot \vec{D1} &= \biggl[M - \frac{(M \cdot L)L}{L^2}\biggr] \cdot D1 - \frac{(M \cdot P)(D1 \cdot P)}{m^2_P}\\
457  |\vec{M_{\parallel}}| &= |\vec{M}| \sqrt{1 - \cos^2 \psi}\\
458  |\vec{M}| &= \sqrt{\frac{(M \cdot P)^2}{m^2_P} - m^2_M}\\
459  \cos \psi &= \frac{\vec{M} \cdot \vec{L}}{|\vec{M}| \cdot |\vec{L}|} = \frac{-M \cdot L}{|\vec{M}| \cdot \sqrt{-L^2}}\\
460  |\vec{D1}| &= \sqrt{\frac{(D1 \cdot P)^2}{m^2_P} - m^2_{D1}}
461 
462  )DOC");
463 
464  REGISTER_VARIABLE("massDifference(i)", massDifference, "Difference in invariant masses of this particle and its i-th daughter");
465  REGISTER_VARIABLE("massDifferenceError(i)", massDifferenceError,
466  "Estimated uncertainty on difference in invariant masses of this particle and its i-th daughter");
467  REGISTER_VARIABLE("massDifferenceSignificance(i)", massDifferenceSignificance,
468  "Signed significance of the deviation from the nominal mass difference of this particle and its i-th daughter [(massDiff - NOMINAL_MASS_DIFF)/ErrMassDiff]");
469 
470  REGISTER_VARIABLE("constant(float i)", Constant, R"DOC(
471  Returns i.
472 
473  Useful for debugging purposes and in conjunction with the formula meta-variable.)DOC");
474 
475  REGISTER_VARIABLE("randomChoice(i, j, ...)", RandomChoice, R"DOC(
476  Returns random element of given numbers.
477 
478  Useful for testing purposes.)DOC");
479 
480 
481  }
483 }
@ c_PrimaryParticle
bit 0: Particle is primary particle.
Definition: MCParticle.h:47
const MCParticle * getMCParticle() const
Returns the pointer to the MCParticle object that was used to create this Particle (ParticleType == c...
Definition: Particle.cc:911
static const ReferenceFrame & GetCurrent()
Get current rest frame.
Abstract base class for different kinds of events.