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