Belle II Software  release-06-01-15
MCRecoTracksMatcherModule.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 #include <tracking/modules/mcMatcher/MCRecoTracksMatcherModule.h>
9 
10 // datastore types
11 #include <framework/datastore/StoreArray.h>
12 
13 #include <framework/gearbox/Const.h>
14 
15 #include <tracking/dataobjects/RecoTrack.h>
16 #include <mdst/dataobjects/MCParticle.h>
17 
18 // hit types
19 #include <pxd/dataobjects/PXDCluster.h>
20 #include <svd/dataobjects/SVDCluster.h>
21 #include <cdc/dataobjects/CDCHit.h>
22 
23 #include <map>
24 
25 #include <Eigen/Dense>
26 
27 /* disables the false positive warning of Intel icc about iter_pair_range::epmty() being unused
28 tracking/modules/mcMatcher/src/MCRecoTracksMatcherModule.cc:55: warning #177: function
29 "::iter_pair_range::empty [with Iter=std::_Rb_tree_const_iterator::DetId={Belle2::Const::EDetector},
30 ::HitId={int}>, ::WeightedRecoTrackId>>]" was declared but never referenced
31 bool empty() const
32 */
33 #ifdef __INTEL_COMPILER
34 #pragma warning disable 177
35 #endif
36 
37 using namespace Belle2;
38 
39 REG_MODULE(MCRecoTracksMatcher);
40 
41 namespace {
42  // small anonymous helper construct making converting a pair of iterators usable
43  // with range based for
44  template <class Iter>
45  struct iter_pair_range : std::pair<Iter, Iter> {
46  explicit iter_pair_range(std::pair<Iter, Iter> const& x)
47  : std::pair<Iter, Iter>(x)
48  {
49  }
50 
51  Iter begin() const
52  {
53  return this->first;
54  }
55 
56  Iter end() const
57  {
58  return this->second;
59  }
60 
61  bool empty() const
62  {
63  return begin() == end();
64  }
65  };
66 
67  template <class Iter>
68  inline iter_pair_range<Iter> as_range(std::pair<Iter, Iter> const& x)
69  {
70  return iter_pair_range<Iter>(x);
71  }
72 
73  using DetId = Const::EDetector;
74  using HitId = int;
75  using RecoTrackId = int;
76  struct WeightedRecoTrackId {
77  operator int() const { return id; }
78  RecoTrackId id;
79  double weight;
80  };
81 
82  using DetHitIdPair = std::pair<DetId, HitId>;
83 
84  struct CompDetHitIdPair {
85  bool operator()(const std::pair<DetId, HitId>& lhs,
86  const std::pair<std::pair<DetId, HitId>, WeightedRecoTrackId>& rhs)
87  {
88  return lhs < rhs.first;
89  }
90 
91  bool operator()(const std::pair<std::pair<DetId, HitId>, WeightedRecoTrackId>& lhs,
92  const std::pair<DetId, HitId>& rhs)
93  {
94  return lhs.first < rhs;
95  }
96  };
97 
98  // anonymous helper function to fill a set or a map with the hit IDs and det IDs (we need both a set or a map in the following).
99  template <class AMapOrSet>
100  void fillIDsFromStoreArray(AMapOrSet& recoTrackID_by_hitID,
101  const StoreArray<RecoTrack>& storedRecoTracks)
102  {
103  RecoTrackId recoTrackId = -1;
104  for (const RecoTrack& recoTrack : storedRecoTracks) {
105  ++recoTrackId;
106  std::vector<std::pair<DetHitIdPair, WeightedRecoTrackId> > hitIDsInTrack;
107  double totalWeight = 0;
108  using OriginTrackFinder = RecoHitInformation::OriginTrackFinder;
109  const OriginTrackFinder c_MCTrackFinderAuxiliaryHit =
110  OriginTrackFinder::c_MCTrackFinderAuxiliaryHit;
111 
112  for (const RecoHitInformation::UsedCDCHit* cdcHit : recoTrack.getCDCHitList()) {
113  OriginTrackFinder originFinder = recoTrack.getFoundByTrackFinder(cdcHit);
114  double weight = originFinder == c_MCTrackFinderAuxiliaryHit ? 0 : 1;
115  totalWeight += weight;
116  hitIDsInTrack.push_back({{Const::CDC, cdcHit->getArrayIndex()}, {recoTrackId, weight}});
117  }
118  for (const RecoHitInformation::UsedSVDHit* svdHit : recoTrack.getSVDHitList()) {
119  OriginTrackFinder originFinder = recoTrack.getFoundByTrackFinder(svdHit);
120  double weight = originFinder == c_MCTrackFinderAuxiliaryHit ? 0 : 1;
121  totalWeight += weight;
122  hitIDsInTrack.push_back({{Const::SVD, svdHit->getArrayIndex()}, {recoTrackId, weight}});
123  }
124  for (const RecoHitInformation::UsedPXDHit* pxdHit : recoTrack.getPXDHitList()) {
125  OriginTrackFinder originFinder = recoTrack.getFoundByTrackFinder(pxdHit);
126  double weight = originFinder == c_MCTrackFinderAuxiliaryHit ? 0 : 1;
127  totalWeight += weight;
128  hitIDsInTrack.push_back({{Const::PXD, pxdHit->getArrayIndex()}, {recoTrackId, weight}});
129  }
130 
131  // In case all hits are auxiliary for a track - reset all weights to 1
132  if (totalWeight == 0) {
133  for (std::pair<DetHitIdPair, WeightedRecoTrackId>& recoTrack_for_hitID : hitIDsInTrack) {
134  recoTrack_for_hitID.second.weight = 1;
135  }
136  }
137 
138  // Commit to output
139  typename AMapOrSet::iterator itInsertHint = recoTrackID_by_hitID.end();
140  for (std::pair<DetHitIdPair, WeightedRecoTrackId>& recoTrack_for_hitID : hitIDsInTrack) {
141  itInsertHint = recoTrackID_by_hitID.insert(itInsertHint, recoTrack_for_hitID);
142  }
143  }
144  }
145 }
146 
148  : Module()
149 {
150  setDescription("This module compares reconstructed tracks generated by some pattern recognition "
151  "algorithm for PXD, SVD and/or CDC to ideal Monte Carlo tracks and performs a "
152  "matching from the former to the underlying MCParticles.");
153 
155 
156  //Parameter definition
157  // Inputs
158  addParam("prRecoTracksStoreArrayName",
160  "Name of the collection containing the tracks as generate a patter recognition algorithm to be evaluated ",
161  std::string(""));
162 
163  addParam("mcRecoTracksStoreArrayName",
165  "Name of the collection containing the reference tracks as generate by a Monte-Carlo-Tracker (e.g. MCTrackFinder)",
166  std::string("MCGFTrackCands"));
167 
168  // Hit content to be evaluated
169  addParam("UsePXDHits",
171  "Set true if PXDHits or PXDClusters should be used in the matching in case they are present",
172  true);
173 
174  addParam("UseSVDHits",
176  "Set true if SVDHits or SVDClusters should be used in the matching in case they are present",
177  true);
178 
179  addParam("UseCDCHits",
181  "Set true if CDCHits should be used in the matching in case they are present",
182  true);
183 
184  addParam("UseOnlyAxialCDCHits",
186  "Set true if only the axial CDCHits should be used",
187  false);
188 
189  addParam("MinimalPurity",
191  "Minimal purity of a PRTrack to be considered matchable to a MCTrack. "
192  "This number encodes how many correct hits are minimally need to compensate for a false hits. "
193  "The default 2.0 / 3.0 suggests that for each background hit can be compensated by two correct hits.",
194  2.0 / 3.0);
195 
196  addParam("MinimalEfficiency",
198  "Minimal efficiency of a MCTrack to be considered matchable to a PRTrack. "
199  "This number encodes which fraction of the true hits must at least be in the reconstructed track. "
200  "The default 0.05 suggests that at least 5% of the true hits should have been picked up.",
201  0.05);
202 }
203 
205 {
206  // Check if there are MC Particles
207  StoreArray<MCParticle> storeMCParticles;
208 
209  if (storeMCParticles.isOptional()) {
210  m_mcParticlesPresent = true;
211 
212  // Actually retrieve the StoreArrays
215 
216  // Require both RecoTrack arrays and the MCParticles to be present in the DataStore
217  storePRRecoTracks.isRequired();
218  storeMCRecoTracks.isRequired();
219  storeMCParticles.isRequired();
220 
221  // Extract the default names for the case empty stings were given
222  m_param_prRecoTracksStoreArrayName = storePRRecoTracks.getName();
223  m_param_mcRecoTracksStoreArrayName = storeMCRecoTracks.getName();
224 
225  // Purity relation - for each PRTrack to store the purest MCTrack
226  storePRRecoTracks.registerRelationTo(storeMCRecoTracks);
227 
228  // Efficiency relation - for each MCTrack to store the most efficient PRTrack
229  storeMCRecoTracks.registerRelationTo(storePRRecoTracks);
230 
231  // MCParticle matching relation - purity
232  storePRRecoTracks.registerRelationTo(storeMCParticles);
233 
234  // MCParticle matching relation - efficiency
235  storeMCParticles.registerRelationTo(storePRRecoTracks);
236 
237  // Announce optional store arrays to the hits or clusters in case they should be used
238  // We make them optional in case of limited detector setup.
239  // PXD
240  if (m_param_usePXDHits) {
241  StoreArray<PXDCluster> pxdClusters;
242  pxdClusters.isOptional();
243  }
244 
245  // SVD
246  if (m_param_useSVDHits) {
247  StoreArray<SVDCluster> svdClusters;
248  svdClusters.isOptional();
249  }
250 
251  // CDC
252  if (m_param_useCDCHits) {
253  StoreArray<CDCHit> cdcHits;
254  cdcHits.isOptional();
255  }
256  }
257 }
258 
260 {
261  // Skip in the case there are no MC particles present.
262  if (not m_mcParticlesPresent) {
263  B2DEBUG(100, "Skipping MC Track Matcher as there are no MC Particles registered in the DataStore.");
264  return;
265  }
266 
267  B2DEBUG(100, "########## MCRecoTracksMatcherModule ############");
268 
269  // Fetch store array
272  StoreArray<MCParticle> mcParticles("");
273 
274  int nMCRecoTracks = mcRecoTracks.getEntries();
275  int nPRRecoTracks = prRecoTracks.getEntries();
276 
277  B2DEBUG(100, "Number patter recognition tracks is " << nPRRecoTracks);
278  B2DEBUG(100, "Number Monte-Carlo tracks is " << nMCRecoTracks);
279 
280  if (not nMCRecoTracks or not nPRRecoTracks) {
281  // Neither no pattern recognition tracks
282  // or no Monte Carlo tracks are present in this event
283  // Cannot perform matching.
284  return;
285  }
286 
287  // ### Build a detector_id hit_id to reco track map for easier lookup later ###
288  std::multimap<DetHitIdPair, WeightedRecoTrackId > mcId_by_hitId;
289  fillIDsFromStoreArray(mcId_by_hitId, mcRecoTracks);
290 
291  // Use set instead of multimap to handel to following situation
292  // * One hit may be assigned to multiple tracks should contribute to the efficiency of both tracks
293  // * One hit assigned twice or more to the same track should not contribute to the purity multiple times
294  // The first part is handled well by the multimap. But to enforce that one hit is also only assigned
295  // once to a track we use a set.
296  std::set<std::pair<DetHitIdPair, WeightedRecoTrackId>> prId_by_hitId;
297  fillIDsFromStoreArray(prId_by_hitId, prRecoTracks);
298 
299  // ### Get the number of relevant hits for each detector ###
300  // Since we are mostly dealing with indices in this module, this is all we need from the StoreArray
301  // Silently skip store arrays that are not present in reduced detector setups.
302  std::map<DetId, int> nHits_by_detId;
303 
304  // PXD
305  if (m_param_usePXDHits) {
306  StoreArray<PXDCluster> pxdClusters;
307  if (pxdClusters.isOptional()) {
308  int nHits = pxdClusters.getEntries();
309  nHits_by_detId[Const::PXD] = nHits;
310  }
311  }
312 
313  // SVD
314  if (m_param_useSVDHits) {
315  StoreArray<SVDCluster> svdClusters;
316  if (svdClusters.isOptional()) {
317  int nHits = svdClusters.getEntries();
318  nHits_by_detId[Const::SVD] = nHits;
319  }
320  }
321 
322  // CDC
323  if (m_param_useCDCHits) {
324  StoreArray<CDCHit> cdcHits;
325  if (cdcHits.isOptional()) {
326  nHits_by_detId[Const::CDC] = cdcHits.getEntries();
327  }
328  }
329 
330  //### Build the confusion matrix ###
331 
332  // Reserve enough space for the confusion matrix
333  // The last row is meant for hits not assigned to a mcRecoTrack (aka background hits)
334  Eigen::MatrixXd confusionMatrix = Eigen::MatrixXd::Zero(nPRRecoTracks, nMCRecoTracks + 1);
335  Eigen::MatrixXd weightedConfusionMatrix = Eigen::MatrixXd::Zero(nPRRecoTracks, nMCRecoTracks + 1);
336 
337  // Accumulated the total number of hits/ndf for each Monte-Carlo track separately to avoid double counting,
338  // in case patter recognition tracks share hits.
339  Eigen::RowVectorXd totalNDF_by_mcId = Eigen::RowVectorXd::Zero(nMCRecoTracks + 1);
340  Eigen::RowVectorXd totalWeight_by_mcId = Eigen::RowVectorXd::Zero(nMCRecoTracks + 1);
341 
342  // Accumulated the total number of hits/ndf for each pattern recognition track separately to avoid double counting,
343  // in case Monte-Carlo tracks share hits.
344  Eigen::VectorXd totalNDF_by_prId = Eigen::VectorXd::Zero(nPRRecoTracks);
345 
346  // Column index for the hits not assigned to any MCRecoTrack
347  const int mcBkgId = nMCRecoTracks;
348 
349  // for each detector examine every hit to which mcRecoTrack and prRecoTrack it belongs
350  // if the hit is not part of any mcRecoTrack put the hit in the background column.
351  for (const std::pair<const DetId, NDF>& detId_nHits_pair : nHits_by_detId) {
352 
353  DetId detId = detId_nHits_pair.first;
354  int nHits = detId_nHits_pair.second;
355  NDF ndfForOneHit = m_ndf_by_detId[detId];
356 
357  for (HitId hitId = 0; hitId < nHits; ++hitId) {
358  DetHitIdPair detId_hitId_pair(detId, hitId);
359 
360  if (m_param_useOnlyAxialCDCHits and detId == Const::CDC) {
361  StoreArray<CDCHit> cdcHits;
362  const CDCHit* cdcHit = cdcHits[hitId];
363  if (cdcHit->getISuperLayer() % 2 != 0) {
364  // Skip stereo hits
365  continue;
366  }
367  }
368 
369  // Seek all Monte Carlo tracks with the given hit
370  const auto mcIds_for_detId_hitId_pair =
371  as_range(mcId_by_hitId.equal_range(detId_hitId_pair));
372 
373  // Seek all pattern recognition tracks with the given hit
374  const auto prIds_for_detId_hitId_pair =
375  as_range(std::equal_range(prId_by_hitId.begin(),
376  prId_by_hitId.end(),
377  detId_hitId_pair,
378  CompDetHitIdPair()));
379 
380  // Assign the hits/ndf to the total ndf vector separately to avoid double counting,
381  // if patter recognition track share hits.
382  if (mcIds_for_detId_hitId_pair.empty()) {
383  // If the hit is not assigned to any mcRecoTrack
384  // The hit is assigned to the background column
385  RecoTrackId mcId = mcBkgId;
386  double mcWeight = 1;
387  totalNDF_by_mcId(mcId) += ndfForOneHit;
388  totalWeight_by_mcId(mcId) += ndfForOneHit * mcWeight;
389  } else {
390  for (const auto& detId_hitId_pair_and_mcId : mcIds_for_detId_hitId_pair) {
391  WeightedRecoTrackId mcId = detId_hitId_pair_and_mcId.second;
392  double mcWeight = mcId.weight;
393  totalNDF_by_mcId(mcId) += ndfForOneHit;
394  totalWeight_by_mcId(mcId) += ndfForOneHit * mcWeight;
395  }
396  }
397 
398  // Assign the hits/ndf to the total ndf vector separately here to avoid double counting,
399  // if Monte-Carlo track share hits.
400  for (const auto& detId_hitId_pair_and_prId : prIds_for_detId_hitId_pair) {
401  RecoTrackId prId = detId_hitId_pair_and_prId.second;
402  totalNDF_by_prId(prId) += ndfForOneHit;
403  }
404 
405  // Fill the confusion matrix
406  for (const auto& detId_hitId_pair_and_prId : prIds_for_detId_hitId_pair) {
407  RecoTrackId prId = detId_hitId_pair_and_prId.second;
408  if (mcIds_for_detId_hitId_pair.empty()) {
409  RecoTrackId mcId = mcBkgId;
410  double mcWeight = 1;
411  confusionMatrix(prId, mcId) += ndfForOneHit;
412  weightedConfusionMatrix(prId, mcId) += ndfForOneHit * mcWeight;
413  } else {
414  for (const auto& detId_hitId_pair_and_mcId : mcIds_for_detId_hitId_pair) {
415  WeightedRecoTrackId mcId = detId_hitId_pair_and_mcId.second;
416  double mcWeight = mcId.weight;
417  confusionMatrix(prId, mcId) += ndfForOneHit;
418  weightedConfusionMatrix(prId, mcId) += ndfForOneHit * mcWeight;
419  }
420  }
421  } // end for
422  } // end for hitId
423  } // end for detId
424 
425  B2DEBUG(200, "Confusion matrix of the event : " << std::endl << confusionMatrix);
426  B2DEBUG(200, "Weighted confusion matrix of the event : " << std::endl << weightedConfusionMatrix);
427 
428  B2DEBUG(200, "totalNDF_by_mcId : " << std::endl << totalNDF_by_mcId);
429  B2DEBUG(200, "totalWeight_by_mcId : " << std::endl << totalWeight_by_mcId);
430 
431  B2DEBUG(200, "totalNDF_by_prId : " << std::endl << totalNDF_by_prId);
432 
433  Eigen::MatrixXd purityMatrix = confusionMatrix.array().colwise() / totalNDF_by_prId.array();
434  Eigen::MatrixXd efficiencyMatrix = confusionMatrix.array().rowwise() / totalNDF_by_mcId.array();
435  Eigen::MatrixXd weightedEfficiencyMatrix = weightedConfusionMatrix.array().rowwise() / totalWeight_by_mcId.array();
436 
437  B2DEBUG(100, "Purities");
438  B2DEBUG(100, purityMatrix);
439 
440  B2DEBUG(100, "Efficiencies");
441  B2DEBUG(100, efficiencyMatrix);
442 
443  B2DEBUG(100, "Weighted efficiencies");
444  B2DEBUG(100, weightedEfficiencyMatrix);
445 
446  // ### Building the Monte-Carlo track to highest efficiency patter recognition track relation ###
447  // Weighted efficiency
448  using Efficiency = float;
449  using Purity = float;
450 
451  struct MostWeightEfficientPRId {
452  RecoTrackId id;
453  Efficiency weightedEfficiency;
454  // cppcheck-suppress unusedStructMember
455  Efficiency efficiency;
456  };
457  std::vector<MostWeightEfficientPRId> mostWeightEfficientPRId_by_mcId(nMCRecoTracks);
458  for (RecoTrackId mcId = 0; mcId < nMCRecoTracks; ++mcId) {
459  Eigen::VectorXd efficiencyCol = efficiencyMatrix.col(mcId);
460  Eigen::VectorXd weightedEfficiencyCol = weightedEfficiencyMatrix.col(mcId);
461 
462  RecoTrackId bestPrId = 0;
463  Efficiency bestWeightedEfficiency = weightedEfficiencyCol(0);
464  Efficiency bestEfficiency = efficiencyCol(0);
465  Purity bestPurity = purityMatrix.row(0)(mcId);
466 
467  // Reject efficiency smaller than the minimal one
468  if (bestWeightedEfficiency < m_param_minimalEfficiency) {
469  bestWeightedEfficiency = 0;
470  }
471 
472  // In case of a tie in the weighted efficiency we use the regular efficiency to break it.
473  for (RecoTrackId prId = 1; prId < nPRRecoTracks; ++prId) {
474  Eigen::RowVectorXd purityRow = purityMatrix.row(prId);
475 
476  Efficiency currentWeightedEfficiency = weightedEfficiencyCol(prId);
477  Efficiency currentEfficiency = efficiencyCol(prId);
478  Purity currentPurity = purityRow(mcId);
479 
480  // Reject efficiency smaller than the minimal one
481  if (currentWeightedEfficiency < m_param_minimalEfficiency) {
482  currentWeightedEfficiency = 0;
483  }
484 
485  if (std::tie(currentWeightedEfficiency, currentEfficiency, currentPurity) >
486  std::tie(bestWeightedEfficiency, bestEfficiency, bestPurity)) {
487  bestPrId = prId;
488  bestEfficiency = currentEfficiency;
489  bestWeightedEfficiency = currentWeightedEfficiency;
490  bestPurity = currentPurity;
491  }
492  }
493 
494  bestWeightedEfficiency = weightedEfficiencyCol(bestPrId);
495  bestEfficiency = efficiencyCol(bestPrId);
496  mostWeightEfficientPRId_by_mcId[mcId] = {bestPrId, bestWeightedEfficiency, bestEfficiency};
497  }
498 
499  // ### Building the patter recognition track to highest purity Monte-Carlo track relation ###
500  // Unweighted purity
501  struct MostPureMCId {
502  RecoTrackId id;
503  Purity purity;
504  };
505 
506  std::vector<MostPureMCId> mostPureMCId_by_prId(nPRRecoTracks);
507  for (int prId = 0; prId < nPRRecoTracks; ++prId) {
508  Eigen::RowVectorXd purityRow = purityMatrix.row(prId);
509 
510  int mcId;
511  Purity highestPurity = purityRow.maxCoeff(&mcId);
512 
513  mostPureMCId_by_prId[prId] = {mcId, highestPurity};
514  }
515 
516  // Log the Monte-Carlo track to highest efficiency patter recognition track relation
517  // Weighted efficiency
518  {
519  RecoTrackId mcId = -1;
520  B2DEBUG(200, "MCTrack to highest weighted efficiency PRTrack relation");
521  for (const auto& mostWeightEfficientPRId_for_mcId : mostWeightEfficientPRId_by_mcId) {
522  ++mcId;
523  const Efficiency& weightedEfficiency = mostWeightEfficientPRId_for_mcId.weightedEfficiency;
524  const RecoTrackId& prId = mostWeightEfficientPRId_for_mcId.id;
525  B2DEBUG(200,
526  "mcId : " << mcId << " -> prId : " << prId << " with weighted efficiency "
527  << weightedEfficiency);
528  }
529  }
530 
531  // Log the patter recognition track to highest purity Monte-Carlo track relation
532  // Unweighted purity
533  {
534  RecoTrackId prId = -1;
535  B2DEBUG(200, "PRTrack to highest purity MCTrack relation");
536  for (const auto& mostPureMCId_for_prId : mostPureMCId_by_prId) {
537  ++prId;
538  const RecoTrackId& mcId = mostPureMCId_for_prId.id;
539  const Purity& purity = mostPureMCId_for_prId.purity;
540  B2DEBUG(200, "prId : " << prId << " -> mcId : " << mcId << " with purity " << purity);
541  }
542  }
543 
544  // Count the categories
545  int nMatched{}, nBackground{}, nClones{}, nGhost{};
546 
547  // ### Classify the patter recognition tracks ###
548  // Means saving the highest purity relation to the data store
549  // + setup the PRTrack to MCParticle relation
550  // + save the set the MatchingStatus
551  for (RecoTrackId prId = 0; prId < nPRRecoTracks; ++prId) {
552  RecoTrack* prRecoTrack = prRecoTracks[prId];
553 
554  const MostPureMCId& mostPureMCId = mostPureMCId_by_prId[prId];
555 
556  const RecoTrackId& mcId = mostPureMCId.id;
557  const Purity& purity = mostPureMCId.purity;
558 
559  // GHOST
560  if (not(purity > 0) or not(purity >= m_param_minimalPurity)) {
561  prRecoTrack->setMatchingStatus(RecoTrack::MatchingStatus::c_ghost);
562 
563  B2DEBUG(100, "Stored PRTrack " << prId << " as ghost because of too low purity");
564  continue;
565  }
566 
567  // BACKGROUND
568  if (mcId == mcBkgId) {
569  prRecoTrack->setMatchingStatus(RecoTrack::MatchingStatus::c_background);
570 
571  B2DEBUG(100, "Stored PRTrack " << prId << " as background because of too low purity.");
572  ++nBackground;
573  continue;
574  }
575 
576  // After the classification for bad purity and background we examine,
577  // whether the highest purity Monte-Carlo track has in turn the highest efficiency
578  // patter recognition track equal to this track.
579  // All extra patter recognition tracks are marked as clones.
580 
581  RecoTrack* mcRecoTrack = mcRecoTracks[mcId];
582  MCParticle* mcParticle = mcRecoTrack->getRelated<MCParticle>();
583  B2ASSERT("No relation from MCRecoTrack to MCParticle.", mcParticle);
584 
585  const MostWeightEfficientPRId& mostWeightEfficientPRId_for_mcId =
586  mostWeightEfficientPRId_by_mcId[mcId];
587 
588  const RecoTrackId& mostWeightEfficientPRId = mostWeightEfficientPRId_for_mcId.id;
589  const Efficiency& weightedEfficiency = mostWeightEfficientPRId_for_mcId.weightedEfficiency;
590  // const Efficiency& efficiency = mostWeightEfficientPRId_for_mcId.efficiency;
591 
592  // Note : The matched category may also contain higher order clones recognisable by their low
593  // absolute efficiency
594 
595  // MATCHED
596  if (prId == mostWeightEfficientPRId) {
597  // Setup the relation purity relation
598  prRecoTrack->setMatchingStatus(RecoTrack::MatchingStatus::c_matched);
599  prRecoTrack->addRelationTo(mcRecoTrack, purity);
600 
601  // Add the mc matching relation
602  prRecoTrack->addRelationTo(mcParticle, purity);
603 
604  B2DEBUG(100, "Stored PRTrack " << prId << " as matched.");
605  B2DEBUG(100, "MC Match prId " << prId << " to mcPartId " << mcParticle->getArrayIndex());
606  B2DEBUG(100, "Purity rel: prId " << prId << " -> mcId " << mcId << " : " << purity);
607  ++nMatched;
608  continue;
609  }
610 
611  // GHOST
612  // Pattern recognition track fails the minimal efficiency requirement to be matched.
613  // We might want to introduce a different classification here, if we see problems
614  // with too many ghosts and want to investigate the specific source of the mismatch.
615  if (not(weightedEfficiency >= m_param_minimalEfficiency)) {
616  prRecoTrack->setMatchingStatus(RecoTrack::MatchingStatus::c_ghost);
617  B2DEBUG(100, "Stored PRTrack " << prId << " as ghost because of too low efficiency.");
618  ++nGhost;
619  continue;
620  }
621 
622  // Final category
623  // CLONE
624  // Setup the relation purity relation
625  prRecoTrack->setMatchingStatus(RecoTrack::MatchingStatus::c_clone);
626  prRecoTrack->addRelationTo(mcRecoTrack, -purity);
627  prRecoTrack->addRelationTo(mcParticle, -purity);
628  ++nClones;
629 
630  // Add the mc matching relation to the mc particle
631  B2DEBUG(100, "Stored PRTrack " << prId << " as clone.");
632  B2DEBUG(100, "MC Match prId " << prId << " to mcPartId " << mcParticle->getArrayIndex());
633  B2DEBUG(100, "Purity rel: prId " << prId << " -> mcId " << mcId << " : " << -purity);
634  } // end for prId
635 
636 
637  B2DEBUG(100, "Number of matches " << nMatched);
638  B2DEBUG(100, "Number of clones " << nClones);
639  B2DEBUG(100, "Number of bkg " << nBackground);
640  B2DEBUG(100, "Number of ghost " << nGhost);
641 
642  // ### Classify the Monte-Carlo tracks ###
643  // Meaning save the highest weighted efficiency relation to the data store.
644  for (RecoTrackId mcId = 0; mcId < nMCRecoTracks; ++mcId) {
645  RecoTrack* mcRecoTrack = mcRecoTracks[mcId];
646  MCParticle* mcParticle = mcRecoTrack->getRelated<MCParticle>();
647 
648  const MostWeightEfficientPRId& mostWeightEfficiencyPRId = mostWeightEfficientPRId_by_mcId[mcId];
649 
650  const RecoTrackId& prId = mostWeightEfficiencyPRId.id;
651  const Efficiency& weightedEfficiency = mostWeightEfficiencyPRId.weightedEfficiency;
652  // const Efficiency& efficiency = mostWeightEfficiencyPRId.efficiency;
653 
654  B2ASSERT("Index of pattern recognition tracks out of range.", prId < nPRRecoTracks and prId >= 0);
655 
656  RecoTrack* prRecoTrack = prRecoTracks[prId];
657 
658  const MostPureMCId& mostPureMCId_for_prId = mostPureMCId_by_prId[prId];
659  const RecoTrackId& mostPureMCId = mostPureMCId_for_prId.id;
660 
661  // MATCHED
662  if (mcId == mostPureMCId and
663  (prRecoTrack->getMatchingStatus() == RecoTrack::MatchingStatus::c_matched or
664  prRecoTrack->getMatchingStatus() == RecoTrack::MatchingStatus::c_clone)) {
665  // Setup the relation with positive weighted efficiency for this case.
666  mcRecoTrack->addRelationTo(prRecoTrack, weightedEfficiency);
667  mcParticle->addRelationTo(prRecoTrack, weightedEfficiency);
668  B2DEBUG(100, "Efficiency rel: mcId " << mcId << " -> prId " << prId << " : " << weightedEfficiency);
669  continue;
670  }
671 
672  // MERGED
673  // This MCTrack has a significant portion of hits in a PRTrack
674  // which in turn better describes a MCTrack different form this.
675  // Setup the relation with negative weighted efficiency for this case.
676  bool isMergedMCRecoTrack =
677  weightedEfficiency >= m_param_minimalEfficiency and
678  (prRecoTrack->getMatchingStatus() == RecoTrack::MatchingStatus::c_matched or
679  prRecoTrack->getMatchingStatus() == RecoTrack::MatchingStatus::c_clone);
680 
681  if (isMergedMCRecoTrack) {
682  mcRecoTrack->addRelationTo(prRecoTrack, -weightedEfficiency);
683  mcParticle->addRelationTo(prRecoTrack, -weightedEfficiency);
684  B2DEBUG(100, "Efficiency rel: mcId " << mcId << " -> prId " << prId << " : " << -weightedEfficiency);
685  continue;
686  }
687 
688  // MISSING
689  // No related pattern recognition track
690  // Do not create a relation.
691  B2DEBUG(100, "mcId " << mcId << " is missing. No relation created.");
692  B2DEBUG(100, "is Primary" << mcRecoTracks[mcId]->getRelatedTo<MCParticle>()->isPrimaryParticle());
693  B2DEBUG(100, "best prId " << prId << " with purity " << mostPureMCId_for_prId.purity << " -> " << mostPureMCId);
694  B2DEBUG(100, "MC Total ndf " << totalNDF_by_mcId[mcId]);
695  B2DEBUG(100, "MC Total weight" << totalWeight_by_mcId[mcId]);
696  B2DEBUG(100, "MC Overlap ndf\n " << confusionMatrix.col(mcId).transpose());
697  B2DEBUG(100, "MC Overlap weight\n " << weightedConfusionMatrix.col(mcId).transpose());
698  B2DEBUG(100, "MC Efficiencies for the track\n" << efficiencyMatrix.col(mcId).transpose());
699  B2DEBUG(100, "MC Weighted efficiencies for the track\n" << weightedEfficiencyMatrix.col(mcId).transpose());
700  } // end for mcId
701 
702  B2DEBUG(100, "########## End MCRecoTracksMatcherModule ############");
703 
704 } //end event()
Class containing the result of the unpacker in raw data and the result of the digitizer in simulation...
Definition: CDCHit.h:40
unsigned short getISuperLayer() const
Getter for iSuperLayer.
Definition: CDCHit.h:184
EDetector
Enum for identifying the detector components (detector and subdetector).
Definition: Const.h:42
A Class to store the Monte Carlo particle information.
Definition: MCParticle.h:32
int getArrayIndex() const
Get 0-based index of the particle in the corresponding MCParticle list.
Definition: MCParticle.h:244
double m_param_minimalPurity
Parameter : Minimal purity of a PRTrack to be considered matchable to a MCTrack.
double m_param_minimalEfficiency
Parameter : Minimal efficiency for a MCTrack to be considered matchable to a PRTrack.
void initialize() final
Signal the beginning of the event processing.
int NDF
Descriptive type defintion for a number of degrees of freedom.
bool m_mcParticlesPresent
Flag to indicated whether the Monte Carlo track are on the DataStore.
bool m_param_useSVDHits
Parameter : Switch whether SVDHits should be used in the matching.
MCRecoTracksMatcherModule()
Constructor setting up the parameter.
std::string m_param_prRecoTracksStoreArrayName
Parameter : RecoTracks store array name from the patter recognition.
void event() final
Process the event.
bool m_param_useOnlyAxialCDCHits
Parameter : Switch whether only axial CDCHits should be used.
bool m_param_usePXDHits
Parameter : Switch whether PXDHits should be used in the matching.
std::string m_param_mcRecoTracksStoreArrayName
Parameter : RecoTracks store array name from the mc recognition.
std::map< int, NDF > m_ndf_by_detId
Map storing the standard number degrees of freedom for a single hit by detector *‍/.
bool m_param_useCDCHits
Parameter : Switch whether CDCHits should be used in the matching.
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
The PXD Cluster class This class stores all information about reconstructed PXD clusters The position...
Definition: PXDCluster.h:30
OriginTrackFinder
The TrackFinder which has added the hit to the track.
This is the Reconstruction Event-Data Model Track.
Definition: RecoTrack.h:76
void setMatchingStatus(MatchingStatus matchingStatus)
Set the matching status (used by the TrackMatcher module)
Definition: RecoTrack.h:723
MatchingStatus getMatchingStatus() const
Return the matching status set by the TrackMatcher module.
Definition: RecoTrack.h:717
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
T * getRelated(const std::string &name="", const std::string &namedRelation="") const
Get the object to or from which this object has a relation.
The SVD Cluster class This class stores all information about reconstructed SVD clusters.
Definition: SVDCluster.h:28
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.
bool isOptional(const std::string &name="")
Tell the DataStore about an optional input.
const std::string & getName() const
Return name under which the object is saved in the DataStore.
Accessor to arrays stored in the data store.
Definition: StoreArray.h:113
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition: StoreArray.h:140
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.