91 """Receive signal at the start of event processing"""
92 TrackingValidationModule.initialize(self)
94 # Use dequeues in favour of lists to prevent repeated memory allocation of cost O(n)
96 self.number_of_total_hits = collections.deque()
97 ## number of hits on MC track
98 self.number_of_mc_hits = collections.deque()
99 ## number of hits on pattern reconstructed tracks
100 self.number_of_pr_hits = collections.deque()
101 ## list of flags for [not-]found hits
102 self.is_hit_found = collections.deque()
103 ## list of flags for [not-]matched hits
104 self.is_hit_matched = collections.deque()
107 ## list of flags where MCRecoTrack is [not] missing MCTrackCand
108 self.mc_missing = collections.deque()
109 ## list of fraction of number of hits in MC track but not in PR track
110 self.ratio_hits_in_mc_tracks_and_not_in_pr_tracks = collections.deque()
111 ## list of fraction of number of hits in MC track and in PR track
112 self.ratio_hits_in_mc_tracks_and_in_pr_tracks = collections.deque()
113 ## list of fraction of number of hits in missing MC track and in PR track
114 self.ratio_hits_in_missing_mc_tracks_and_in_pr_tracks = collections.deque()
115 ## list of fraction of number of hits in MC track and in fake PR track
116 self.ratio_hits_in_mc_tracks_and_in_fake_pr_tracks = \
118 ## list of fraction of number of hits in MC track and in good PR track
119 self.ratio_hits_in_mc_tracks_and_in_good_pr_tracks = \
121 ## list of flags indicating that the MC track is [not] a primary MCParticle
122 self.mc_is_primary = collections.deque()
123 ## list of the number of MCTrackCandHits on the MC track
124 self.mc_number_of_hits = collections.deque()
127 ## This is the number of mcTrackCands sharing a hit with the track cand.
128 self.number_of_connected_tracks = collections.deque()
129 ## This number gives information about the "badness" of the fake.
130 self.number_of_wrong_hits = collections.deque()
131 # It is calculated by going through all hits of the fake track and the connected mc track cands and counting the number.
132 # These numbers are than summed up and subtracted by the biggest number
133 # of hits this candidates shares with the mc track cands.
134 ## list of the number of pattern-reconstructed hits
135 self.pr_number_of_hits = collections.deque()
136 ## list of the number of pattern-reconstructed hits matched to MC track
137 self.pr_number_of_matched_hits = collections.deque()
138 ## list of the quality indicator of tracks
139 self.pr_track_QI = collections.deque()
147 def examine_hits_in_event(self):
148 """Classify all of the hits in the event according to the parent track(s)"""
150 trackCands = Belle2.PyStoreArray(self.trackCandidatesColumnName)
151 mcTrackCands = Belle2.PyStoreArray(self.mcTrackCandidatesColumnName)
152 if self.cdcHitsColumnname not in Belle2.PyStoreArray.list():
153 # No CDC hits available, hit analysis incomplete, don't perform
156 cdcHits = Belle2.PyStoreArray(self.cdcHitsColumnname)
158 # # CDC Hits in MC tracks
160 for mcTrackCand in mcTrackCands:
161 cdcHitIDs = [cdcHit.getArrayIndex() for cdcHit in getObjectList(mcTrackCand.getCDCHitList())] # Checked
162 # Working around a bug in ROOT where you should not access empty std::vectors
163 if len(cdcHitIDs) == 0:
166 cdcHitIDs = set(cdcHitIDs)
167 totalHitListMC.extend(cdcHitIDs)
169 # Make the ids unique
170 totalHitListMC = set(totalHitListMC)
172 # # CDC Hits in PR tracks
174 totalHitListPRGood = []
175 totalHitListPRClone = []
176 totalHitListPRFake = []
177 for trackCand in trackCands:
178 if trackCand.getNumberOfTotalHits() == 0:
179 basf2.B2WARNING("Encountered a pattern recognition track with no hits")
182 cdcHitIDs = [cdcHit.getArrayIndex() for cdcHit in getObjectList(trackCand.getCDCHitList())] # Checked
183 # Working around a bug in ROOT where you should not access empty std::vectors
184 if len(cdcHitIDs) == 0:
187 cdcHitIDs = set(cdcHitIDs)
189 totalHitListPR.extend(cdcHitIDs)
190 if self.trackMatchLookUp.isAnyChargeMatchedPRRecoTrack(trackCand):
191 totalHitListPRGood.extend(cdcHitIDs)
193 if self.trackMatchLookUp.isAnyChargeClonePRRecoTrack(trackCand):
194 totalHitListPRClone.extend(cdcHitIDs)
196 if (self.trackMatchLookUp.isBackgroundPRRecoTrack(trackCand) or
197 self.trackMatchLookUp.isGhostPRRecoTrack(trackCand)):
198 totalHitListPRFake.extend(cdcHitIDs)
200 # Make the ids unique
201 totalHitListPR = set(totalHitListPR)
202 totalHitListPRGood = set(totalHitListPRGood)
203 totalHitListPRClone = set(totalHitListPRClone)
204 totalHitListPRFake = set(totalHitListPRFake)
207 totalHitList = {cdcHit.getArrayIndex() for cdcHit in cdcHits}
209 number_of_mc_hits = len(totalHitListMC)
210 number_of_pr_hits = len(totalHitListPR)
211 number_of_all_hits = len(totalHitList)
214 is_hit_found = len(totalHitListMC & totalHitListPR)
216 for trackCand in trackCands:
218 is_matched = self.trackMatchLookUp.isAnyChargeMatchedPRRecoTrack(trackCand)
219 is_clone = self.trackMatchLookUp.isAnyChargeClonePRRecoTrack(trackCand)
221 trackCandHits = [cdcHit.getArrayIndex() for cdcHit in getObjectList(trackCand.getCDCHitList())]
222 # Working around a bug in ROOT where you should not access empty std::vectors
223 if len(trackCandHits) == 0:
224 trackCandHits = set()
226 trackCandHits = set(trackCandHits)
228 # this is not very efficient...
229 list_of_connected_mc_tracks = set()
230 list_of_numbers_of_hits_for_connected_tracks = collections.deque()
231 # number_of_connected_tracks = 0
232 # number_of_wrong_hits = 0
234 for mcTrackCand in mcTrackCands:
235 mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in getObjectList(mcTrackCand.getCDCHitList())]
236 # Working around a bug in ROOT where you should not access empty std::vectors
237 if len(mcTrackCandHits) == 0:
238 mcTrackCandHits = set()
240 mcTrackCandHits = set(mcTrackCandHits)
242 length_of_intersection = len(mcTrackCandHits & trackCandHits)
243 if length_of_intersection > 0:
244 list_of_connected_mc_tracks.add(mcTrackCand)
245 list_of_numbers_of_hits_for_connected_tracks.append(length_of_intersection)
247 if len(list_of_numbers_of_hits_for_connected_tracks) == 0:
248 self.number_of_wrong_hits.append(0)
249 self.pr_number_of_matched_hits.append(0)
251 maximum_intersection = \
252 max(list_of_numbers_of_hits_for_connected_tracks)
253 self.pr_number_of_matched_hits.append(sum(list_of_numbers_of_hits_for_connected_tracks))
254 self.number_of_wrong_hits.append(sum(list_of_numbers_of_hits_for_connected_tracks) -
255 maximum_intersection)
257 self.number_of_connected_tracks.append(len(list_of_connected_mc_tracks))
259 if is_matched or is_clone:
261 self.trackMatchLookUp.getRelatedMCRecoTrack(trackCand)
262 mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in getObjectList(mcTrackCand.getCDCHitList())] # Checked
263 # Working around a bug in ROOT where you should not access empty std::vectors
264 if len(mcTrackCandHits) == 0:
265 mcTrackCandHits = set()
267 mcTrackCandHits = set(mcTrackCandHits)
269 is_hit_matched += len(trackCandHits & mcTrackCandHits)
271 self.pr_number_of_hits.append(len(trackCandHits))
273 if self.plotTrackQualityIndicator:
274 self.pr_track_QI.append(trackCand.getQualityIndicator())
276 for mcTrackCand in mcTrackCands:
278 self.trackMatchLookUp.isMissingMCRecoTrack(mcTrackCand)
280 mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in getObjectList(mcTrackCand.getCDCHitList())] # Checked
282 # Working around a bug in ROOT where you should not access empty std::vectors
283 if len(mcTrackCandHits) == 0:
286 mcTrackCandHits = set(mcTrackCandHits)
288 ratio = 1.0 * len(mcTrackCandHits & totalHitListPR) / len(mcTrackCandHits)
290 self.ratio_hits_in_mc_tracks_and_not_in_pr_tracks.append(1.0 - ratio)
291 self.ratio_hits_in_mc_tracks_and_in_pr_tracks.append(ratio)
293 self.ratio_hits_in_missing_mc_tracks_and_in_pr_tracks.append(ratio)
294 self.ratio_hits_in_mc_tracks_and_in_good_pr_tracks.append(
295 1.0 * len(mcTrackCandHits & totalHitListPRGood) / len(mcTrackCandHits))
296 self.ratio_hits_in_mc_tracks_and_in_fake_pr_tracks.append(
297 1.0 * len(mcTrackCandHits & totalHitListPRFake) / len(mcTrackCandHits))
300 self.trackMatchLookUp.getRelatedMCParticle(mcTrackCand)
302 mcParticle.hasStatus(Belle2.MCParticle.c_PrimaryParticle)
303 self.mc_is_primary.append(is_primary)
304 self.mc_number_of_hits.append(len(mcTrackCandHits))
306 self.mc_missing.append(is_missing)
308 self.number_of_total_hits.append(number_of_all_hits)
309 self.number_of_mc_hits.append(number_of_mc_hits)
310 self.number_of_pr_hits.append(number_of_pr_hits)
312 self.is_hit_found.append(is_hit_found)
313 self.is_hit_matched.append(is_hit_matched)
316 """Receive signal at the end of event processing"""
317 TrackingValidationModule.terminate(self)
319 output_tfile = ROOT.TFile(self.output_file_name, 'update')
321 validation_plots = []
324 ######################
325 all_tracks_plot = self.profiles_by_parameters_base(
326 xs=self.ratio_hits_in_mc_tracks_and_in_pr_tracks,
327 quantity_name="ratio of hits in MCTracks found by the track finder",
330 profile_parameters={},
333 validation_plots.extend(all_tracks_plot)
335 missing_tracks_plot = self.profiles_by_parameters_base(
336 xs=self.ratio_hits_in_missing_mc_tracks_and_in_pr_tracks,
337 quantity_name="ratio of hits in missing MCTracks found by the track finder",
340 profile_parameters={},
343 validation_plots.extend(missing_tracks_plot)
345 # Quality Indicator #
346 if self.plotTrackQualityIndicator:
347 track_QI_plot = self.profiles_by_parameters_base(
349 quantity_name="track quality indicator",
352 profile_parameters={},
355 validation_plots.extend(track_QI_plot)
357 for validation_plot in validation_plots:
358 validation_plot.write()
360 if self.write_tables:
361 # MC Figures of merit
362 mc_figures_of_merit = \
363 ValidationManyFiguresOfMerit(f'{self.validation_name}_mc_figures_of_merit')
365 mc_figures_of_merit['mc_pts'] = self.mc_pts
366 mc_figures_of_merit['mc_d0s'] = self.mc_d0s
367 mc_figures_of_merit['mc_matches'] = self.mc_matches
368 mc_figures_of_merit['mc_hit_efficiencies'] = self.mc_hit_efficiencies
369 mc_figures_of_merit['mc_multiplicities'] = self.mc_multiplicities
370 mc_figures_of_merit['mc_phis'] = self.mc_phi
371 mc_figures_of_merit['mc_tan_lambdas'] = self.mc_tan_lambdas
372 mc_figures_of_merit['mc_missing'] = self.mc_missing
373 mc_figures_of_merit['mc_is_primary'] = self.mc_is_primary
374 mc_figures_of_merit['mc_number_of_hits'] = self.mc_number_of_hits
375 mc_figures_of_merit['ratio_hits_in_mc_tracks_and_in_good_pr_tracks'] = \
376 self.ratio_hits_in_mc_tracks_and_in_good_pr_tracks
377 mc_figures_of_merit['ratio_hits_in_mc_tracks_and_in_fake_pr_tracks'] = \
378 self.ratio_hits_in_mc_tracks_and_in_fake_pr_tracks
379 mc_figures_of_merit['ratio_hits_in_mc_tracks_and_not_in_pr_tracks'] = \
380 self.ratio_hits_in_mc_tracks_and_not_in_pr_tracks
382 mc_figures_of_merit.write()
384 # PR Figures of merit
385 pr_figures_of_merit = \
386 ValidationManyFiguresOfMerit(f'{self.validation_name}_pr_figures_of_merit')
388 pr_figures_of_merit['pr_clones_and_matches'] = \
389 self.pr_clones_and_matches
390 pr_figures_of_merit['pr_matches'] = self.pr_matches
391 pr_figures_of_merit['pr_fakes'] = self.pr_fakes
392 pr_figures_of_merit['pr_number_of_hits'] = self.pr_number_of_hits
393 pr_figures_of_merit['pr_number_of_matched_hits'] = \
394 self.pr_number_of_matched_hits
395 pr_figures_of_merit['pr_seed_tan_lambdas'] = self.pr_seed_tan_lambdas
396 pr_figures_of_merit['pr_seed_phi'] = self.pr_seed_phi
398 pr_figures_of_merit['number_of_connected_tracks'] = \
399 self.number_of_connected_tracks
400 pr_figures_of_merit['number_of_wrong_hits'] = self.number_of_wrong_hits
402 pr_figures_of_merit.write()
404 # Hit Figures of merit
405 hit_figures_of_merit = \
406 ValidationFiguresOfMerit(f'{self.validation_name}_hit_figures_of_merit')
408 hit_figures_of_merit['number_of_total_hits'] = \
409 np.sum(self.number_of_total_hits)
410 hit_figures_of_merit['number_of_mc_hits'] = \
411 np.sum(self.number_of_mc_hits)
412 hit_figures_of_merit['number_of_pr_hits'] = \
413 np.sum(self.number_of_pr_hits)
414 hit_figures_of_merit['is_hit_found'] = np.sum(self.is_hit_found)
415 hit_figures_of_merit['is_hit_matched'] = np.sum(self.is_hit_matched)
417 print(hit_figures_of_merit)
418 hit_figures_of_merit.write()