Belle II Software  release-06-01-15
mc_side_module.py
1 
8 
9 import ROOT
10 from ROOT import Belle2
11 
12 import numpy as np
13 
14 import tracking.validation.utilities as utilities
15 
16 import tracking.harvest.refiners as refiners
17 import tracking.harvest.harvesting as harvesting
18 import tracking.harvest.peelers as peelers
19 ROOT.gSystem.Load("libtracking")
20 
21 
22 class MCSideTrackingValidationModule(harvesting.HarvestingModule):
23  """Module to collect matching information about the found particles and to generate
24  validation plots and figures of merit on the performance of track finding."""
25 
26  """ Expert level behavior:
27  expert_level = default_expert_level: all figures and plots from this module except tree entries
28  expert_level > default_expert_level: everything including tree entries
29  expert_level <= default_expert_level//2: only basic figures
30  default_expert_level//2 < expert_level < default_expert_level: basic figures and basic tree entries
31  """
32 
33  default_expert_level = 10
34 
35  def __init__(
36  self,
37  name,
38  contact,
39  output_file_name=None,
40  reco_tracks_name='RecoTracks',
41  mc_reco_tracks_name='MCRecoTracks',
42  expert_level=None):
43  """Constructor"""
44 
45  output_file_name = output_file_name or name + 'TrackingValidation.root'
46 
47  super().__init__(foreach=mc_reco_tracks_name,
48  name=name,
49  output_file_name=output_file_name,
50  contact=contact,
51  expert_level=expert_level)
52 
53 
54  self.reco_tracks_namereco_tracks_name = reco_tracks_name
55 
56 
57  self.mc_reco_tracks_namemc_reco_tracks_name = mc_reco_tracks_name
58 
59 
60  self.track_match_look_uptrack_match_look_up = None
61 
62  if self.expert_level >= self.default_expert_leveldefault_expert_level:
63 
64  self.found_det_hit_idsfound_det_hit_ids = set()
65 
66 
67  self.matched_det_hit_idsmatched_det_hit_ids = set()
68 
69 
70  self.clone_det_hit_idsclone_det_hit_ids = set()
71 
72 
73  self.fake_det_hit_idsfake_det_hit_ids = set()
74 
75  def initialize(self):
76  """Initialization signal at the start of the event processing"""
77  super().initialize()
78  self.track_match_look_uptrack_match_look_up = Belle2.TrackMatchLookUp(self.mc_reco_tracks_namemc_reco_tracks_name, self.reco_tracks_namereco_tracks_name)
79 
80  def prepare(self):
81  """Collect some statistics about the pattern recognition tracks used for comparision to the MC tracks
82 
83  Executed once at the start of each event.
84  """
85  super().prepare()
86  if self.expert_level >= self.default_expert_leveldefault_expert_level:
87  reco_tracks = Belle2.PyStoreArray(self.reco_tracks_namereco_tracks_name)
88  track_match_look_up = self.track_match_look_uptrack_match_look_up
89 
90  found_det_hit_ids = set()
91  matched_det_hit_ids = set()
92  clone_det_hit_ids = set()
93  fake_det_hit_ids = set()
94 
95  for reco_track in reco_tracks:
96  det_hit_ids = utilities.get_det_hit_ids(reco_track)
97 
98  found_det_hit_ids |= det_hit_ids
99 
100  if track_match_look_up.isMatchedPRRecoTrack(reco_track):
101  matched_det_hit_ids |= det_hit_ids
102 
103  if track_match_look_up.isClonePRRecoTrack(reco_track):
104  clone_det_hit_ids |= det_hit_ids
105 
106  if (track_match_look_up.isGhostPRRecoTrack(reco_track) or
107  track_match_look_up.isBackgroundPRRecoTrack(reco_track)):
108  fake_det_hit_ids |= det_hit_ids
109 
110  self.found_det_hit_idsfound_det_hit_ids = found_det_hit_ids
111  self.matched_det_hit_idsmatched_det_hit_ids = matched_det_hit_ids
112  self.clone_det_hit_idsclone_det_hit_ids = clone_det_hit_ids
113  self.fake_det_hit_idsfake_det_hit_ids = fake_det_hit_ids
114 
115  def pick(self, mc_reco_track):
116  """Pick every MCRecoTrack"""
117  return True
118 
119  def peel(self, mc_reco_track):
120  """Looks at the individual Monte Carlo tracks and store information about them"""
121  track_match_look_up = self.track_match_look_uptrack_match_look_up
122 
123  # Analyse from the Monte Carlo reference side
124  mc_reco_tracks = Belle2.PyStoreArray(self.foreach)
125  multiplicity = mc_reco_tracks.getEntries()
126 
127  mc_particle = track_match_look_up.getRelatedMCParticle(mc_reco_track)
128  is_primary = bool(mc_particle.hasStatus(Belle2.MCParticle.c_PrimaryParticle))
129  mc_to_pr_match_info_crops = self.peel_mc_to_pr_match_infopeel_mc_to_pr_match_info(mc_reco_track)
130  mc_store_array_crops = peelers.peel_store_array_info(mc_reco_track, key="mc_{part_name}")
131 
132  crops = dict(is_primary=is_primary,
133  multiplicity=multiplicity,
134  **mc_to_pr_match_info_crops,
135  **mc_store_array_crops
136  )
137 
138  if self.expert_level >= self.default_expert_leveldefault_expert_level:
139  reco_track = track_match_look_up.getRelatedPRRecoTrack(mc_reco_track)
140  mc_particle_crops = peelers.peel_mc_particle(mc_particle)
141  hit_content_crops = peelers.peel_reco_track_hit_content(mc_reco_track)
142 
143  # Custom peel function to get single detector hit purities
144  subdetector_hit_efficiency_crops = peelers.peel_subdetector_hit_efficiency(mc_reco_track, reco_track)
145 
146  mc_hit_efficiencies_in_all_pr_tracks_crops = self.peel_hit_efficiencies_in_all_pr_trackspeel_hit_efficiencies_in_all_pr_tracks(mc_reco_track)
147 
148  # Event Info
149  event_meta_data = Belle2.PyStoreObj("EventMetaData")
150  event_crops = peelers.peel_event_info(event_meta_data)
151 
152  # Store Array for easier joining
153  store_array_crops = peelers.peel_store_array_info(reco_track, key="pr_{part_name}")
154 
155  # Information on PR reco track
156  pr_purity_information = {
157  "pr_hit_purity": track_match_look_up.getRelatedPurity(reco_track) if reco_track else float("nan"),
158  **peelers.peel_subdetector_hit_purity(reco_track=reco_track, mc_reco_track=mc_reco_track,
159  key="pr_{part_name}")
160  }
161 
162  # Information on TrackFinders
163  trackfinder_crops = peelers.peel_trackfinder(reco_track)
164 
165  # Basic peel function to get Quality Indicators
166  qualityindicator_crops = peelers.peel_quality_indicators(reco_track)
167 
168  crops.update(dict(**hit_content_crops,
169  **mc_particle_crops,
170  **subdetector_hit_efficiency_crops,
171  **mc_hit_efficiencies_in_all_pr_tracks_crops,
172  **event_crops,
173  **store_array_crops,
174  **pr_purity_information,
175  **trackfinder_crops,
176  **qualityindicator_crops
177  ))
178 
179  return crops
180 
181  def peel_mc_to_pr_match_info(self, mc_reco_track):
182  """Extracts track-match information from the MCMatcherTracksModule results"""
183  track_match_look_up = self.track_match_look_uptrack_match_look_up
184  return dict(
185  is_matched=track_match_look_up.isMatchedMCRecoTrack(mc_reco_track),
186  is_merged=track_match_look_up.isMergedMCRecoTrack(mc_reco_track),
187  is_missing=track_match_look_up.isMissingMCRecoTrack(mc_reco_track),
188  hit_efficiency=track_match_look_up.getRelatedEfficiency(mc_reco_track),
189  )
190 
191  def peel_hit_efficiencies_in_all_pr_tracks(self, mc_reco_track):
192  """Extracts hit efficiencies"""
193  mc_det_hit_ids = utilities.get_det_hit_ids(mc_reco_track)
194 
195  hit_efficiency_in_all_found = utilities.calc_hit_efficiency(self.found_det_hit_idsfound_det_hit_ids,
196  mc_det_hit_ids)
197 
198  unfound_hit_efficiency = 1.0 - hit_efficiency_in_all_found
199 
200  hit_efficiency_in_all_matched = utilities.calc_hit_efficiency(self.matched_det_hit_idsmatched_det_hit_ids,
201  mc_det_hit_ids)
202 
203  hit_efficiency_in_all_fake = utilities.calc_hit_efficiency(self.fake_det_hit_idsfake_det_hit_ids,
204  mc_det_hit_ids)
205 
206  hit_efficiency_crops = dict(
207  hit_efficiency_in_all_found=hit_efficiency_in_all_found,
208  unfound_hit_efficiency=unfound_hit_efficiency,
209  hit_efficiency_in_all_matched=hit_efficiency_in_all_matched,
210  hit_efficiency_in_all_fake=hit_efficiency_in_all_fake,
211  )
212  return hit_efficiency_crops
213 
214  # Refiners to be executed on terminate #
215  # #################################### #
216 
217 
218  save_tree = refiners.save_tree(
219  # using cond to suppress false doxygen warnings
220 
221  name="mc_tree",
222  folder_name="mc_tree",
223  above_expert_level=default_expert_level
224 
225  )
226 
227 
228  save_tree_basic = refiners.save_tree(
229  # using cond to suppress false doxygen warnings
230 
231  name="mc_tree",
232  folder_name="mc_tree",
233  above_expert_level=default_expert_level // 2,
234  below_expert_level=default_expert_level
235 
236  )
237 
238 
239  save_overview_figures_of_merit = refiners.save_fom(
240 
241  name="{module.id}_overview_figures_of_merit",
242  title="Overview figures in {module.title}",
243  aggregation=np.nanmean,
244  key="{part_name}",
245  select={"is_matched": "finding efficiency", "hit_efficiency": "hit efficiency", },
246  filter_on="is_primary",
247  description="""
248 finding efficiency - the ratio of matched primary Monte Carlo tracks to all Monte Carlo tracks
249 hit efficiency - the ratio of hits picked up by the matched pattern recognition track of primary Monte Carlo tracks
250 """
251 
252  )
253 
254  # Default refiners that can be disabled with a lower expert_level
255  # #################################### #
256 
257 
258  save_hit_efficiency_histogram = refiners.save_histograms(
259 
260  above_expert_level=default_expert_level - 1,
261  select={"hit_efficiency": "hit efficiency"},
262  filter_on="is_primary",
263  description="Not a serious plot yet.",
264 
265  )
266 
267 
268  renaming_select_for_finding_efficiency_profiles = {
269  'is_matched': 'finding efficiency',
270  'd0_truth': 'd_{0}',
271  'pt_truth': 'p_{t}',
272  'multiplicity': 'multiplicity',
273  'phi0_truth': '#phi',
274  }
275 
276 
277  save_finding_efficiency_profiles = refiners.save_profiles(
278 
279  above_expert_level=default_expert_level - 1,
280  select=renaming_select_for_finding_efficiency_profiles,
281  y='finding efficiency',
282  y_binary=True,
283  filter_on="is_primary",
284  outlier_z_score=5.0,
285  allow_discrete=True,
286 
287  )
288 
289 
290  save_finding_efficiency_by_tan_lamba_profiles = refiners.save_profiles(
291 
292  above_expert_level=default_expert_level - 1,
293  select={
294  'is_matched': 'finding efficiency',
295  'tan_lambda_truth': 'tan #lambda'
296  },
297  y='finding efficiency',
298  y_binary=True,
299  filter_on="is_primary",
300  outlier_z_score=5.0,
301  lower_bound=-1.73,
302  upper_bound=3.27,
303 
304  )
305 
306 
307  save_finding_efficiency_by_tan_lamba_in_pt_groups_profiles = refiners.save_profiles(
308 
309  above_expert_level=default_expert_level - 1,
310  select={
311  'is_matched': 'finding efficiency',
312  'tan_lambda_truth': 'tan #lambda'
313  },
314  y='finding efficiency',
315  y_binary=True,
316  filter_on="is_primary",
317  groupby=[("pt_truth", [0.070, 0.250, 0.600])],
318  outlier_z_score=5.0,
319  lower_bound=-1.73,
320  upper_bound=3.27,
321 
322  )
323 
324  # Make profiles of the hit efficiencies versus various fit parameters
325 
326  renaming_select_for_hit_efficiency_profiles = {
327  'hit_efficiency': 'hit efficiency',
328  'd0_truth': 'd_{0}',
329  'pt_truth': 'p_{t}',
330  'multiplicity': 'multiplicity',
331  'phi0_truth': '#phi',
332  }
333 
334 
335  save_hit_efficiency_profiles = refiners.save_profiles(
336 
337  above_expert_level=default_expert_level - 1,
338  select=renaming_select_for_hit_efficiency_profiles,
339  y='hit efficiency',
340  y_binary=True,
341  filter_on="is_primary",
342  outlier_z_score=5.0,
343  allow_discrete=True,
344 
345  )
346 
347 
348  save_hit_efficiency_by_tan_lambda_profiles = refiners.save_profiles(
349 
350  above_expert_level=default_expert_level - 1,
351  select={
352  'hit_efficiency': 'hit efficiency',
353  'tan_lambda_truth': 'tan #lambda',
354  },
355  y='hit efficiency',
356  y_binary=True,
357  filter_on="is_primary",
358  outlier_z_score=5.0,
359  lower_bound=-1.73,
360  upper_bound=3.27,
361 
362  )
363 
364 
366  save_finding_efficiency_by_pt_profiles_groupbyCharge = refiners.save_profiles(
367 
368  above_expert_level=default_expert_level - 1,
369  select={
370  'is_matched': 'finding efficiency',
371  'pt_truth': 'p_{t}',
372  },
373  y='finding efficiency',
374  y_binary=True,
375  filter_on="is_primary",
376  groupby=[("charge_truth", [0.])],
377  outlier_z_score=5.0,
378  allow_discrete=True,
379 
380  )
381 
382 
384  save_finding_efficiency_by_tan_lambda_profiles_groupbyCharge = refiners.save_profiles(
385 
386  above_expert_level=default_expert_level - 1,
387  select={
388  'is_matched': 'finding efficiency',
389  'tan_lambda_truth': 'tan #lambda'
390  },
391  y='finding efficiency',
392  y_binary=True,
393  filter_on="is_primary",
394  groupby=[("charge_truth", [0.])],
395  outlier_z_score=5.0,
396  lower_bound=-1.73,
397  upper_bound=3.27,
398 
399  )
400 
401 
403  save_hit_efficiency_by_pt_profiles_groupbyCharge = refiners.save_profiles(
404 
405  above_expert_level=default_expert_level - 1,
406  select={
407  'hit_efficiency': 'hit efficiency',
408  'pt_truth': 'p_{t}',
409  },
410  y='hit efficiency',
411  y_binary=True,
412  filter_on="is_primary",
413  groupby=[("charge_truth", [0.])],
414  outlier_z_score=5.0,
415  allow_discrete=True,
416 
417  )
418 
419 
421  save_hit_efficiency_by_tan_lambda_profiles_groupbyCharge = refiners.save_profiles(
422 
423  above_expert_level=default_expert_level - 1,
424  select={
425  'hit_efficiency': 'hit efficiency',
426  'tan_lambda_truth': 'tan #lambda',
427  },
428  y='hit efficiency',
429  y_binary=True,
430  filter_on="is_primary",
431  groupby=[("charge_truth", [0.])],
432  outlier_z_score=5.0,
433  lower_bound=-1.73,
434  upper_bound=3.27,
435 
436  )
437 
438 
441  save_hit_efficiency_in_all_found_hist = refiners.save_histograms(
442 
443  above_expert_level=default_expert_level - 1,
444  # renaming quantity to name that is more suitable for display
445  select=dict(hit_efficiency_in_all_found="total hit efficiency vs. all reconstructed tracks")
446 
447  )
448 
449 
452  save_missing_mc_tracks_hit_efficiency_in_all_found_hist = refiners.save_histograms(
453 
454  above_expert_level=default_expert_level - 1,
455  filter_on="is_missing", # show only the efficiencies of missing mc tracks
456  # renaming quantity to name that is more suitable for display
457  select=dict(hit_efficiency_in_all_found="total hit efficiency in all reconstructed tracks for missing mc tracks")
458 
459  )
a (simplified) python wrapper for StoreArray.
Definition: PyStoreArray.h:56
a (simplified) python wrapper for StoreObjPtr.
Definition: PyStoreObj.h:67
Class to provide convenient methods to look up matching information between pattern recognition and M...
clone_det_hit_ids
Set of all detector and hits ids contained in clone pr tracks.
track_match_look_up
Reference to the track match lookup object reading the relation information constructed by the MCMatc...
def __init__(self, name, contact, output_file_name=None, reco_tracks_name='RecoTracks', mc_reco_tracks_name='MCRecoTracks', expert_level=None)
matched_det_hit_ids
Set of all detector and hits ids contained in matched pr tracks.
found_det_hit_ids
Set of all detector and hits ids contained in any pr track.
fake_det_hit_ids
Set of all detector and hits ids contained in background and ghost pr tracks.
reco_tracks_name
Name of the StoreArray of the tracks from pattern recognition.