Belle II Software release-09-00-00
TrackVariables.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/TrackVariables.h>
11
12// include VariableManager
13#include <analysis/VariableManager/Manager.h>
14
15#include <analysis/dataobjects/Particle.h>
16#include <analysis/utility/DetectorSurface.h>
17
18// framework - DataStore
19#include <framework/datastore/StoreObjPtr.h>
20#include <framework/dataobjects/Helix.h>
21
22// dataobjects from the MDST
23#include <mdst/dbobjects/BeamSpot.h>
24#include <mdst/dataobjects/Track.h>
25#include <mdst/dataobjects/MCParticle.h>
26#include <mdst/dataobjects/TrackFitResult.h>
27#include <mdst/dataobjects/EventLevelTrackingInfo.h>
28#include <mdst/dataobjects/HitPatternVXD.h>
29#include <mdst/dataobjects/ECLCluster.h>
30
31// framework aux
32#include <framework/logging/Logger.h>
33
34#include <cmath>
35
36namespace Belle2 {
41 namespace Variable {
42
44
45 double trackNHits(const Particle* part, const Const::EDetector& det)
46 {
47 auto trackFit = part->getTrackFitResult();
48 if (!trackFit) return Const::doubleNaN;
49
50 // Before release-05 (MC13 + proc 11 and older) the hit patterns of TrackFitResults for V0s from the V0Finder were set to 0.
51 // Then, we have to take the detour via the related track to access the number of track hits.
52 if (trackFit->getHitPatternCDC().getNHits() + trackFit->getHitPatternVXD().getNdf() < 1) {
53 trackFit = part->getTrack()->getTrackFitResultWithClosestMass(Const::ChargedStable(std::abs(part->getPDGCode())));
54 }
55 if (det == Const::EDetector::CDC) {
56 return trackFit->getHitPatternCDC().getNHits();
57 } else if (det == Const::EDetector::SVD) {
58 return trackFit->getHitPatternVXD().getNSVDHits();
59 } else if (det == Const::EDetector::PXD) {
60 return trackFit->getHitPatternVXD().getNPXDHits();
61 } else {
62 return Const::doubleNaN;
63 }
64 }
65
66 double trackNCDCHits(const Particle* part)
67 {
68 return trackNHits(part, Const::EDetector::CDC);
69 }
70
71 double trackNSVDHits(const Particle* part)
72 {
73 return trackNHits(part, Const::EDetector::SVD);
74 }
75
76 double trackNPXDHits(const Particle* part)
77 {
78 return trackNHits(part, Const::EDetector::PXD);
79 }
80
81 double trackNVXDHits(const Particle* part)
82 {
83 return trackNPXDHits(part) + trackNSVDHits(part);
84 }
85
86 double trackNDF(const Particle* part)
87 {
88 auto trackFit = part->getTrackFitResult();
89 if (!trackFit) return Const::doubleNaN;
90 return trackFit->getNDF();
91 }
92
93 double trackChi2(const Particle* part)
94 {
95 auto trackFit = part->getTrackFitResult();
96 if (!trackFit) return Const::doubleNaN;
97 return trackFit->getChi2();
98 }
99
100 double trackFirstSVDLayer(const Particle* part)
101 {
102 auto trackFit = part->getTrackFitResult();
103 if (!trackFit) return Const::doubleNaN;
104 // Before release-05 (MC13 + proc 11 and older) the hit patterns of TrackFitResults for V0s from the V0Finder were set to 0.
105 // Then, we have to take the detour via the related track to access the real pattern and get the first SVD layer if available.
106 if (trackFit->getHitPatternCDC().getNHits() + trackFit->getHitPatternVXD().getNdf() < 1) {
107 trackFit = part->getTrack()->getTrackFitResultWithClosestMass(Const::ChargedStable(std::abs(part->getPDGCode())));
108 }
109 return trackFit->getHitPatternVXD().getFirstSVDLayer();
110 }
111
112 double trackFirstPXDLayer(const Particle* part)
113 {
114 auto trackFit = part->getTrackFitResult();
115 if (!trackFit) return Const::doubleNaN;
116 // Before release-05 (MC13 + proc 11 and older) the hit patterns of TrackFitResults for V0s from the V0Finder were set to 0.
117 // Then, we have to take the detour via the related track to access the real pattern and get the first PXD layer if available.
118 if (trackFit->getHitPatternCDC().getNHits() + trackFit->getHitPatternVXD().getNdf() < 1) {
119 trackFit = part->getTrack()->getTrackFitResultWithClosestMass(Const::ChargedStable(std::abs(part->getPDGCode())));
120 }
121 return trackFit->getHitPatternVXD().getFirstPXDLayer(HitPatternVXD::PXDMode::normal);
122 }
123
124 double trackFirstCDCLayer(const Particle* part)
125 {
126 auto trackFit = part->getTrackFitResult();
127 if (!trackFit) return Const::doubleNaN;
128 // Before release-05 (MC13 + proc 11 and older) the hit patterns of TrackFitResults for V0s from the V0Finder were set to 0.
129 // Then, we have to take the detour via the related track to access the real pattern and get the first CDC layer if available.
130 if (trackFit->getHitPatternCDC().getNHits() + trackFit->getHitPatternVXD().getNdf() < 1) {
131 trackFit = part->getTrack()->getTrackFitResultWithClosestMass(Const::ChargedStable(std::abs(part->getPDGCode())));
132 }
133 return trackFit->getHitPatternCDC().getFirstLayer();
134 }
135
136 double trackLastCDCLayer(const Particle* part)
137 {
138 auto trackFit = part->getTrackFitResult();
139 if (!trackFit) return Const::doubleNaN;
140 // Before release-05 (MC13 + proc 11 and older) the hit patterns of TrackFitResults for V0s from the V0Finder were set to 0.
141 // Then, we have to take the detour via the related track to access the real pattern and get the last CDC layer if available.
142 if (trackFit->getHitPatternCDC().getNHits() + trackFit->getHitPatternVXD().getNdf() < 1) {
143 trackFit = part->getTrack()->getTrackFitResultWithClosestMass(Const::ChargedStable(std::abs(part->getPDGCode())));
144 }
145 return trackFit->getHitPatternCDC().getLastLayer();
146 }
147
148 double trackD0(const Particle* part)
149 {
150 auto trackFit = part->getTrackFitResult();
151 if (!trackFit) return Const::doubleNaN;
152 return trackFit->getD0();
153 }
154
155 double trackPhi0(const Particle* part)
156 {
157 auto trackFit = part->getTrackFitResult();
158 if (!trackFit) return Const::doubleNaN;
159 return trackFit->getPhi0();
160 }
161
162 double trackOmega(const Particle* part)
163 {
164 auto trackFit = part->getTrackFitResult();
165 if (!trackFit) return Const::doubleNaN;
166 return trackFit->getOmega();
167 }
168
169 double trackZ0(const Particle* part)
170 {
171 auto trackFit = part->getTrackFitResult();
172 if (!trackFit) return Const::doubleNaN;
173 return trackFit->getZ0();
174 }
175
176 double trackTanLambda(const Particle* part)
177 {
178 auto trackFit = part->getTrackFitResult();
179 if (!trackFit) return Const::doubleNaN;
180 return trackFit->getTanLambda();
181 }
182
183 double trackD0FromIP(const Particle* part)
184 {
185 auto trackFit = part->getTrackFitResult();
186 if (!trackFit) return Const::doubleNaN;
187 static DBObjPtr<BeamSpot> beamSpotDB;
188 auto helix = trackFit->getHelix();
189 helix.passiveMoveBy(ROOT::Math::XYZVector(beamSpotDB->getIPPosition()));
190 return helix.getD0();
191 }
192
193 double trackZ0FromIP(const Particle* part)
194 {
195 auto trackFit = part->getTrackFitResult();
196 if (!trackFit) return Const::doubleNaN;
197 static DBObjPtr<BeamSpot> beamSpotDB;
198 auto helix = trackFit->getHelix();
199 helix.passiveMoveBy(ROOT::Math::XYZVector(beamSpotDB->getIPPosition()));
200 return helix.getZ0();
201 }
202
203 double trackPhi0FromIP(const Particle* part)
204 {
205 auto trackFit = part->getTrackFitResult();
206 if (!trackFit) return Const::doubleNaN;
207 static DBObjPtr<BeamSpot> beamSpotDB;
208 auto helix = trackFit->getHelix();
209 helix.passiveMoveBy(ROOT::Math::XYZVector(beamSpotDB->getIPPosition()));
210 return helix.getPhi0();
211 }
212
213 double trackD0Error(const Particle* part)
214 {
215 auto trackFit = part->getTrackFitResult();
216 if (!trackFit) return Const::doubleNaN;
217
218 double errorSquared = trackFit->getCovariance5()[0][0];
219 if (errorSquared <= 0) return Const::doubleNaN;
220 return sqrt(errorSquared);
221 }
222
223 double trackPhi0Error(const Particle* part)
224 {
225 auto trackFit = part->getTrackFitResult();
226 if (!trackFit) return Const::doubleNaN;
227
228 double errorSquared = trackFit->getCovariance5()[1][1];
229 if (errorSquared <= 0) return Const::doubleNaN;
230 return sqrt(errorSquared);
231 }
232
233 double trackOmegaError(const Particle* part)
234 {
235 auto trackFit = part->getTrackFitResult();
236 if (!trackFit) return Const::doubleNaN;
237
238 double errorSquared = trackFit->getCovariance5()[2][2];
239 if (errorSquared <= 0) return Const::doubleNaN;
240 return sqrt(errorSquared);
241 }
242
243 double trackZ0Error(const Particle* part)
244 {
245 auto trackFit = part->getTrackFitResult();
246 if (!trackFit) return Const::doubleNaN;
247
248 double errorSquared = trackFit->getCovariance5()[3][3];
249 if (errorSquared <= 0) return Const::doubleNaN;
250 return sqrt(errorSquared);
251 }
252
253 double trackTanLambdaError(const Particle* part)
254 {
255 auto trackFit = part->getTrackFitResult();
256 if (!trackFit) return Const::doubleNaN;
257
258 double errorSquared = trackFit->getCovariance5()[4][4];
259 if (errorSquared <= 0) return Const::doubleNaN;
260 return sqrt(errorSquared);
261 }
262
263 double trackFitCovariance(const Particle* particle, const std::vector<double>& indices)
264 {
265 if (indices.size() != 2) {
266 B2FATAL("Exactly two indices must be provided to the variable trackFitCovariance!");
267 }
268 if (*(std::min_element(indices.begin(), indices.end())) < 0 or *(std::max_element(indices.begin(), indices.end())) > 4) {
269 B2FATAL("The indices provided to the variable trackFitCovariance must be in the range 0 - 4!");
270 }
271 auto trackFit = particle->getTrackFitResult();
272 if (!trackFit) return Const::doubleNaN;
273 return trackFit->getCovariance5()[indices[0]][indices[1]];
274 }
275
276 double trackPValue(const Particle* part)
277 {
278 auto trackFit = part->getTrackFitResult();
279 if (!trackFit) return Const::doubleNaN;
280 return trackFit->getPValue();
281 }
282
283 double trackFitHypothesisPDG(const Particle* part)
284 {
285 auto trackFit = part->getTrackFitResult();
286 if (!trackFit) return Const::doubleNaN;
287 return trackFit->getParticleType().getPDGCode();
288 }
289
290 double trackNECLClusters(const Particle* part)
291 {
292 const Track* track = part->getTrack();
293 if (!track) return Const::doubleNaN;
294
295 // count the number of nPhotons hypothesis ecl clusters
296 int count = 0;
297 for (const ECLCluster& cluster : track->getRelationsTo<ECLCluster>())
298 if (cluster.hasHypothesis(ECLCluster::EHypothesisBit::c_nPhotons))
299 ++count;
300 return count;
301 }
302
303 // used in trackHelixExtTheta and trackHelixExtPhi
304 B2Vector3D getPositionOnHelix(const TrackFitResult* trackFit, const std::vector<double>& pars)
305 {
306 const double r = pars[0];
307 const double zfwd = pars[1];
308 const double zbwd = pars[2];
309
310 // get helix and parameters
311 const double z0 = trackFit->getZ0();
312 const double tanlambda = trackFit->getTanLambda();
313 const Helix h = trackFit->getHelix();
314
315 // extrapolate to radius
316 const double arcLength = h.getArcLength2DAtCylindricalR(r);
317 const double lHelixRadius = arcLength > 0 ? arcLength : std::numeric_limits<double>::max();
318
319 // extrapolate to FWD z
320 const double lFWD = (zfwd - z0) / tanlambda > 0 ? (zfwd - z0) / tanlambda : std::numeric_limits<double>::max();
321
322 // extrapolate to BWD z
323 const double lBWD = (zbwd - z0) / tanlambda > 0 ? (zbwd - z0) / tanlambda : std::numeric_limits<double>::max();
324
325 // pick smallest arclength
326 const double l = std::min({lHelixRadius, lFWD, lBWD});
327
328 return h.getPositionAtArcLength2D(l);
329 }
330
331 B2Vector3D getPositionOnHelix(const Particle* part, const std::vector<double>& pars)
332 {
333 if (pars.size() == 4 and pars[3]) {
334 const Track* track = part->getTrack();
335 if (!track)
336 return vecNaN;
337
338 auto highestProbMass = part->getMostLikelyTrackFitResult().first;
339 const TrackFitResult* trackFit = track->getTrackFitResultWithClosestMass(highestProbMass);
340 return getPositionOnHelix(trackFit, pars);
341 } else {
342 const TrackFitResult* trackFit = part->getTrackFitResult();
343 return getPositionOnHelix(trackFit, pars);
344 }
345 }
346
347 // returns extrapolated theta position based on helix parameters
348 double trackHelixExtTheta(const Particle* part, const std::vector<double>& pars)
349 {
350 const auto nParams = pars.size();
351 if (nParams != 3 && nParams != 4) {
352 B2FATAL("Exactly three (+1 optional) parameters (r, zfwd, zbwd, [useHighestProbMass]) required for helixExtTheta.");
353 }
354
355 B2Vector3D position = getPositionOnHelix(part, pars);
356 if (position == vecNaN) return Const::doubleNaN;
357 return position.Theta();
358 }
359
360 // returns extrapolated phi position based on helix parameters
361 double trackHelixExtPhi(const Particle* part, const std::vector<double>& pars)
362 {
363 const auto nParams = pars.size();
364 if (nParams != 3 && nParams != 4) {
365 B2FATAL("Exactly three (+1 optional) parameters (r, zfwd, zbwd, [useHighestProbMass]) required for helixExtPhi.");
366 }
367
368 B2Vector3D position = getPositionOnHelix(part, pars);
369 if (position == vecNaN) return Const::doubleNaN;
370 return position.Phi();
371 }
372
373 Manager::FunctionPtr trackHelixExtThetaOnDet(const std::vector<std::string>& arguments)
374 {
375 if (arguments.size() != 1 && arguments.size() != 2)
376 B2FATAL("Exactly one (+1 optional) parameter (detector_surface_name, [useHighestProbMass]) is required for helixExtThetaOnDet.");
377
378 std::vector<double> parameters(3);
379 const std::string det = arguments[0];
381 parameters[0] = DetectorSurface::detToSurfBoundaries.at(det).m_rho;
382 parameters[1] = DetectorSurface::detToSurfBoundaries.at(det).m_zfwd;
383 parameters[2] = DetectorSurface::detToSurfBoundaries.at(det).m_zbwd;
385 parameters[0] = DetectorSurface::detLayerToSurfBoundaries.at(det).m_rho;
386 parameters[1] = DetectorSurface::detLayerToSurfBoundaries.at(det).m_zfwd;
387 parameters[2] = DetectorSurface::detLayerToSurfBoundaries.at(det).m_zbwd;
388 } else
389 B2FATAL("Given detector surface name is not supported.");
390
391 if (arguments.size() == 2)
392 parameters.push_back(std::stod(arguments[1]));
393
394 auto func = [parameters](const Particle * part) -> double {
395
396 B2Vector3D position = getPositionOnHelix(part, parameters);
397 if (position == vecNaN) return Const::doubleNaN;
398 return position.Theta();
399 };
400 return func;
401 }
402
403 Manager::FunctionPtr trackHelixExtPhiOnDet(const std::vector<std::string>& arguments)
404 {
405 if (arguments.size() != 1 && arguments.size() != 2)
406 B2FATAL("Exactly one (+1 optional) parameter (detector_surface_name, [useHighestProbMass]) is required for helixExtPhiOnDet.");
407
408 std::vector<double> parameters(3);
409 const std::string det = arguments[0];
411 parameters[0] = DetectorSurface::detToSurfBoundaries.at(det).m_rho;
412 parameters[1] = DetectorSurface::detToSurfBoundaries.at(det).m_zfwd;
413 parameters[2] = DetectorSurface::detToSurfBoundaries.at(det).m_zbwd;
415 parameters[0] = DetectorSurface::detLayerToSurfBoundaries.at(det).m_rho;
416 parameters[1] = DetectorSurface::detLayerToSurfBoundaries.at(det).m_zfwd;
417 parameters[2] = DetectorSurface::detLayerToSurfBoundaries.at(det).m_zbwd;
418 } else
419 B2FATAL("Given detector surface name is not supported.");
420
421 if (arguments.size() == 2)
422 parameters.push_back(std::stod(arguments[1]));
423
424 auto func = [parameters](const Particle * part) -> double {
425
426 B2Vector3D position = getPositionOnHelix(part, parameters);
427 if (position == vecNaN) return Const::doubleNaN;
428 return position.Phi();
429 };
430 return func;
431 }
432
433
434 /***************************************************
435 * Event level tracking quantities
436 */
437
438 // The number of CDC hits in the event not assigned to any track
439 double nExtraCDCHits(const Particle*)
440 {
441 StoreObjPtr<EventLevelTrackingInfo> elti;
442 if (!elti) return Const::doubleNaN;
443 return elti->getNCDCHitsNotAssigned();
444 }
445
446 // The number of CDC hits in the event not assigned to any track nor very
447 // likely beam background (i.e. hits that survive a cleanup selection)
448 double nExtraCDCHitsPostCleaning(const Particle*)
449 {
450 StoreObjPtr<EventLevelTrackingInfo> elti;
451 if (!elti) return Const::doubleNaN;
452 return elti->getNCDCHitsNotAssignedPostCleaning();
453 }
454
455 // Check for the presence of a non-assigned hit in the specified CDC layer
456 double hasExtraCDCHitsInLayer(const Particle*, const std::vector<double>& layer)
457 {
458 StoreObjPtr<EventLevelTrackingInfo> elti;
459 if (!elti) return Const::doubleNaN;
460 int ilayer = std::lround(layer[0]);
461 return elti->hasCDCLayer(ilayer);
462 }
463
464 // Check for the presence of a non-assigned hit in the specified CDC SuperLayer
465 double hasExtraCDCHitsInSuperLayer(const Particle*, const std::vector<double>& layer)
466 {
467 StoreObjPtr<EventLevelTrackingInfo> elti;
468 if (!elti) return Const::doubleNaN;
469 int ilayer = std::lround(layer[0]);
470 return elti->hasCDCSLayer(ilayer);
471 }
472
473 // The number of segments that couldn't be assigned to any track
474 double nExtraCDCSegments(const Particle*)
475 {
476 StoreObjPtr<EventLevelTrackingInfo> elti;
477 if (!elti) return Const::doubleNaN;
478 return elti->getNCDCSegments();
479 }
480
481 // The number of VXD hits not assigned to any track in the specified layer
482 double nExtraVXDHitsInLayer(const Particle*, const std::vector<double>& layer)
483 {
484 StoreObjPtr<EventLevelTrackingInfo> elti;
485 if (!elti) return Const::doubleNaN;
486 int ilayer = std::lround(layer[0]);
487 return elti->getNVXDClustersInLayer(ilayer);
488 }
489
490 // The number of VXD hits not assigned to any track
491 double nExtraVXDHits(const Particle*)
492 {
493 StoreObjPtr<EventLevelTrackingInfo> elti;
494 if (!elti) return Const::doubleNaN;
495 double out = 0.0;
496 for (uint16_t ilayer = 1; ilayer < 7; ++ilayer)
497 out += elti->getNVXDClustersInLayer(ilayer);
498 return out;
499 }
500
501 // time of first SVD sample relative to event T0
502 double svdFirstSampleTime(const Particle*)
503 {
504 StoreObjPtr<EventLevelTrackingInfo> elti;
505 if (!elti) return Const::doubleNaN;
506 return elti->getSVDFirstSampleTime();
507 }
508
509 // A flag set by the tracking if there is reason to assume there was a track
510 // in the event missed by the tracking or the track finding was (partly) aborted
511 // for this event. Further information about this can be obtained from the flagBlock
512 // of the EventLevelTrackingInfo object.
513 double trackFindingFailureFlag(const Particle*)
514 {
515 StoreObjPtr<EventLevelTrackingInfo> elti;
516 if (!elti) return Const::doubleNaN;
517 return elti->hasAnErrorFlag();
518 }
519
520 // Build a Helix from a MCParticle's kinematics. Does NOT check for null pointer!!!
521 Belle2::Helix getMCHelix(const MCParticle* mcparticle)
522 {
523 const ROOT::Math::XYZVector mcProdVertex = mcparticle->getVertex();
524 const ROOT::Math::XYZVector mcMomentum = mcparticle->getMomentum();
525 const double BzAtProdVertex = Belle2::BFieldManager::getFieldInTesla(mcProdVertex).Z();
526 const double mcParticleCharge = mcparticle->getCharge();
527 return Belle2::Helix(mcProdVertex, mcMomentum, mcParticleCharge, BzAtProdVertex);
528 }
529
530 double getMCHelixParameterAtIndex(const Particle* particle, const int index)
531 {
532 if (!particle) return Const::doubleNaN;
533
534 const MCParticle* mcparticle = particle->getMCParticle();
535 if (!mcparticle) return Const::doubleNaN;
536
537 const Belle2::Helix mcHelix(getMCHelix(mcparticle));
538 const std::vector<double> mcHelixPars{mcHelix.getD0(), mcHelix.getPhi0(), mcHelix.getOmega(), mcHelix.getZ0(), mcHelix.getTanLambda()};
539 return mcHelixPars.at(index);
540 }
541
542 double getHelixParameterPullAtIndex(const Particle* particle, const int index)
543 {
544 if (!particle) return Const::doubleNaN;
545
546 const MCParticle* mcparticle = particle->getMCParticle();
547 if (!mcparticle) return Const::doubleNaN;
548
549 const Belle2::TrackFitResult* trackfit = particle->getTrackFitResult();
550 if (!trackfit) return Const::doubleNaN;
551
552 const Belle2::UncertainHelix measHelix = trackfit->getUncertainHelix();
553 const TMatrixDSym& measCovariance = measHelix.getCovariance();
554 const Belle2::Helix mcHelix(getMCHelix(mcparticle));
555
556 const std::vector<double> mcHelixPars = {mcHelix.getD0(), mcHelix.getPhi0(), mcHelix.getOmega(), mcHelix.getZ0(), mcHelix.getTanLambda()};
557 const std::vector<double> measHelixPars = {measHelix.getD0(), measHelix.getPhi0(), measHelix.getOmega(), measHelix.getZ0(), measHelix.getTanLambda()};
558
559 return (mcHelixPars.at(index) - measHelixPars.at(index)) / std::sqrt(measCovariance(index, index));
560 }
561
562 double getHelixMCD0(const Particle* part) { return getMCHelixParameterAtIndex(part, 0); }
563 double getHelixMCPhi0(const Particle* part) { return getMCHelixParameterAtIndex(part, 1); }
564 double getHelixMCOmega(const Particle* part) { return getMCHelixParameterAtIndex(part, 2); }
565 double getHelixMCZ0(const Particle* part) { return getMCHelixParameterAtIndex(part, 3); }
566 double getHelixMCTanLambda(const Particle* part) { return getMCHelixParameterAtIndex(part, 4); }
567
568 double getHelixD0Pull(const Particle* part)
569 {
570 return getHelixParameterPullAtIndex(part, 0);
571 }
572
573 double getHelixPhi0Pull(const Particle* part)
574 {
575 return getHelixParameterPullAtIndex(part, 1);
576 }
577
578 double getHelixOmegaPull(const Particle* part)
579 {
580 return getHelixParameterPullAtIndex(part, 2);
581 }
582
583 double getHelixZ0Pull(const Particle* part)
584 {
585 return getHelixParameterPullAtIndex(part, 3);
586 }
587
588 double getHelixTanLambdaPull(const Particle* part)
589 {
590 return getHelixParameterPullAtIndex(part, 4);
591 }
592
593 double getTrackTime(const Particle* part)
594 {
595 const Track* track = part->getTrack();
596 if (!track) return Const::doubleNaN;
597 return track->getTrackTime();
598 }
599
600 double isTrackFlippedAndRefitted(const Particle* part)
601 {
602 auto track = part->getTrack();
603 if (!track) return Const::doubleNaN;
604 return track->isFlippedAndRefitted() ? 1 : 0;
605 }
606
607 double getTrackLength(const Particle* part)
608 {
609 auto trackFit = part->getTrackFitResult();
610 if (!trackFit) return Const::doubleNaN;
611
612 const double lastCDCLayer = trackLastCDCLayer(part);
613 if (std::isnan(lastCDCLayer) or lastCDCLayer < 0)
614 return Const::doubleNaN;
615
616 const double r = DetectorSurface::cdcWireRadiuses.at((int)lastCDCLayer);
617
618 return trackFit->getHelix().getArcLength2DAtCylindricalR(r);
619 }
620
621
622 VARIABLE_GROUP("Tracking");
623
624 REGISTER_VARIABLE("mcD0", getHelixMCD0, R"DOC(
625Returns the MC value of :math:`d_0`, the signed distance to the
626point-of-closest-approach (POCA) in the :math:`r-\phi` plane.
627
628.. seealso:: :b2:var:`d0`
629
630Returns NaN if the particle is not related to any MCParticle.
631
632)DOC", "cm");
633 REGISTER_VARIABLE("mcPhi0", getHelixMCPhi0, R"DOC(
634Returns the MC value of :math:`\phi_0`, the angle of the transverse momentum
635in the :math:`r-\phi` plane.
636
637.. seealso:: :b2:var:`phi0`
638
639Returns NaN if the particle is not related to any MCParticle.
640
641)DOC", "rad");
642 REGISTER_VARIABLE("mcOmega", getHelixMCOmega, R"DOC(
643Returns the MC value of :math:`\omega`, the curvature of the track.
644
645.. seealso:: :b2:var:`omega`
646
647Returns NaN if the particle is not related to any MCParticle.
648
649)DOC", ":math:`\\text{cm}^{-1}`");
650 REGISTER_VARIABLE("mcZ0", getHelixMCZ0, R"DOC(
651Returns the MC value of :math:`z_0`, the z-coordinate of the
652point-of-closest-approach (POCA).
653
654.. seealso:: :b2:var:`z0`
655
656Returns NaN if the particle is not related to any MCParticle.
657
658)DOC", "cm");
659 REGISTER_VARIABLE("mcTanLambda", getHelixMCTanLambda, R"DOC(
660Returns the MC value of :math:`\tan\lambda`, the slope of the track in the
661:math:`r-z` plane.
662
663.. seealso:: :b2:var:`tanLambda`
664
665Returns NaN if the particle is not related to any MCParticle.
666)DOC");
667
668 REGISTER_VARIABLE("d0Pull", getHelixD0Pull, R"DOC(
669The pull of the tracking parameter :math:`d_0` for the reconstructed
670pattern-recognition track, with respect to the MC track. That is:
671
672.. math::
673
674 \frac{d_0^\textrm{MC} - d_0^\textrm{PR}}{\sigma_{d_0; \textrm{PR}}}
675
676.. seealso:: :b2:var:`d0`, :b2:var:`d0Err`
677
678Returns NaN if no MC particle is related or if called on something other than a
679track-based particle.
680 )DOC");
681 REGISTER_VARIABLE("phi0Pull", getHelixPhi0Pull, R"DOC(
682The pull of the tracking parameter :math:`\phi_0` for the reconstructed
683pattern-recognition track, with respect to the MC track. That is:
684
685.. math::
686
687 \frac{\phi_0^\textrm{MC} - \phi_0^\textrm{PR}}{\sigma_{\phi_0; \textrm{PR}}}
688
689.. seealso:: :b2:var:`phi0`, :b2:var:`phi0Err`
690
691Returns NaN if no MC particle is related or if called on something other than a
692track-based particle.
693 )DOC");
694 REGISTER_VARIABLE("omegaPull", getHelixOmegaPull, R"DOC(
695The pull of the tracking parameter :math:`\omega` for the reconstructed
696pattern-recognition track, with respect to the MC track. That is:
697
698.. math::
699
700 \frac{\omega^\textrm{MC} - \omega^\textrm{PR}}{\sigma_{\omega; \textrm{PR}}}
701
702.. seealso:: :b2:var:`omega`, :b2:var:`omegaErr`
703
704Returns NaN if no MC particle is related or if called on something other than a
705track-based particle.
706 )DOC");
707 REGISTER_VARIABLE("z0Pull", getHelixZ0Pull, R"DOC(
708The pull of the tracking parameter :math:`z_0` for the reconstructed
709pattern-recognition track, with respect to the MC track. That is:
710
711.. math::
712
713 \frac{z_0^\textrm{MC} - z_0^\textrm{PR}}{\sigma_{z_0; \textrm{PR}}}
714
715.. seealso:: :b2:var:`z0`, :b2:var:`z0Err`
716
717Returns NaN if no MC particle is related or if called on something other than a
718track-based particle.
719 )DOC");
720 REGISTER_VARIABLE("tanLambdaPull", getHelixTanLambdaPull, R"DOC(
721The pull of the tracking parameter :math:`\tan\lambda` for the reconstructed
722pattern-recognition track, with respect to the MC track. That is:
723
724.. math::
725
726 \frac{(\tan\lambda)^\textrm{MC} - (\tan\lambda)^\textrm{PR}}{\sigma_{\tan\lambda; \textrm{PR}}}
727
728.. seealso:: :b2:var:`tanLambda`, :b2:var:`tanLambdaErr`
729
730Returns NaN if no MC particle is related or if called on something other than a
731track-based particle.
732 )DOC");
733 REGISTER_VARIABLE("nCDCHits", trackNCDCHits,
734 "The number of CDC hits associated to the track. Returns NaN if called for something other than a track-based particle.");
735 REGISTER_VARIABLE("nSVDHits", trackNSVDHits,
736 "The number of SVD hits associated to the track. Returns NaN if called for something other than a track-based particle.");
737 REGISTER_VARIABLE("nPXDHits", trackNPXDHits,
738 "The number of PXD hits associated to the track. Returns NaN if called for something other than a track-based particle.");
739 REGISTER_VARIABLE("nVXDHits", trackNVXDHits,
740 "The number of PXD and SVD hits associated to the track. Returns NaN if called for something other than a track-based particle.");
741 REGISTER_VARIABLE("ndf", trackNDF, R"DOC(
742Returns the number of degrees of freedom of the track fit.
743
744.. note::
745
746 Note that this is not simply the number of hits -5 due to outlier hit
747 rejection.
748
749Returns NaN if called for something other than a track-based particle, or for
750mdst files processed with basf2 versions older than ``release-05-01``.
751 )DOC");
752 REGISTER_VARIABLE("chi2", trackChi2, R"DOC(
753Returns the :math:`\chi^2` of the track fit. This is actually computed based on
754:b2:var:`pValue` and :b2:var:`ndf`.
755
756.. note:: Note that for :b2:var:`pValue` exactly equal to 0 it returns infinity.
757
758Returns NaN if called for something other than a track-based particle, or for
759mdst files processed with basf2 versions older than ``release-05-01``.
760 )DOC");
761 REGISTER_VARIABLE("firstSVDLayer", trackFirstSVDLayer,
762 "The first activated SVD layer associated to the track. Returns NaN if called for something other than a track-based particle.");
763 REGISTER_VARIABLE("firstPXDLayer", trackFirstPXDLayer,
764 "The first activated PXD layer associated to the track. Returns NaN if called for something other than a track-based particle.");
765 REGISTER_VARIABLE("firstCDCLayer", trackFirstCDCLayer,
766 "The first activated CDC layer associated to the track. Returns NaN if called for something other than a track-based particle.");
767 REGISTER_VARIABLE("lastCDCLayer", trackLastCDCLayer,
768 "The last CDC layer associated to the track. Returns NaN if called for something other than a track-based particle.");
769 REGISTER_VARIABLE("d0", trackD0, R"DOC(
770Returns the tracking parameter :math:`d_0`, the signed distance to the
771point-of-closest-approach (POCA) in the :math:`r-\phi` plane.
772
773.. note::
774
775 Tracking parameters are with respect to the origin (0,0,0). For the
776 POCA with respect to the measured beam interaction point, see
777 :b2:var:`dr` (you probably want this unless you're doing a tracking
778 study or some debugging) and :b2:var:`d0FromIP`.
779
780Returns NaN if called for something other than a track-based particle.
781
782)DOC", "cm");
783 REGISTER_VARIABLE("phi0", trackPhi0, R"DOC(
784Returns the tracking parameter :math:`\phi_0`, the angle of the transverse
785momentum in the :math:`r-\phi` plane.
786
787.. note::
788
789 Tracking parameters are with respect to the origin (0,0,0). For the
790 POCA with respect to the measured beam interaction point, see
791 :b2:var:`phi0FromIP`.
792
793Returns NaN if called for something other than a track-based particle.
794
795)DOC", "rad");
796 REGISTER_VARIABLE("omega", trackOmega, R"DOC(
797Returns the tracking parameter :math:`\omega`, the curvature of the track.
798
799Returns NaN if called for something other than a track-based particle.
800
801)DOC", ":math:`\\text{cm}^{-1}`");
802 REGISTER_VARIABLE("z0", trackZ0, R"DOC(
803Returns the tracking parameter :math:`z_0`, the z-coordinate of the
804point-of-closest-approach (POCA).
805
806.. note::
807
808 Tracking parameters are with respect to the origin (0,0,0). For the
809 POCA with respect to the measured beam interaction point, see
810 :b2:var:`dz` (you probably want this unless you're doing a tracking
811 study or some debugging) and :b2:var:`z0FromIP`.
812
813Returns NaN if called for something other than a track-based particle.
814
815)DOC", "cm");
816 REGISTER_VARIABLE("tanLambda", trackTanLambda, R"DOC(
817Returns :math:`\tan\lambda`, the slope of the track in the :math:`r-z` plane.
818
819Returns NaN if called for something other than a track-based particle.
820 )DOC");
821 REGISTER_VARIABLE("d0FromIP", trackD0FromIP, R"DOC(
822Returns the tracking parameter :math:`d_0`, the signed distance to the
823point-of-closest-approach (POCA) in the :math:`r-\phi` plane, with respect to the measured beam interaction point.
824
825Returns NaN if called for something other than a track-based particle.
826
827)DOC", "cm");
828 REGISTER_VARIABLE("z0FromIP", trackZ0FromIP, R"DOC(
829Returns the tracking parameter :math:`z_0`, the z-coordinate of the
830point-of-closest-approach (POCA), with respect to the measured beam interaction point.
831
832Returns NaN if called for something other than a track-based particle.
833
834)DOC", "cm");
835 REGISTER_VARIABLE("phi0FromIP", trackPhi0FromIP, R"DOC(
836Returns the tracking parameter :math:`\phi_0`, the angle of the transverse
837momentum in the :math:`r-\phi` plane, with respect to the measured beam interaction point.
838
839Returns NaN if called for something other than a track-based particle.
840
841)DOC", "rad");
842 REGISTER_VARIABLE("d0Err", trackD0Error, R"DOC(
843Returns the uncertainty on :math:`d_0`, the signed distance to the
844point-of-closest-approach (POCA) in the :math:`r-\phi` plane.
845
846.. seealso:: :b2:var:`d0`, :b2:var:`d0Pull`
847
848Returns NaN if called for something other than a track-based particle.
849
850)DOC", "cm");
851 REGISTER_VARIABLE("phi0Err", trackPhi0Error, R"DOC(
852Returns the uncertainty on :math:`\phi_0`, the angle of the transverse momentum
853in the :math:`r-\phi` plane.
854
855.. seealso:: :b2:var:`phi0`, :b2:var:`phi0Pull`
856
857Returns NaN if called for something other than a track-based particle.
858
859)DOC", "rad");
860 REGISTER_VARIABLE("omegaErr", trackOmegaError, R"DOC(
861Returns the uncertainty on :math:`\omega`, the curvature of the track.
862
863.. seealso:: :b2:var:`omega`, :b2:var:`omegaPull`
864
865Returns NaN if called for something other than a track-based particle.
866
867)DOC", ":math:`\\text{cm}^{-1}`");
868 REGISTER_VARIABLE("z0Err", trackZ0Error, R"DOC(
869Returns the uncertainty on :math:`z_0`, the z-coordinate of the
870point-of-closest-approach (POCA).
871
872.. seealso:: :b2:var:`z0`, :b2:var:`z0Pull`
873
874Returns NaN if called for something other than a track-based particle."
875
876)DOC", "cm");
877 REGISTER_VARIABLE("tanLambdaErr", trackTanLambdaError, R"DOC(
878Returns the uncertainty on :math:`\tan\lambda`, the slope of the track in the
879:math:`r-z` plane.
880
881.. seealso:: :b2:var:`tanLambda`, :b2:var:`tanLambdaPull`
882
883Returns NaN if called for something other than a track-based particle.
884 )DOC");
885 REGISTER_VARIABLE("trackFitCovariance(i, j)", trackFitCovariance, R"DOC(
886 The track fit covariance matrix element corresponding to the two indices is returned.
887 This is the association between integers and parameters:
888
889 * 0: :math:`d_0`
890 * 1: :math:`\phi_0`
891 * 2: :math:`\omega`
892 * 3: :math:`z_0`
893 * 4: :math:`\tan\lambda`
894
895 .. note::
896
897 The covariance is returned. This means that the return value can be negative.
898 Furthermore, it's the squared value of the track fit error variables :b2:var:`d0Err`, etc.
899 when selecting the diagonal entries.
900
901 )DOC");
902 REGISTER_VARIABLE("pValue", trackPValue, R"DOC(
903The :math:`\chi^2` probability of the **track** fit.
904
905.. note::
906
907 This is the p-value of the track-fit. It does not get updated after
908 vertex fitting or kinematic fitting, and is meaningless for composite
909 particles.
910
911 See :b2:var:`chiProb` (you probably want this for high-level analysis).
912
913Returns NaN if called for something other than a track-based particle.
914 )DOC");
915 REGISTER_VARIABLE("trackFitHypothesisPDG", trackFitHypothesisPDG, R"DOC(
916Returns the PDG code of the track hypothesis actually used for the fit.
917Returns NaN if called for something other than a track-based particle.
918 )DOC");
919 REGISTER_VARIABLE("trackNECLClusters", trackNECLClusters, R"DOC(
920Returns a count of the number of ECLClusters matched to the track. This is
921always 0 or 1 with newer versions of ECL reconstruction.
922
923.. note::
924
925 For high-level analysis it is recommended to require the presence of a
926 matched ECL cluster along with a minimum energy requirement. A
927 track-based particle will have a clusterE if it is matched (NaN if
928 there is no cluster match for the track.
929
930 .. code-block:: python
931
932 import modularAnalysis as ma
933 # minimum energy of 200 MeV
934 ma.fillParticleList("e+:clusters", "clusterE > 0.2", path)
935
936 # these two are equivalent
937 ma.fillParticleList("e+:unmatched", "isNAN(clusterE) == 1", path)
938 ma.fillParticleList("e+:unmatched2", "trackNECLClusters == 0", path)
939
940Returns NaN if called for something other than a track-based particle.
941 )DOC");
942 REGISTER_VARIABLE("helixExtTheta(radius [cm], z fwd [cm], z bwd [cm], useHighestProbMass=0)", trackHelixExtTheta,
943 R"DOC(Returns theta of extrapolated helix parameters. If ``useHighestProbMass=1`` is set, the extrapolation will
944 use the track fit result for the mass hypothesis with the highest pValue.
945
946 )DOC", "rad");
947 REGISTER_VARIABLE("helixExtPhi(radius, z fwd, z bwd, useHighestProbMass=0)", trackHelixExtPhi,
948 "Returns phi of extrapolated helix parameters. If ``useHighestProbMass=1`` is set, the extrapolation will use the track fit result for the mass hypothesis with the highest pValue.\n\n",
949 "rad");
950
951 REGISTER_METAVARIABLE("helixExtThetaOnDet(detector_surface_name, useHighestProbMass=0)", trackHelixExtThetaOnDet,
952 R"DOC(Returns theta of extrapolated helix parameters on the given detector surface. The unit of angle is ``rad``.
953 If ``useHighestProbMass=1`` is set, the extrapolation will use the track fit result for the mass hypothesis with the highest pValue.
954 The supported detector surface names are ``{'CDC', 'TOP', 'ARICH', 'ECL', 'KLM'}``.
955 Also, the detector name with number of meaningful-layer is supported, e.g. ``'CDC8'``: last superlayer of CDC, ``'ECL1'``: mid-point of ECL.
956
957 ..note:: You can find more information in `modularAnalysis.calculateTrackIsolation`.
958 )DOC", Manager::VariableDataType::c_double);
959 REGISTER_METAVARIABLE("helixExtPhiOnDet(detector_surface_name, useHighestProbMass=0)", trackHelixExtPhiOnDet,
960 R"DOC(Returns phi of extrapolated helix parameters on the given detector surface. The unit of angle is ``rad``.
961 If ``useHighestProbMass=1`` is set, the extrapolation will use the track fit result for the mass hypothesis with the highest pValue.
962 The supported detector surface names are ``{'CDC', 'TOP', 'ARICH', 'ECL', 'KLM'}``.
963 Also, the detector name with number of meaningful-layer is supported, e.g. ``'CDC8'``: last superlayer of CDC, ``'ECL1'``: mid-point of ECL.
964
965 ..note:: You can find more information in `modularAnalysis.calculateTrackIsolation`.
966 )DOC", Manager::VariableDataType::c_double);
967
968
969 REGISTER_VARIABLE("nExtraCDCHits", nExtraCDCHits, R"DOC(
970[Eventbased] The number of CDC hits in the event not assigned to any track.
971
972Returns NaN if there is no event-level tracking information available.
973 )DOC");
974 REGISTER_VARIABLE("nExtraCDCHitsPostCleaning", nExtraCDCHitsPostCleaning, R"DOC(
975[Eventbased] Returns a count of the number of CDC hits in the event not assigned
976to any track nor very likely beam background (i.e. hits that survive a cleanup
977selection).
978
979Returns NaN if there is no event-level tracking information available.
980 )DOC");
981 REGISTER_VARIABLE("hasExtraCDCHitsInLayer(i)", hasExtraCDCHitsInLayer, R"DOC(
982[Eventbased] Returns 1 if a non-assigned hit exists in the specified CDC layer,
9830 otherwise.
984
985Returns NaN if there is no event-level tracking information available.
986 )DOC");
987 REGISTER_VARIABLE("hasExtraCDCHitsInSuperLayer(i)", hasExtraCDCHitsInSuperLayer, R"DOC(
988[Eventbased] Returns 1 if a non-assigned hit exists in the specified CDC
989SuperLayer, 0 otherwise.
990
991Returns NaN if there is no event-level tracking information available.
992 )DOC");
993 REGISTER_VARIABLE("nExtraCDCSegments", nExtraCDCSegments, R"DOC(
994[Eventbased] Returns the number of CDC segments not assigned to any track.
995
996Returns NaN if there is no event-level tracking information available.
997 )DOC");
998 // TODO: once the Tracking group fill the dataobject these can be
999 // uncommented - at the moment they are not filled, so leave out
1000 //REGISTER_VARIABLE("nExtraVXDHitsInLayer(i)", nExtraVXDHitsInLayer,
1001 //"[Eventbased] The number VXD hits not assigned in the specified VXD layer");
1002 //REGISTER_VARIABLE("nExtraVXDHits", nExtraVXDHits, "[Eventbased] The number of VXD hits not assigned to any track");
1003 //REGISTER_VARIABLE("svdFirstSampleTime", svdFirstSampleTime, "[Eventbased] The time of first SVD sample relatvie to event T0");
1004 REGISTER_VARIABLE("trackFindingFailureFlag", trackFindingFailureFlag, R"DOC(
1005[Eventbased] Returns a flag set by the tracking if there is reason to assume
1006there was a track in the event missed by the tracking, or the track finding was
1007(partly) aborted for this event.
1008
1009Returns NaN if there is no event-level tracking information available.
1010 )DOC");
1011
1012 REGISTER_VARIABLE("isTrackFlippedAndRefitted", isTrackFlippedAndRefitted, R"DOC(
1013Returns 1 if the charged final state particle comes from a track that has been flipped and refitted
1014at the end of the reconstruction chain, in particular after the outer detector reconstruction.
1015 )DOC");
1016
1017 REGISTER_VARIABLE("trackTime", getTrackTime, R"DOC(
1018Returns the time at which the track is produced relative to the time of the collision (given by SVD EventT0).
1019Both the time of the collision and the track time are computed using only SVD hits.
1020Returns NaN if SVD EventT0 is NaN, or if no SVD Hits are attached to the track.
1021For more details, see :ref:`Time Extraction <tracking_eventTimeExtraction>` page.
1022
1023)DOC", "ns");
1024
1025 REGISTER_VARIABLE("trackLength", getTrackLength, R"DOC(
1026Returns the arc length of the helix for the TrackFitResult associated with the particle.
1027The arc length is measured from the track origin to the radius of the CDC layer in which the Track has a hit.
1028Returns NaN if the particle has no CDC Hits.
1029
1030)DOC", "cm");
1031
1032
1033 }
1035}
static ROOT::Math::XYZVector getFieldInTesla(const ROOT::Math::XYZVector &pos)
return the magnetic field at a given position in Tesla.
Definition: BFieldManager.h:61
Helix parameter class.
Definition: Helix.h:48
EDetector
Enum for identifying the detector components (detector and subdetector).
Definition: Const.h:42
static const double doubleNaN
quiet_NaN
Definition: Const.h:703
@ c_nPhotons
CR is split into n photons (N1)
Values of the result of a track fit with a given particle hypothesis.
UncertainHelix getUncertainHelix() const
Conversion to framework Uncertain Helix (i.e., with covariance).
This class represents an ideal helix in perigee parameterization including the covariance matrix of t...
const TMatrixDSym & getCovariance() const
Getter for covariance matrix of perigee parameters in matrix form.
std::function< VarVariant(const Particle *)> FunctionPtr
functions stored take a const Particle* and return VarVariant.
Definition: Manager.h:113
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:516
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
Abstract base class for different kinds of events.
static const std::unordered_map< int, double > cdcWireRadiuses
CDC sense wire radiuses Values are take from cdc/data/CDC.xml.
static const std::unordered_map< std::string, DetSurfCylBoundaries > detToSurfBoundaries
Map that associates to each detector its valid cylindrical surface's boundaries.
static const std::unordered_map< std::string, DetSurfCylBoundaries > detLayerToSurfBoundaries
Map that associates to each detector layer its valid cylindrical surface's boundaries.