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