88 """Receive signal at the start of event processing"""
89 TrackingValidationModule.initialize(self)
91 # Use dequeues in favour of lists to prevent repeated memory allocation of cost O(n)
93 self.number_of_total_hits = collections.deque()
94 ## number of hits on MC track
95 self.number_of_mc_hits = collections.deque()
96 ## number of hits on pattern reconstructed tracks
97 self.number_of_pr_hits = collections.deque()
98 ## list of flags for [not-]found hits
99 self.is_hit_found = collections.deque()
100 ## list of flags for [not-]matched hits
101 self.is_hit_matched = collections.deque()
104 ## list of flags where MCRecoTrack is [not] missing MCTrackCand
105 self.mc_missing = collections.deque()
106 ## list of fraction of number of hits in MC track but not in PR track
107 self.ratio_hits_in_mc_tracks_and_not_in_pr_tracks = collections.deque()
108 ## list of fraction of number of hits in MC track and in PR track
109 self.ratio_hits_in_mc_tracks_and_in_pr_tracks = collections.deque()
110 ## list of fraction of number of hits in missing MC track and in PR track
111 self.ratio_hits_in_missing_mc_tracks_and_in_pr_tracks = collections.deque()
112 ## list of fraction of number of hits in MC track and in fake PR track
113 self.ratio_hits_in_mc_tracks_and_in_fake_pr_tracks = \
115 ## list of fraction of number of hits in MC track and in good PR track
116 self.ratio_hits_in_mc_tracks_and_in_good_pr_tracks = \
118 ## list of flags indicating that the MC track is [not] a primary MCParticle
119 self.mc_is_primary = collections.deque()
120 ## list of the number of MCTrackCandHits on the MC track
121 self.mc_number_of_hits = collections.deque()
124 ## This is the number of mcTrackCands sharing a hit with the track cand.
125 self.number_of_connected_tracks = collections.deque()
126 ## This number gives information about the "badness" of the fake.
127 self.number_of_wrong_hits = collections.deque()
128 # It is calculated by going through all hits of the fake track and the connected mc track cands and counting the number.
129 # These numbers are than summed up and subtracted by the biggest number
130 # of hits this candidates shares with the mc track cands.
131 ## list of the number of pattern-reconstructed hits
132 self.pr_number_of_hits = collections.deque()
133 ## list of the number of pattern-reconstructed hits matched to MC track
134 self.pr_number_of_matched_hits = collections.deque()
142 def examine_hits_in_event(self):
143 """Classify all of the hits in the event according to the parent track(s)"""
145 trackCands = Belle2.PyStoreArray(self.trackCandidatesColumnName)
146 mcTrackCands = Belle2.PyStoreArray(self.mcTrackCandidatesColumnName)
147 if self.cdcHitsColumnname not in Belle2.PyStoreArray.list():
148 # No CDC hits available, hit analysis incomplete, don't perform
151 cdcHits = Belle2.PyStoreArray(self.cdcHitsColumnname)
153 # # CDC Hits in MC tracks
155 for mcTrackCand in mcTrackCands:
156 cdcHitIDs = [cdcHit.getArrayIndex() for cdcHit in getObjectList(mcTrackCand.getCDCHitList())] # Checked
157 # Working around a bug in ROOT where you should not access empty std::vectors
158 if len(cdcHitIDs) == 0:
161 cdcHitIDs = set(cdcHitIDs)
162 totalHitListMC.extend(cdcHitIDs)
164 # Make the ids unique
165 totalHitListMC = set(totalHitListMC)
167 # # CDC Hits in PR tracks
169 totalHitListPRGood = []
170 totalHitListPRClone = []
171 totalHitListPRFake = []
172 for trackCand in trackCands:
173 if trackCand.getNumberOfTotalHits() == 0:
174 basf2.B2WARNING("Encountered a pattern recognition track with no hits")
177 cdcHitIDs = [cdcHit.getArrayIndex() for cdcHit in getObjectList(trackCand.getCDCHitList())] # Checked
178 # Working around a bug in ROOT where you should not access empty std::vectors
179 if len(cdcHitIDs) == 0:
182 cdcHitIDs = set(cdcHitIDs)
184 totalHitListPR.extend(cdcHitIDs)
185 if self.trackMatchLookUp.isAnyChargeMatchedPRRecoTrack(trackCand):
186 totalHitListPRGood.extend(cdcHitIDs)
188 if self.trackMatchLookUp.isAnyChargeClonePRRecoTrack(trackCand):
189 totalHitListPRClone.extend(cdcHitIDs)
191 if (self.trackMatchLookUp.isBackgroundPRRecoTrack(trackCand) or
192 self.trackMatchLookUp.isGhostPRRecoTrack(trackCand)):
193 totalHitListPRFake.extend(cdcHitIDs)
195 # Make the ids unique
196 totalHitListPR = set(totalHitListPR)
197 totalHitListPRGood = set(totalHitListPRGood)
198 totalHitListPRClone = set(totalHitListPRClone)
199 totalHitListPRFake = set(totalHitListPRFake)
202 totalHitList = {cdcHit.getArrayIndex() for cdcHit in cdcHits}
204 number_of_mc_hits = len(totalHitListMC)
205 number_of_pr_hits = len(totalHitListPR)
206 number_of_all_hits = len(totalHitList)
209 is_hit_found = len(totalHitListMC & totalHitListPR)
211 for trackCand in trackCands:
213 is_matched = self.trackMatchLookUp.isAnyChargeMatchedPRRecoTrack(trackCand)
214 is_clone = self.trackMatchLookUp.isAnyChargeClonePRRecoTrack(trackCand)
216 trackCandHits = [cdcHit.getArrayIndex() for cdcHit in getObjectList(trackCand.getCDCHitList())]
217 # Working around a bug in ROOT where you should not access empty std::vectors
218 if len(trackCandHits) == 0:
219 trackCandHits = set()
221 trackCandHits = set(trackCandHits)
223 # this is not very efficient...
224 list_of_connected_mc_tracks = set()
225 list_of_numbers_of_hits_for_connected_tracks = collections.deque()
226 # number_of_connected_tracks = 0
227 # number_of_wrong_hits = 0
229 for mcTrackCand in mcTrackCands:
230 mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in getObjectList(mcTrackCand.getCDCHitList())]
231 # Working around a bug in ROOT where you should not access empty std::vectors
232 if len(mcTrackCandHits) == 0:
233 mcTrackCandHits = set()
235 mcTrackCandHits = set(mcTrackCandHits)
237 length_of_intersection = len(mcTrackCandHits & trackCandHits)
238 if length_of_intersection > 0:
239 list_of_connected_mc_tracks.add(mcTrackCand)
240 list_of_numbers_of_hits_for_connected_tracks.append(length_of_intersection)
242 if len(list_of_numbers_of_hits_for_connected_tracks) == 0:
243 self.number_of_wrong_hits.append(0)
244 self.pr_number_of_matched_hits.append(0)
246 maximum_intersection = \
247 max(list_of_numbers_of_hits_for_connected_tracks)
248 self.pr_number_of_matched_hits.append(sum(list_of_numbers_of_hits_for_connected_tracks))
249 self.number_of_wrong_hits.append(sum(list_of_numbers_of_hits_for_connected_tracks) -
250 maximum_intersection)
252 self.number_of_connected_tracks.append(len(list_of_connected_mc_tracks))
254 if is_matched or is_clone:
256 self.trackMatchLookUp.getRelatedMCRecoTrack(trackCand)
257 mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in getObjectList(mcTrackCand.getCDCHitList())] # Checked
258 # Working around a bug in ROOT where you should not access empty std::vectors
259 if len(mcTrackCandHits) == 0:
260 mcTrackCandHits = set()
262 mcTrackCandHits = set(mcTrackCandHits)
264 is_hit_matched += len(trackCandHits & mcTrackCandHits)
266 self.pr_number_of_hits.append(len(trackCandHits))
268 for mcTrackCand in mcTrackCands:
270 self.trackMatchLookUp.isMissingMCRecoTrack(mcTrackCand)
272 mcTrackCandHits = [cdcHit.getArrayIndex() for cdcHit in getObjectList(mcTrackCand.getCDCHitList())] # Checked
274 # Working around a bug in ROOT where you should not access empty std::vectors
275 if len(mcTrackCandHits) == 0:
278 mcTrackCandHits = set(mcTrackCandHits)
280 ratio = 1.0 * len(mcTrackCandHits & totalHitListPR) / len(mcTrackCandHits)
282 self.ratio_hits_in_mc_tracks_and_not_in_pr_tracks.append(1.0 - ratio)
283 self.ratio_hits_in_mc_tracks_and_in_pr_tracks.append(ratio)
285 self.ratio_hits_in_missing_mc_tracks_and_in_pr_tracks.append(ratio)
286 self.ratio_hits_in_mc_tracks_and_in_good_pr_tracks.append(
287 1.0 * len(mcTrackCandHits & totalHitListPRGood) / len(mcTrackCandHits))
288 self.ratio_hits_in_mc_tracks_and_in_fake_pr_tracks.append(
289 1.0 * len(mcTrackCandHits & totalHitListPRFake) / len(mcTrackCandHits))
292 self.trackMatchLookUp.getRelatedMCParticle(mcTrackCand)
294 mcParticle.hasStatus(Belle2.MCParticle.c_PrimaryParticle)
295 self.mc_is_primary.append(is_primary)
296 self.mc_number_of_hits.append(len(mcTrackCandHits))
298 self.mc_missing.append(is_missing)
300 self.number_of_total_hits.append(number_of_all_hits)
301 self.number_of_mc_hits.append(number_of_mc_hits)
302 self.number_of_pr_hits.append(number_of_pr_hits)
304 self.is_hit_found.append(is_hit_found)
305 self.is_hit_matched.append(is_hit_matched)
308 """Receive signal at the end of event processing"""
309 TrackingValidationModule.terminate(self)
311 output_tfile = ROOT.TFile(self.output_file_name, 'update')
313 validation_plots = []
316 ######################
317 all_tracks_plot = self.profiles_by_parameters_base(
318 xs=self.ratio_hits_in_mc_tracks_and_in_pr_tracks,
319 quantity_name="ratio of hits in MCTracks found by the track finder",
322 profile_parameters={},
325 validation_plots.extend(all_tracks_plot)
327 missing_tracks_plot = self.profiles_by_parameters_base(
328 xs=self.ratio_hits_in_missing_mc_tracks_and_in_pr_tracks,
329 quantity_name="ratio of hits in missing MCTracks found by the track finder",
332 profile_parameters={},
335 validation_plots.extend(missing_tracks_plot)
337 for validation_plot in validation_plots:
338 validation_plot.write()
340 if self.write_tables:
341 # MC Figures of merit
342 mc_figures_of_merit = \
343 ValidationManyFiguresOfMerit(f'{self.validation_name}_mc_figures_of_merit')
345 mc_figures_of_merit['mc_pts'] = self.mc_pts
346 mc_figures_of_merit['mc_d0s'] = self.mc_d0s
347 mc_figures_of_merit['mc_matches'] = self.mc_matches
348 mc_figures_of_merit['mc_hit_efficiencies'] = self.mc_hit_efficiencies
349 mc_figures_of_merit['mc_multiplicities'] = self.mc_multiplicities
350 mc_figures_of_merit['mc_phis'] = self.mc_phi
351 mc_figures_of_merit['mc_tan_lambdas'] = self.mc_tan_lambdas
352 mc_figures_of_merit['mc_missing'] = self.mc_missing
353 mc_figures_of_merit['mc_is_primary'] = self.mc_is_primary
354 mc_figures_of_merit['mc_number_of_hits'] = self.mc_number_of_hits
355 mc_figures_of_merit['ratio_hits_in_mc_tracks_and_in_good_pr_tracks'] = \
356 self.ratio_hits_in_mc_tracks_and_in_good_pr_tracks
357 mc_figures_of_merit['ratio_hits_in_mc_tracks_and_in_fake_pr_tracks'] = \
358 self.ratio_hits_in_mc_tracks_and_in_fake_pr_tracks
359 mc_figures_of_merit['ratio_hits_in_mc_tracks_and_not_in_pr_tracks'] = \
360 self.ratio_hits_in_mc_tracks_and_not_in_pr_tracks
362 mc_figures_of_merit.write()
364 # PR Figures of merit
365 pr_figures_of_merit = \
366 ValidationManyFiguresOfMerit(f'{self.validation_name}_pr_figures_of_merit')
368 pr_figures_of_merit['pr_clones_and_matches'] = \
369 self.pr_clones_and_matches
370 pr_figures_of_merit['pr_matches'] = self.pr_matches
371 pr_figures_of_merit['pr_fakes'] = self.pr_fakes
372 pr_figures_of_merit['pr_number_of_hits'] = self.pr_number_of_hits
373 pr_figures_of_merit['pr_number_of_matched_hits'] = \
374 self.pr_number_of_matched_hits
375 pr_figures_of_merit['pr_seed_tan_lambdas'] = self.pr_seed_tan_lambdas
376 pr_figures_of_merit['pr_seed_phi'] = self.pr_seed_phi
378 pr_figures_of_merit['number_of_connected_tracks'] = \
379 self.number_of_connected_tracks
380 pr_figures_of_merit['number_of_wrong_hits'] = self.number_of_wrong_hits
382 pr_figures_of_merit.write()
384 # Hit Figures of merit
385 hit_figures_of_merit = \
386 ValidationFiguresOfMerit(f'{self.validation_name}_hit_figures_of_merit')
388 hit_figures_of_merit['number_of_total_hits'] = \
389 np.sum(self.number_of_total_hits)
390 hit_figures_of_merit['number_of_mc_hits'] = \
391 np.sum(self.number_of_mc_hits)
392 hit_figures_of_merit['number_of_pr_hits'] = \
393 np.sum(self.number_of_pr_hits)
394 hit_figures_of_merit['is_hit_found'] = np.sum(self.is_hit_found)
395 hit_figures_of_merit['is_hit_matched'] = np.sum(self.is_hit_matched)
397 print(hit_figures_of_merit)
398 hit_figures_of_merit.write()