Belle II Software  release-05-01-25
hit_module.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 import math
5 import collections
6 import numpy as np
7 
8 # Need for B2WARNING for some reason
9 import inspect
10 
11 from .pull import PullAnalysis
12 from .fom import (
13  ValidationFiguresOfMerit,
14  ValidationManyFiguresOfMerit
15 )
16 
17 from .module import (
18  AlwaysPassFilter,
19  getHelixFromMCParticle,
20  TrackingValidationModule
21 )
22 
23 import basf2
24 
25 import ROOT
26 
27 ROOT.gSystem.Load('libtracking')
28 from ROOT import Belle2
29 
30 # FIXME: define hash function for TrackCand to be able to add it to set. This is
31 # not really correct, it just checks for the address and normally if a==b also
32 # hash(a) == hash(b) is required
33 ROOT.genfit.TrackCand.__hash__ = lambda x: id(x)
34 
35 
37 
38  """Module to collect more matching information about the found particles and to generate validation
39  plots and figures of merit on the performance of track finding. This module gives information on the
40  number of hits etc. """
41 
42  def __init__(
43  self,
44  name,
45  contact,
46  fit=False,
47  pulls=False,
48  resolution=False,
49  output_file_name=None,
50  track_filter_object=AlwaysPassFilter(),
51  plot_name_postfix='',
52  plot_title_postfix='',
53  exclude_profile_mc_parameter='',
54  exclude_profile_pr_parameter='',
55  use_expert_folder=True,
56  trackCandidatesColumnName='RecoTracks',
57  mcTrackCandidatesColumnName='MCRecoTracks',
58  cdcHitsColumnName='CDCHits',
59  write_tables=False):
60  """Constructor"""
61 
62  TrackingValidationModule.__init__(
63  self,
64  name,
65  contact,
66  fit,
67  pulls,
68  resolution,
69  output_file_name,
70  track_filter_object,
71  plot_name_postfix,
72  plot_title_postfix,
73  exclude_profile_mc_parameter,
74  exclude_profile_pr_parameter,
75  use_expert_folder,
76  trackCandidatesColumnName,
77  mcTrackCandidatesColumnName)
78 
79 
80  self.cdcHitsColumnname = cdcHitsColumnName
81 
82  self.write_tables = write_tables
83 
84  def initialize(self):
85  """Receive signal at the start of event processing"""
86  TrackingValidationModule.initialize(self)
87 
88  # Use deques in favour of lists to prevent repeated memory allocation of cost O(n)
89 
90  self.number_of_total_hits = collections.deque()
91 
92  self.number_of_mc_hits = collections.deque()
93 
94  self.number_of_pr_hits = collections.deque()
95 
96  self.is_hit_found = collections.deque()
97 
98  self.is_hit_matched = collections.deque()
99 
100  # MC information
101 
102  self.mc_missing = collections.deque()
103 
105 
106  self.ratio_hits_in_mc_tracks_and_in_pr_tracks = collections.deque()
107 
109 
111  collections.deque()
112 
114  collections.deque()
115 
116  self.mc_is_primary = collections.deque()
117 
118  self.mc_number_of_hits = collections.deque()
119 
120  # PT information
121 
122  self.number_of_connected_tracks = collections.deque()
123 
124  self.number_of_wrong_hits = collections.deque()
125  # It is calculated by going through all hits of the fake track and the connected mc track cands and counting the number.
126  # These numbers are than summed up and substracted by the biggest number
127  # of hits this candidates shares with the mc track cands.
128 
129  self.pr_number_of_hits = collections.deque()
130 
131  self.pr_number_of_matched_hits = collections.deque()
132 
133  def event(self):
134  """Event method"""
135 
136  TrackingValidationModule.event(self)
137  self.examine_hits_in_event()
138 
140  """Classify all of the hits in the event according to the parent track(s)"""
141 
145  # No CDC hits available, hit analysis incomplete, don't perform
146  # hit analysis
147  return
148  cdcHits = Belle2.PyStoreArray(self.cdcHitsColumnname)
149 
150  # # CDC Hits in MC tracks
151  totalHitListMC = []
152  for mcTrackCand in mcTrackCands:
153  cdcHitIDs = [cdcHit.getArrayIndex() for cdcHit in mcTrackCand.getCDCHitList()] # Checked
154  # Working around a bug in ROOT where you should not access empty std::vectors
155  if len(cdcHitIDs) == 0:
156  cdcHitIDs = set()
157  else:
158  cdcHitIDs = set(cdcHitIDs)
159  totalHitListMC.extend(cdcHitIDs)
160 
161  # Make the ids unique
162  totalHitListMC = set(totalHitListMC)
163 
164  # # CDC Hits in PR tracks
165  totalHitListPR = []
166  totalHitListPRGood = []
167  totalHitListPRClone = []
168  totalHitListPRFake = []
169  for trackCand in trackCands:
170  if trackCand.getNumberOfTotalHits() == 0:
171  basf2.B2WARNING("Encountered a pattern recognition track with no hits")
172  continue
173 
174  cdcHitIDs = [cdcHit.getArrayIndex() for cdcHit in trackCand.getCDCHitList()] # Checked
175  # Working around a bug in ROOT where you should not access empty std::vectors
176  if len(cdcHitIDs) == 0:
177  cdcHitIDs = set()
178  else:
179  cdcHitIDs = set(cdcHitIDs)
180 
181  totalHitListPR.extend(cdcHitIDs)
182  if self.trackMatchLookUp.isMatchedPRRecoTrack(trackCand):
183  totalHitListPRGood.extend(cdcHitIDs)
184 
185  if self.trackMatchLookUp.isClonePRRecoTrack(trackCand):
186  totalHitListPRClone.extend(cdcHitIDs)
187 
188  if (self.trackMatchLookUp.isBackgroundPRRecoTrack(trackCand) or
189  self.trackMatchLookUp.isBackgroundPRRecoTrack(trackCand)):
190  totalHitListPRFake.extend(cdcHitIDs)
191 
192  # Make the ids unique
193  totalHitListPR = set(totalHitListPR)
194  totalHitListPRGood = set(totalHitListPRGood)
195  totalHitListPRClone = set(totalHitListPRClone)
196  totalHitListPRFake = set(totalHitListPRFake)
197 
198  # # All CDC Hits
199  totalHitList = set([cdcHit.getArrayIndex() for cdcHit in cdcHits])
200 
201  number_of_mc_hits = len(totalHitListMC)
202  number_of_pr_hits = len(totalHitListPR)
203  number_of_all_hits = len(totalHitList)
204 
205  is_hit_matched = 0
206  is_hit_found = len(totalHitListMC & totalHitListPR)
207 
208  for trackCand in trackCands:
209 
210  is_matched = self.trackMatchLookUp.isMatchedPRRecoTrack(trackCand)
211  is_clone = self.trackMatchLookUp.isClonePRRecoTrack(trackCand)
212 
213  trackCandHits = [cdcHit.getArrayIndex() for cdcHit in trackCand.getCDCHitList()]
214  # Working around a bug in ROOT where you should not access empty std::vectors
215  if len(trackCandHits) == 0:
216  trackCandHits = set()
217  else:
218  trackCandHits = set(trackCandHits)
219 
220  # this is not very efficient...
221  list_of_connected_mc_tracks = set()
222  list_of_numbers_of_hits_for_connected_tracks = collections.deque()
223  number_of_connected_tracks = 0
224  number_of_wrong_hits = 0
225 
226  for mcTrackCand in mcTrackCands:
227  mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in mcTrackCand.getCDCHitList()]
228  # Working around a bug in ROOT where you should not access empty std::vectors
229  if len(mcTrackCandHits) == 0:
230  mcTrackCandHits = set()
231  else:
232  mcTrackCandHits = set(mcTrackCandHits)
233 
234  length_of_intersection = len(mcTrackCandHits & trackCandHits)
235  if length_of_intersection > 0:
236  list_of_connected_mc_tracks.add(mcTrackCand)
237  list_of_numbers_of_hits_for_connected_tracks.append(length_of_intersection)
238 
239  if len(list_of_numbers_of_hits_for_connected_tracks) == 0:
240  self.number_of_wrong_hits.append(0)
241  self.pr_number_of_matched_hits.append(0)
242  else:
243  maximum_intersection = \
244  max(list_of_numbers_of_hits_for_connected_tracks)
245  self.pr_number_of_matched_hits.append(sum(list_of_numbers_of_hits_for_connected_tracks))
246  self.number_of_wrong_hits.append(sum(list_of_numbers_of_hits_for_connected_tracks) -
247  maximum_intersection)
248 
249  self.number_of_connected_tracks.append(len(list_of_connected_mc_tracks))
250 
251  if is_matched or is_clone:
252  mcTrackCand = \
253  self.trackMatchLookUp.getRelatedMCRecoTrack(trackCand)
254  mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in mcTrackCand.getCDCHitList()] # Checked
255  # Working around a bug in ROOT where you should not access empty std::vectors
256  if len(mcTrackCandHits) == 0:
257  mcTrackCandHits = set()
258  else:
259  mcTrackCandHits = set(mcTrackCandHits)
260 
261  is_hit_matched += len(trackCandHits & mcTrackCandHits)
262 
263  self.pr_number_of_hits.append(len(trackCandHits))
264 
265  for mcTrackCand in mcTrackCands:
266  is_missing = \
267  self.trackMatchLookUp.isMissingMCRecoTrack(mcTrackCand)
268 
269  mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in mcTrackCand.getCDCHitList()] # Checked
270 
271  # Working around a bug in ROOT where you should not access empty std::vectors
272  if len(mcTrackCandHits) == 0:
273  continue
274  else:
275  mcTrackCandHits = set(mcTrackCandHits)
276 
277  ratio = 1.0 * len(mcTrackCandHits & totalHitListPR) / len(mcTrackCandHits)
278 
279  self.ratio_hits_in_mc_tracks_and_not_in_pr_tracks.append(1.0 - ratio)
281  if is_missing:
284  1.0 * len(mcTrackCandHits & totalHitListPRGood) / len(mcTrackCandHits))
286  1.0 * len(mcTrackCandHits & totalHitListPRFake) / len(mcTrackCandHits))
287 
288  mcParticle = \
289  self.trackMatchLookUp.getRelatedMCParticle(mcTrackCand)
290  is_primary = \
291  mcParticle.hasStatus(Belle2.MCParticle.c_PrimaryParticle)
292  self.mc_is_primary.append(is_primary)
293  self.mc_number_of_hits.append(len(mcTrackCandHits))
294 
295  self.mc_missing.append(is_missing)
296 
297  self.number_of_total_hits.append(number_of_all_hits)
298  self.number_of_mc_hits.append(number_of_mc_hits)
299  self.number_of_pr_hits.append(number_of_pr_hits)
300 
301  self.is_hit_found.append(is_hit_found)
302  self.is_hit_matched.append(is_hit_matched)
303 
304  def terminate(self):
305  """Receive signal at the end of event processing"""
306  TrackingValidationModule.terminate(self)
307 
308  output_tfile = ROOT.TFile(self.output_file_name, 'update')
309 
310  validation_plots = []
311 
312  # Hit ratios #
313 
314  all_tracks_plot = self.profiles_by_parameters_base(
316  quantity_name="ratio of hits in MCTracks found by the track finder",
317  make_hist=True,
318  parameter_names=[],
319  profile_parameters={},
320  unit=None)
321 
322  validation_plots.extend(all_tracks_plot)
323 
324  missing_tracks_plot = self.profiles_by_parameters_base(
326  quantity_name="ratio of hits in missing MCTracks found by the track finder",
327  make_hist=True,
328  parameter_names=[],
329  profile_parameters={},
330  unit=None)
331 
332  validation_plots.extend(missing_tracks_plot)
333 
334  for validation_plot in validation_plots:
335  validation_plot.write()
336 
337  if self.write_tables:
338  # MC Figures of merit
339  mc_figures_of_merit = \
340  ValidationManyFiguresOfMerit('%s_mc_figures_of_merit' % self.validation_name)
341 
342  mc_figures_of_merit['mc_pts'] = self.mc_pts
343  mc_figures_of_merit['mc_d0s'] = self.mc_d0s
344  mc_figures_of_merit['mc_matches'] = self.mc_matches
345  mc_figures_of_merit['mc_hit_efficiencies'] = self.mc_hit_efficiencies
346  mc_figures_of_merit['mc_multiplicities'] = self.mc_multiplicities
347  mc_figures_of_merit['mc_phis'] = self.mc_phi
348  mc_figures_of_merit['mc_tan_lambdas'] = self.mc_tan_lambdas
349  mc_figures_of_merit['mc_thetas'] = self.mc_theta
350  mc_figures_of_merit['mc_missing'] = self.mc_missing
351  mc_figures_of_merit['mc_is_primary'] = self.mc_is_primary
352  mc_figures_of_merit['mc_number_of_hits'] = self.mc_number_of_hits
353  mc_figures_of_merit['ratio_hits_in_mc_tracks_and_in_good_pr_tracks'] = \
355  mc_figures_of_merit['ratio_hits_in_mc_tracks_and_in_fake_pr_tracks'] = \
357  mc_figures_of_merit['ratio_hits_in_mc_tracks_and_not_in_pr_tracks'] = \
359 
360  mc_figures_of_merit.write()
361 
362  # PR Figures of merit
363  pr_figures_of_merit = \
364  ValidationManyFiguresOfMerit('%s_pr_figures_of_merit' % self.validation_name)
365 
366  pr_figures_of_merit['pr_clones_and_matches'] = \
368  pr_figures_of_merit['pr_matches'] = self.pr_matches
369  pr_figures_of_merit['pr_fakes'] = self.pr_fakes
370  pr_figures_of_merit['pr_number_of_hits'] = self.pr_number_of_hits
371  pr_figures_of_merit['pr_number_of_matched_hits'] = \
373  pr_figures_of_merit['pr_seed_tan_lambdas'] = self.pr_seed_tan_lambdas
374  pr_figures_of_merit['pr_seed_phi'] = self.pr_seed_phi
375  pr_figures_of_merit['pr_seed_theta'] = self.pr_seed_theta
376 
377  pr_figures_of_merit['number_of_connected_tracks'] = \
379  pr_figures_of_merit['number_of_wrong_hits'] = self.number_of_wrong_hits
380 
381  pr_figures_of_merit.write()
382 
383  # Hit Figures of merit
384  hit_figures_of_merit = \
385  ValidationFiguresOfMerit('%s_hit_figures_of_merit' % self.validation_name)
386 
387  hit_figures_of_merit['number_of_total_hits'] = \
388  np.sum(self.number_of_total_hits)
389  hit_figures_of_merit['number_of_mc_hits'] = \
390  np.sum(self.number_of_mc_hits)
391  hit_figures_of_merit['number_of_pr_hits'] = \
392  np.sum(self.number_of_pr_hits)
393  hit_figures_of_merit['is_hit_found'] = np.sum(self.is_hit_found)
394  hit_figures_of_merit['is_hit_matched'] = np.sum(self.is_hit_matched)
395 
396  print(hit_figures_of_merit)
397  hit_figures_of_merit.write()
398 
399  output_tfile.Close()
tracking.validation.fom.ValidationFiguresOfMerit
Definition: fom.py:11
tracking.validation.module.TrackingValidationModule.pr_clones_and_matches
pr_clones_and_matches
Use deques in favour of lists to prevent repeated memory allocation of cost O(n)
Definition: module.py:179
tracking.validation.module.TrackingValidationModule.pr_seed_phi
pr_seed_phi
list of PR-track seed phi values
Definition: module.py:188
tracking.validation.module.TrackingValidationModule.validation_name
validation_name
cached value of the tracking-validation name
Definition: module.py:109
tracking.validation.hit_module.ExpertTrackingValidationModule.number_of_total_hits
number_of_total_hits
number of all hits
Definition: hit_module.py:90
tracking.validation.module.TrackingValidationModule.mcTrackCandidatesColumnName
mcTrackCandidatesColumnName
cached name of the MCRecoTracks StoreArray
Definition: module.py:136
tracking.validation.module.TrackingValidationModule.pr_seed_theta
pr_seed_theta
list of PR-track seed theta values
Definition: module.py:190
tracking.validation.module.TrackingValidationModule.trackCandidatesColumnName
trackCandidatesColumnName
cached name of the RecoTracks StoreArray
Definition: module.py:134
tracking.validation.module.TrackingValidationModule
Definition: module.py:98
tracking.validation.module.TrackingValidationModule.mc_theta
mc_theta
direction of the track in theta
Definition: module.py:235
tracking.validation.hit_module.ExpertTrackingValidationModule
Definition: hit_module.py:36
tracking.validation.module.TrackingValidationModule.trackMatchLookUp
trackMatchLookUp
Track-match object that examines relation information from MCMatcherTracksModule.
Definition: module.py:174
tracking.validation.module.TrackingValidationModule.mc_pts
mc_pts
list of MC-track pt values
Definition: module.py:239
tracking.validation.hit_module.ExpertTrackingValidationModule.mc_is_primary
mc_is_primary
list of flags indicating that the MC track is [not] a primary MCParticle
Definition: hit_module.py:116
tracking.validation.hit_module.ExpertTrackingValidationModule.number_of_wrong_hits
number_of_wrong_hits
This number gives information about the "badness" of the fake.
Definition: hit_module.py:124
tracking.validation.module.TrackingValidationModule.mc_d0s
mc_d0s
list of MC-track d0 values
Definition: module.py:231
tracking.validation.hit_module.ExpertTrackingValidationModule.is_hit_matched
is_hit_matched
list of flags for [not-]matched hits
Definition: hit_module.py:98
tracking.validation.module.TrackingValidationModule.mc_phi
mc_phi
direction of the track in phi
Definition: module.py:237
tracking.validation.module.TrackingValidationModule.mc_hit_efficiencies
mc_hit_efficiencies
list of MC-track hit efficiencies
Definition: module.py:241
Belle2::PyStoreArray::list
static std::vector< std::string > list(DataStore::EDurability durability=DataStore::EDurability::c_Event)
Return list of available arrays for given durability.
Definition: PyStoreArray.cc:21
tracking.validation.hit_module.ExpertTrackingValidationModule.mc_missing
mc_missing
list of flags where MCRecoTrack is [not] missing MCTrackCand
Definition: hit_module.py:102
tracking.validation.hit_module.ExpertTrackingValidationModule.terminate
def terminate(self)
Definition: hit_module.py:304
tracking.validation.module.TrackingValidationModule.mc_tan_lambdas
mc_tan_lambdas
list of MC-track tan(lambda) values
Definition: module.py:233
tracking.validation.module.TrackingValidationModule.mc_multiplicities
mc_multiplicities
list of MC-track multiplicities
Definition: module.py:243
tracking.validation.fom.ValidationManyFiguresOfMerit
Definition: fom.py:136
tracking.validation.hit_module.ExpertTrackingValidationModule.pr_number_of_matched_hits
pr_number_of_matched_hits
list of the number of pattern-reconstructed hits matched to MC track
Definition: hit_module.py:131
tracking.validation.hit_module.ExpertTrackingValidationModule.mc_number_of_hits
mc_number_of_hits
list of the number of MCTrackCandHits on the MC track
Definition: hit_module.py:118
tracking.validation.hit_module.ExpertTrackingValidationModule.ratio_hits_in_mc_tracks_and_not_in_pr_tracks
ratio_hits_in_mc_tracks_and_not_in_pr_tracks
list of fraction of number of hits in MC track but not in PR track
Definition: hit_module.py:104
tracking.validation.module.TrackingValidationModule.profiles_by_parameters_base
def profiles_by_parameters_base(self, xs, quantity_name, parameter_names, profile_parameters, unit, make_hist, non_expert_parameters=[], weights=None)
Definition: module.py:703
tracking.validation.hit_module.ExpertTrackingValidationModule.__init__
def __init__(self, name, contact, fit=False, pulls=False, resolution=False, output_file_name=None, track_filter_object=AlwaysPassFilter(), plot_name_postfix='', plot_title_postfix='', exclude_profile_mc_parameter='', exclude_profile_pr_parameter='', use_expert_folder=True, trackCandidatesColumnName='RecoTracks', mcTrackCandidatesColumnName='MCRecoTracks', cdcHitsColumnName='CDCHits', write_tables=False)
Definition: hit_module.py:42
tracking.validation.module.TrackingValidationModule.pr_seed_tan_lambdas
pr_seed_tan_lambdas
list of PR-track seed tan(lambda) values
Definition: module.py:186
tracking.validation.hit_module.ExpertTrackingValidationModule.number_of_connected_tracks
number_of_connected_tracks
This is the number of mcTrackCands sharing a hit with the track cand.
Definition: hit_module.py:122
tracking.validation.hit_module.ExpertTrackingValidationModule.is_hit_found
is_hit_found
list of flags for [not-]found hits
Definition: hit_module.py:96
tracking.validation.hit_module.ExpertTrackingValidationModule.event
def event(self)
Definition: hit_module.py:133
tracking.validation.hit_module.ExpertTrackingValidationModule.write_tables
write_tables
cached value of the flag to write the validation figures of merit
Definition: hit_module.py:65
Belle2::PyStoreArray
a (simplified) python wrapper for StoreArray.
Definition: PyStoreArray.h:58
tracking.validation.hit_module.ExpertTrackingValidationModule.initialize
def initialize(self)
Definition: hit_module.py:84
tracking.validation.hit_module.ExpertTrackingValidationModule.pr_number_of_hits
pr_number_of_hits
list of the number of pattern-reconstructed hits
Definition: hit_module.py:129
tracking.validation.hit_module.ExpertTrackingValidationModule.ratio_hits_in_missing_mc_tracks_and_in_pr_tracks
ratio_hits_in_missing_mc_tracks_and_in_pr_tracks
list of fraction of number of hits in missing MC track and in PR track
Definition: hit_module.py:108
tracking.validation.module.TrackingValidationModule.pr_fakes
pr_fakes
list of PR-track fakes
Definition: module.py:183
tracking.validation.hit_module.ExpertTrackingValidationModule.cdcHitsColumnname
cdcHitsColumnname
cached name of the CDCHits StoreArray
Definition: hit_module.py:63
tracking.validation.hit_module.ExpertTrackingValidationModule.ratio_hits_in_mc_tracks_and_in_pr_tracks
ratio_hits_in_mc_tracks_and_in_pr_tracks
list of fraction of number of hits in MC track and in PR track
Definition: hit_module.py:106
tracking.validation.hit_module.ExpertTrackingValidationModule.ratio_hits_in_mc_tracks_and_in_fake_pr_tracks
ratio_hits_in_mc_tracks_and_in_fake_pr_tracks
list of fraction of number of hits in MC track and in fake PR track
Definition: hit_module.py:110
tracking.validation.hit_module.ExpertTrackingValidationModule.number_of_mc_hits
number_of_mc_hits
number of hits on MC track
Definition: hit_module.py:92
tracking.validation.module.TrackingValidationModule.pr_matches
pr_matches
list of PR-track matches
Definition: module.py:181
tracking.validation.module.TrackingValidationModule.mc_matches
mc_matches
list of MC-track matches
Definition: module.py:227
tracking.validation.hit_module.ExpertTrackingValidationModule.examine_hits_in_event
def examine_hits_in_event(self)
Definition: hit_module.py:139
tracking.validation.hit_module.ExpertTrackingValidationModule.ratio_hits_in_mc_tracks_and_in_good_pr_tracks
ratio_hits_in_mc_tracks_and_in_good_pr_tracks
list of fraction of number of hits in MC track and in good PR track
Definition: hit_module.py:113
tracking.validation.module.TrackingValidationModule.output_file_name
output_file_name
cached value of the output ROOT TFile
Definition: module.py:119
tracking.validation.hit_module.ExpertTrackingValidationModule.number_of_pr_hits
number_of_pr_hits
number of hits on pattern reconstructed tracks
Definition: hit_module.py:94