Belle II Software  release-05-01-25
pr_side_module.py
1 import basf2
2 
3 import ROOT
4 ROOT.gSystem.Load("libtracking")
5 from ROOT import Belle2
6 
7 import math
8 import warnings
9 
10 import numpy as np
11 
12 import tracking.harvest.harvesting as harvesting
13 import tracking.harvest.refiners as refiners
14 import tracking.validation.utilities as utilities
15 
16 import tracking.harvest.peelers as peelers
17 
18 
19 class PRSideTrackingValidationModule(harvesting.HarvestingModule):
20  """Module to collect matching information about the found particles and to generate
21  validation plots and figures of merit on the performance of track finding."""
22 
23  """ Expert level behavior:
24  expert_level <= default_expert_level: all figures and plots from this module except tree entries
25  expert_level > default_expert_level: everything including tree entries
26  """
27 
28  default_expert_level = 10
29 
30  def __init__(
31  self,
32  name,
33  contact,
34  output_file_name=None,
35  reco_tracks_name='RecoTracks',
36  mc_reco_tracks_name='MCRecoTracks',
37  expert_level=None):
38  """Constructor"""
39 
40  output_file_name = output_file_name or name + 'TrackingValidation.root'
41  super().__init__(foreach=reco_tracks_name,
42  name=name,
43  contact=contact,
44  output_file_name=output_file_name,
45  expert_level=expert_level)
46 
47 
48  self.reco_tracks_name = reco_tracks_name
49 
50 
51  self.mc_reco_tracks_name = mc_reco_tracks_name
52 
53 
54  self.track_match_look_up = None
55 
56 
58 
59 
61 
62  def initialize(self):
63  """Receive signal at the start of event processing"""
64  super().initialize()
66  self.reco_tracks_name)
67 
68  def prepare(self):
69  """Called once at the start of each event"""
70  super().prepare()
71  mc_reco_tracks = Belle2.PyStoreArray(self.mc_reco_tracks_name)
72  mc_reco_tracks_det_hit_ids = []
73 
74  for mc_reco_track in mc_reco_tracks:
75  mc_reco_track_det_hit_ids = utilities.get_det_hit_ids(mc_reco_track)
76  mc_reco_tracks_det_hit_ids.append(mc_reco_track_det_hit_ids)
77 
78  self.mc_reco_tracks_det_hit_ids = mc_reco_tracks_det_hit_ids
79 
80  self.mc_hit_lookup.fill()
81 
82  def pick(self, reco_track):
83  """Method to filter the track candidates to reject part of them"""
84  return True
85 
86  def peel(self, reco_track):
87  """Looks at the individual pattern recognition tracks and store information about them"""
88  track_match_look_up = self.track_match_look_up
89 
90  # Matching information
91  mc_reco_track = track_match_look_up.getRelatedMCRecoTrack(reco_track)
92  mc_particle = track_match_look_up.getRelatedMCParticle(reco_track)
93  mc_particle_crops = peelers.peel_mc_particle(mc_particle)
94 
95  hit_content_crops = peelers.peel_reco_track_hit_content(reco_track)
96 
97  pr_to_mc_match_info_crops = self.peel_pr_to_mc_match_info(reco_track)
98 
99  # Peel function to get hit purity of subdetectors
100  subdetector_hit_purity_crops = peelers.peel_subdetector_hit_purity(reco_track, mc_reco_track)
101 
102  # Information on TrackFinders
103  trackfinder_crops = peelers.peel_trackfinder(reco_track)
104 
105  # Basic peel function to get Quality Indicators
106  qualityindicator_crops = peelers.peel_quality_indicators(reco_track)
107 
108  # Get the fit results
109  seed_fit_crops = peelers.peel_reco_track_seed(reco_track)
110 
111  fit_result = track_match_look_up.getRelatedTrackFitResult(reco_track)
112  fit_crops = peelers.peel_track_fit_result(fit_result)
113  fit_status_crops = peelers.peel_fit_status(reco_track)
114 
115  correct_rl_information = sum(peelers.is_correct_rl_information(cdc_hit, reco_track, self.mc_hit_lookup)
116  for cdc_hit in reco_track.getCDCHitList())
117 
118  crops = dict(
119  correct_rl_information=correct_rl_information,
120  **mc_particle_crops,
121  **hit_content_crops,
122  **pr_to_mc_match_info_crops,
123  **subdetector_hit_purity_crops, # Custom
124  **trackfinder_crops,
125  **qualityindicator_crops,
126  **seed_fit_crops,
127  **fit_crops,
128  **fit_status_crops,
129  )
130 
131  if self.expert_level >= self.default_expert_level:
132 
133  # Event Info
134  event_meta_data = Belle2.PyStoreObj("EventMetaData")
135  event_crops = peelers.peel_event_info(event_meta_data)
136 
137  # Store Array for easier joining
138  store_array_crops = peelers.peel_store_array_info(reco_track, key="pr_{part_name}")
139  mc_store_array_crops = peelers.peel_store_array_info(mc_reco_track, key="mc_{part_name}")
140 
141  # Information on PR reco track
142  mc_efficiency_information = {
143  "mc_hit_efficiency": track_match_look_up.getRelatedEfficiency(mc_reco_track) if mc_reco_track else float("nan"),
144  **peelers.peel_subdetector_hit_efficiency(reco_track=reco_track, mc_reco_track=mc_reco_track,
145  key="mc_{part_name}")
146  }
147 
148  crops.update(
149  **event_crops,
150  **store_array_crops,
151  **mc_store_array_crops,
152  **mc_efficiency_information
153  )
154 
155  return crops
156 
157  def peel_pr_to_mc_match_info(self, reco_track):
158  """Extracts track-match information from the MCMatcherTracksModule results"""
159  track_match_look_up = self.track_match_look_up
160  is_matched = track_match_look_up.isMatchedPRRecoTrack(reco_track)
161  is_clone = track_match_look_up.isClonePRRecoTrack(reco_track)
162  is_background = track_match_look_up.isBackgroundPRRecoTrack(reco_track)
163  is_ghost = track_match_look_up.isGhostPRRecoTrack(reco_track)
164 
165  reco_track_det_hit_ids = utilities.get_det_hit_ids(reco_track)
166  n_intersecting_mc_tracks = 0
167  for mc_reco_track_det_hit_ids in self.mc_reco_tracks_det_hit_ids:
168  intersects = len(mc_reco_track_det_hit_ids & reco_track_det_hit_ids) > 0
169  if intersects:
170  n_intersecting_mc_tracks += 1
171 
172  mc_particle = track_match_look_up.getRelatedMCParticle(reco_track)
173  mc_is_primary = False
174  if mc_particle:
175  mc_is_primary = bool(mc_particle.hasStatus(Belle2.MCParticle.c_PrimaryParticle))
176 
177  return dict(
178  is_matched=is_matched,
179  is_matchedPrimary=is_matched and mc_is_primary,
180  is_clone=is_clone,
181  is_background=is_background,
182  is_ghost=is_ghost,
183  is_clone_or_match=(is_matched or is_clone),
184  is_fake=not (is_matched or is_clone),
185  hit_purity=track_match_look_up.getRelatedPurity(reco_track),
186  n_intersecting_mc_tracks=n_intersecting_mc_tracks,
187  )
188 
189  # Refiners to be executed on terminate #
190  # #################################### #
191 
192 
193  save_tree = refiners.save_tree(folder_name="pr_tree", name="pr_tree", above_expert_level=default_expert_level)
194 
195 
196  save_clone_rate = refiners.save_fom(
197  name="{module.id}_overview_figures_of_merit",
198  # Same as in the mc side module to combine the overview figures of merit into the same TNTuple
199  title="Overview figures in {module.title}",
200  description="clone_rate - ratio of clones divided the number of tracks that are related to a particle (clones and matches)",
201  key="clone rate",
202  select=["is_clone"],
203  aggregation=np.mean,
204  filter_on="is_clone_or_match",
205  )
206 
207 
209  save_clone_rate_by_seed_tan_lambda_profile = refiners.save_profiles(
210  filter_on="is_clone_or_match",
211  select={
212  'is_clone': 'clone rate',
213  'seed_tan_lambda_estimate': 'seed tan #lambda',
214  },
215  y='clone rate',
216  y_binary=True,
217  outlier_z_score=5.0,
218  lower_bound=-1.73,
219  upper_bound=3.27,
220  bins=50
221  )
222 
223 
225  save_clone_rate_by_seed_phi0_profile = refiners.save_profiles(
226  select={
227  'is_clone': 'clone rate',
228  'seed_phi0_estimate': 'seed #phi',
229  },
230  y='clone rate',
231  y_binary=True,
232  outlier_z_score=5.0,
233  bins=50
234  )
235 
236 
238  save_clone_rate_by_seed_pt_profile = refiners.save_profiles(
239  filter_on="is_clone_or_match",
240  select={
241  'is_clone': 'clone rate',
242  'seed_pt_estimate': 'seed p_{t}',
243  },
244  y='clone rate',
245  y_binary=True,
246  outlier_z_score=5.0,
247  lower_bound=0,
248  upper_bound=1.7,
249  bins=50
250  )
251 
252 
253  save_fake_rate = refiners.save_fom(
254  name="{module.id}_overview_figures_of_merit",
255  # Same as in the mc side module to combine the overview figures of merit into the same TNTuple
256  title="Overview figures in {module.title}",
257  description="fake_rate - ratio of pattern recognition tracks that are not related to a particle" +
258  "(background, ghost) to all pattern recognition tracks",
259  key="fake rate",
260  select="is_fake",
261  aggregation=np.mean,
262  )
263 
264 
266  save_fake_rate_by_seed_phi0_profile = refiners.save_profiles(
267  select={
268  'is_fake': 'fake rate',
269  'seed_phi0_estimate': 'seed #phi',
270  },
271  y='fake rate',
272  y_binary=True,
273  outlier_z_score=5.0,
274  )
275 
276 
278  save_fake_rate_by_seed_tan_lambda_profile = refiners.save_profiles(
279  select={
280  'is_fake': 'fake rate',
281  'seed_tan_lambda_estimate': 'seed tan #lambda',
282  },
283  y='fake rate',
284  y_binary=True,
285  outlier_z_score=5.0,
286  lower_bound=-1.73,
287  upper_bound=3.27,
288  )
289 
290 
292  save_fake_rate_by_seed_pt_profile = refiners.save_profiles(
293  select={
294  'is_fake': 'fake rate',
295  'seed_pt_estimate': 'seed p_{t}',
296  },
297  y='fake rate',
298  y_binary=True,
299  outlier_z_score=5.0,
300  lower_bound=0,
301  upper_bound=1.7,
302  )
303 
304 
305  save_hit_counts_by_pt_profile = refiners.save_profiles(
306  filter_on="is_matched",
307  select={
308  "pt_truth": "true p_{t}",
309  "n_pxd_hits": "pxd hits",
310  "n_svd_hits": "svd hits",
311  "n_cdc_hits": "cdc hits",
312  },
313  y=[
314  "pxd hits",
315  "svd hits",
316  "cdc hits",
317  ]
318  )
319 
320 
321  save_hit_efficiency_by_pt_profile = refiners.save_profiles(
322  filter_on="is_matchedPrimary",
323  select={
324  "pt_truth": "true p_{t}",
325  "mc_pxd_hit_efficiency": "pxd hit efficiency",
326  "mc_svd_hit_efficiency": "svd hit efficiency",
327  "mc_cdc_hit_efficiency": "cdc hit efficiency",
328  },
329  y=[
330  "pxd hit efficiency",
331  "svd hit efficiency",
332  "cdc hit efficiency",
333  ]
334  )
335 
336 
337  save_hit_purity_by_pt_profile = refiners.save_profiles(
338  filter_on="is_matchedPrimary",
339  select={
340  "pt_truth": "true p_{t}",
341  "pxd_hit_purity": "pxd hit purity",
342  "svd_hit_purity": "svd hit purity",
343  "cdc_hit_purity": "cdc hit purity",
344  },
345  y=[
346  "pxd hit purity",
347  "svd hit purity",
348  "cdc hit purity",
349  ]
350  )
351 
352 
353  save_hit_counts_by_tanlambda_profile = refiners.save_profiles(
354  filter_on="is_matched",
355  select={
356  "tan_lambda_truth": "true tan #lambda",
357  "n_pxd_hits": "pxd hits",
358  "n_svd_hits": "svd hits",
359  "n_cdc_hits": "cdc hits",
360  },
361  y=[
362  "pxd hits",
363  "svd hits",
364  "cdc hits",
365  ]
366  )
367 
368 
369  save_hit_efficiency_by_tanlambda_profile = refiners.save_profiles(
370  filter_on="is_matchedPrimary",
371  select={
372  "tan_lambda_truth": "true tan #lambda",
373  "mc_pxd_hit_efficiency": "pxd hit efficiency",
374  "mc_svd_hit_efficiency": "svd hit efficiency",
375  "mc_cdc_hit_efficiency": "cdc hit efficiency",
376  },
377  y=[
378  "pxd hit efficiency",
379  "svd hit efficiency",
380  "cdc hit efficiency",
381  ]
382  )
383 
384 
385  save_hit_purity_by_tanlambda_profile = refiners.save_profiles(
386  filter_on="is_matchedPrimary",
387  select={
388  "tan_lambda_truth": "true tan #lambda",
389  "pxd_hit_purity": "pxd hit purity",
390  "svd_hit_purity": "svd hit purity",
391  "cdc_hit_purity": "cdc hit purity",
392  },
393  y=[
394  "pxd hit purity",
395  "svd hit purity",
396  "cdc hit purity",
397  ]
398  )
399 
400 
401  save_hit_efficiency = refiners.save_fom(
402  name="{module.id}_subdetector_figures_of_merit",
403  title="Overview figures in {module.title}",
404  description="Hit efficiency in the subdetectors",
405  key="hit efficiency",
406  select="mc_hit_efficiency",
407  aggregation=np.nanmean,
408  filter_on="is_matchedPrimary"
409  )
410 
411 
412  save_pxd_hit_efficiency = refiners.save_fom(
413  name="{module.id}_subdetector_figures_of_merit",
414  title="Overview figures in {module.title}",
415  description="Hit efficiency in the subdetectors",
416  key="pxd hit efficiency",
417  select="mc_pxd_hit_efficiency",
418  aggregation=np.nanmean,
419  filter_on="is_matchedPrimary"
420  )
421 
422 
423  save_svd_hit_efficiency = refiners.save_fom(
424  name="{module.id}_subdetector_figures_of_merit",
425  title="Overview figures in {module.title}",
426  description="Hit efficiency in the subdetectors",
427  key="svd hit efficiency",
428  select="mc_svd_hit_efficiency",
429  aggregation=np.nanmean,
430  filter_on="is_matchedPrimary"
431  )
432 
433 
434  save_cdc_hit_efficiency = refiners.save_fom(
435  name="{module.id}_subdetector_figures_of_merit",
436  title="Overview figures in {module.title}",
437  description="Hit efficiency in the subdetectors",
438  key="cdc hit efficiency",
439  select="mc_cdc_hit_efficiency",
440  aggregation=np.nanmean,
441  filter_on="is_matchedPrimary"
442  )
443 
444 
445  save_hit_purity = refiners.save_fom(
446  name="{module.id}_subdetector_figures_of_merit",
447  title="Overview figures in {module.title}",
448  description="Hit purity in the subdetectors",
449  key="hit purity",
450  select="hit_purity",
451  aggregation=np.nanmean,
452  filter_on="is_matchedPrimary"
453  )
454 
455 
456  save_pxd_hit_purity = refiners.save_fom(
457  name="{module.id}_subdetector_figures_of_merit",
458  title="Overview figures in {module.title}",
459  description="Hit purity in the subdetectors",
460  key="pxd hit purity",
461  select="pxd_hit_purity",
462  aggregation=np.nanmean,
463  filter_on="is_matchedPrimary"
464  )
465 
466 
467  save_svd_hit_purity = refiners.save_fom(
468  name="{module.id}_subdetector_figures_of_merit",
469  title="Overview figures in {module.title}",
470  description="Hit purity in the subdetectors",
471  key="svd hit purity",
472  select="svd_hit_purity",
473  aggregation=np.nanmean,
474  filter_on="is_matchedPrimary"
475  )
476 
477 
478  save_cdc_hit_purity = refiners.save_fom(
479  name="{module.id}_subdetector_figures_of_merit",
480  title="Overview figures in {module.title}",
481  description="Hit purity in the subdetectors",
482  key="cdc hit purity",
483  select="cdc_hit_purity",
484  aggregation=np.nanmean,
485  filter_on="is_matchedPrimary"
486  )
487 
488 
489  save_p_value_histogram = refiners.save_histograms(
490  filter_on="is_matched",
491  select={"p_value": "Genfit p value"},
492  description="""
493  The distribution of p values from the Genfit track fit.
494  If all errors are propagated correctly the distribution should be flat.
495  Generally some peaking behvaiour towards zero is too be expected if the errors are underestimated.
496  """,
497  check="The distribution should be flat."
498  )
499 
500 
501  save_seed_omega_pull_analysis = refiners.save_pull_analysis(
502  filter_on="is_matched",
503  part_name="seed_omega",
504  quantity_name="seed #omega",
505  folder_name="pull_seed_omega",
506  truth_name="omega_truth",
507  unit="1/cm",
508  )
509 
510 
511  save_seed_tan_lambda_pull_analysis = refiners.save_pull_analysis(
512  filter_on="is_matched",
513  part_name="seed_tan_lambda",
514  quantity_name="seed tan #lambda",
515  folder_name="pull_seed_tan_lambda",
516  truth_name="tan_lambda_truth",
517  )
518 
519 
520  save_fitted_omega_pull_analysis = refiners.save_pull_analysis(
521  filter_on="is_matched",
522  part_name="omega",
523  quantity_name="#omega",
524  folder_name="pull_fitted_omega",
525  unit="1/cm",
526  )
527 
528 
529  save_fitted_tan_lambda_pull_analysis = refiners.save_pull_analysis(
530  filter_on="is_matched",
531  part_name="tan_lambda",
532  quantity_name="tan #lambda",
533  folder_name="pull_fitted_tan_lambda",
534  )
535 
536 
537  save_fitted_pt_pull_analysis = refiners.save_pull_analysis(
538  filter_on="is_matched",
539  part_name="pt",
540  quantity_name="p_{t}",
541  folder_name="pull_fitted_p_t",
542  )
543 
544 
545  save_fitted_x_pull_analysis = refiners.save_pull_analysis(
546  filter_on="is_matched",
547  part_name="x",
548  quantity_name="x",
549  folder_name="pull_fitted_x{groupby_addition}",
550  groupby=[None, ("pt_truth", [0.070, 0.250, 0.600])],
551  )
552 
553 
554  save_fitted_y_pull_analysis = refiners.save_pull_analysis(
555  filter_on="is_matched",
556  part_name="y",
557  quantity_name="y",
558  folder_name="pull_fitted_y{groupby_addition}",
559  groupby=[None, ("pt_truth", [0.070, 0.250, 0.600])],
560  )
561 
562 
563  save_fitted_z_pull_analysis = refiners.save_pull_analysis(
564  filter_on="is_matched",
565  part_name="z",
566  quantity_name="z",
567  folder_name="pull_fitted_z{groupby_addition}",
568  groupby=[None, ("pt_truth", [0.070, 0.250, 0.600])],
569  )
570 
571 
572  save_resolutions_by_pt_profile = refiners.save_profiles(
573  filter_on="is_matched",
574  select={
575  "pt_truth": "true p_{t}",
576  "d0_variance": "#sigma(d_{0})",
577  "z0_variance": "#sigma(z_{0})",
578  "pt_resolution": "#sigma(p_{t}) / p_{t}",
579  },
580  y=[
581  "#sigma(d_{0})",
582  "#sigma(z_{0})",
583  "#sigma(p_{t}) / p_{t}",
584  ],
585  y_log=True,
586  )
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.__init__
def __init__(self, name, contact, output_file_name=None, reco_tracks_name='RecoTracks', mc_reco_tracks_name='MCRecoTracks', expert_level=None)
Definition: pr_side_module.py:30
tracking.harvest.refiners
Definition: refiners.py:1
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.initialize
def initialize(self)
Definition: pr_side_module.py:62
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.pick
def pick(self, reco_track)
Definition: pr_side_module.py:82
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.peel_pr_to_mc_match_info
def peel_pr_to_mc_match_info(self, reco_track)
Definition: pr_side_module.py:157
Belle2::PyStoreObj
a (simplified) python wrapper for StoreObjPtr.
Definition: PyStoreObj.h:69
Belle2::TrackMatchLookUp
Class to provide convenient methods to look up matching information between pattern recognition and M...
Definition: TrackMatchLookUp.h:43
tracking.harvest.harvesting
Definition: harvesting.py:1
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.default_expert_level
int default_expert_level
the threshold value for the expert level
Definition: pr_side_module.py:28
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.track_match_look_up
track_match_look_up
Reference to the track match lookup object reading the relation information constructed by the MCMatc...
Definition: pr_side_module.py:47
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.mc_reco_tracks_name
mc_reco_tracks_name
Name of the StoreArray of the ideal mc tracks.
Definition: pr_side_module.py:44
tracking.harvest.peelers
Definition: peelers.py:1
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.reco_tracks_name
reco_tracks_name
Name of the StoreArray of the tracks from pattern recognition.
Definition: pr_side_module.py:41
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule
Definition: pr_side_module.py:19
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.mc_reco_tracks_det_hit_ids
mc_reco_tracks_det_hit_ids
Cache for the hit content of the Monte Carlo tracks - updated each event.
Definition: pr_side_module.py:50
Belle2::TrackFindingCDC::CDCMCHitLookUp::getInstance
static const CDCMCHitLookUp & getInstance()
Getter for the singletone instance.
Definition: CDCMCHitLookUp.cc:32
tracking.validation.utilities
Definition: utilities.py:1
Belle2::PyStoreArray
a (simplified) python wrapper for StoreArray.
Definition: PyStoreArray.h:58
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.mc_hit_lookup
mc_hit_lookup
Cache for the MC hit lookup.
Definition: pr_side_module.py:53
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.peel
def peel(self, reco_track)
Definition: pr_side_module.py:86
tracking.harvesting_validation.pr_side_module.PRSideTrackingValidationModule.prepare
def prepare(self)
Definition: pr_side_module.py:68