Belle II Software  release-08-01-10
module.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 import math
13 import collections
14 import numpy as np
15 
16 from tracking.root_utils import root_save_name
17 
18 from tracking.validation.plot import ValidationPlot, compose_axis_label
19 
20 from tracking.validation.pull import PullAnalysis
21 from tracking.validation.resolution import ResolutionAnalysis
22 from tracking.validation.fom import ValidationFiguresOfMerit
24  getHelixFromMCParticle,
25  getSeedTrackFitResult,
26  is_primary,
27  get_det_hit_ids,
28  calc_ndf_from_det_hit_ids
29 )
30 
31 import basf2
32 
33 import ROOT
34 from ROOT import Belle2
35 import os
36 
37 ROOT.gSystem.Load("libtracking")
38 
39 
40 class FilterProperties(object):
41  """
42  contains all informations necessary for track filters to decide whether
43  track will be included into the processed list of tracks
44  This class is used for both providing information on pattern reco and
45  MC tracks
46  """
47 
48  def __init__(
49  self,
50  trackCand=None,
51  mcParticle=None,
52  mcParticles=None,
53  wasFitted=False,
54  fitResult=None,
55  seedResult=None,
56  ):
57  """Constructor"""
58 
59 
60  self.trackCandtrackCand = trackCand
61 
62  self.mcParticlemcParticle = mcParticle
63 
64  self.mcParticlesmcParticles = mcParticles
65 
66  self.wasFittedwasFitted = wasFitted
67 
68  self.fitResultfitResult = fitResult
69 
70  self.seedResultseedResult = seedResult
71 
72 
73 # This class will accept all pattern reco and mctracks
74 # It is used as the default option for the TrackingValidationModule
75 #
76 # It can be used as a starting point to implement your own particle filter class
77 # and supply it to the TrackingValidationModule via the init method's
78 # track_filter_object property.
79 #
80 # doesPrPass is called for all pattern reconstructed tracks
81 # Objects available infilterProperties:
82 # trackCand is guaranteed to be != None
83 # If wasFitted == True, fitResult will be set
84 # otherwise seedResult will be set
85 #
86 # doesMcPass is called for all MC tracks
87 # Objects available infilterProperties:
88 # mcParticle is guaranteed to be != None
89 #
90 
91 class AlwaysPassFilter(object):
92  """Filter that always passes"""
93 
94  def doesPrPass(self, filterProperties):
95  """Pattern-reconstructed track always passes"""
96  return True
97 
98  def doesMcPass(self, filterProperties):
99  """MC track always passes"""
100  return True
101 
102 
103 class TrackingValidationModule(basf2.Module):
104 
105  """Module to collect matching information about the found particles and to
106  generate validation plots and figures of merit on the performance of track finding."""
107 
108  def __init__(
109  self,
110  name,
111  contact,
112  fit=False,
113  pulls=False,
114  resolution=False,
115  output_file_name=None,
116  track_filter_object=AlwaysPassFilter(),
117  plot_name_postfix='',
118  plot_title_postfix='',
119  exclude_profile_mc_parameter='',
120  exclude_profile_pr_parameter='',
121  use_expert_folder=True,
122  trackCandidatesColumnName="RecoTracks",
123  mcTrackCandidatesColumName="MCRecoTracks",
124  non_expert_parameters=['p_{t}']
125  ):
126  """Constructor"""
127 
128  super(TrackingValidationModule, self).__init__()
129 
130 
131  self.validation_namevalidation_name = name
132 
133  self.contactcontact = contact
134 
135  self.fitfit = fit
136 
137  self.pullspulls = pulls
138 
139  self.resolutionresolution = resolution
140 
141  self.output_file_nameoutput_file_name = output_file_name or self.validation_namevalidation_name \
142  + 'TrackingValidation.root'
143 
144  self.track_filter_objecttrack_filter_object = track_filter_object
145 
146  self.plot_name_postfixplot_name_postfix = plot_name_postfix
147 
148  self.plot_title_postfixplot_title_postfix = plot_title_postfix
149 
150  self.exclude_profile_pr_parameterexclude_profile_pr_parameter = exclude_profile_pr_parameter
151 
152  self.exclude_profile_mc_parameterexclude_profile_mc_parameter = exclude_profile_mc_parameter
153 
154  self.use_expert_folderuse_expert_folder = use_expert_folder
155 
156  self.trackCandidatesColumnNametrackCandidatesColumnName = trackCandidatesColumnName
157 
158  self.mcTrackCandidatesColumnNamemcTrackCandidatesColumnName = mcTrackCandidatesColumName
159 
160  self.non_expert_parametersnon_expert_parameters = non_expert_parameters
161 
162 
163  self.resolution_pt_binningresolution_pt_binning = [0.05, 0.1, 0.25, 0.4, 0.6, 1., 1.5, 2., 3., 4.]
164 
165 
169  self.referenceFileNamereferenceFileName = None
170  if "DO_NOT_READ_BINNING" not in os.environ:
171  # the validity of the file will be checked later
172  self.referenceFileNamereferenceFileName = Belle2.FileSystem.findFile("tracking/validation/" + self.output_file_nameoutput_file_name, True)
173  basf2.B2INFO("Will read binning from: " + self.referenceFileNamereferenceFileName)
174  basf2.B2INFO("If this is not wanted set the environment variable DO_NOT_READ_BINNING or remove reference files.")
175  else:
176  basf2.B2INFO("Will not read binning from reference files.")
177 
178  def initialize(self):
179  """Receive signal at the start of event processing"""
180 
181 
182  self.trackMatchLookUptrackMatchLookUp = Belle2.TrackMatchLookUp(self.mcTrackCandidatesColumnNamemcTrackCandidatesColumnName, self.trackCandidatesColumnNametrackCandidatesColumnName)
183 
184 
185 
186 
187  self.pr_clones_and_matchespr_clones_and_matches = collections.deque()
188 
189  self.pr_matchespr_matches = collections.deque()
190 
191  self.pr_fakespr_fakes = collections.deque()
192 
193 
194  self.pr_seed_ptpr_seed_pt = collections.deque()
195 
196  self.pr_seed_tan_lambdaspr_seed_tan_lambdas = collections.deque()
197 
198  self.pr_seed_phipr_seed_phi = collections.deque()
199 
200 
201  self.pr_omega_truthspr_omega_truths = collections.deque()
202 
203  self.pr_omega_estimatespr_omega_estimates = collections.deque()
204 
205  self.pr_omega_variancespr_omega_variances = collections.deque()
206 
207 
208  self.pr_tan_lambda_truthspr_tan_lambda_truths = collections.deque()
209 
210  self.pr_tan_lambda_estimatespr_tan_lambda_estimates = collections.deque()
211 
212  self.pr_tan_lambda_variancespr_tan_lambda_variances = collections.deque()
213 
214 
215  self.pr_d0_truthspr_d0_truths = collections.deque()
216 
217  self.pr_d0_estimatespr_d0_estimates = collections.deque()
218 
219  self.pr_d0_variancespr_d0_variances = collections.deque()
220 
221 
222  self.pr_z0_truthspr_z0_truths = collections.deque()
223 
224  self.pr_z0_estimatespr_z0_estimates = collections.deque()
225 
226 
227  self.pr_pt_truthspr_pt_truths = collections.deque()
228 
229  self.pr_pt_estimatespr_pt_estimates = collections.deque()
230 
231 
232  self.pr_bining_ptpr_bining_pt = collections.deque()
233 
234 
235  self.mc_matchesmc_matches = collections.deque()
236 
237  self.mc_charge_matchesmc_charge_matches = collections.deque()
238 
239  self.mc_charge_asymmetrymc_charge_asymmetry = collections.deque()
240 
241  self.mc_charge_asymmetry_weightsmc_charge_asymmetry_weights = collections.deque()
242 
243  self.mc_primariesmc_primaries = collections.deque()
244 
245  self.mc_d0smc_d0s = collections.deque()
246 
247  self.mc_tan_lambdasmc_tan_lambdas = collections.deque()
248 
249  self.mc_phimc_phi = collections.deque()
250 
251  self.mc_ptsmc_pts = collections.deque()
252 
253  self.mc_hit_efficienciesmc_hit_efficiencies = collections.deque()
254 
255  self.mc_multiplicitiesmc_multiplicities = collections.deque()
256 
257  self.mc_ndfmc_ndf = collections.deque()
258 
259  def event(self):
260  """Event method"""
261 
262  self.examine_pr_tracksexamine_pr_tracks()
263  self.examine_mc_tracksexamine_mc_tracks()
264 
265  def examine_pr_tracks(self):
266  """Looks at the individual pattern reconstructed tracks and store information about them"""
267 
268  # Analyse from the pattern recognition side
269  trackMatchLookUp = self.trackMatchLookUptrackMatchLookUp
270 
271  trackCands = Belle2.PyStoreArray(self.trackCandidatesColumnNametrackCandidatesColumnName)
272  mcParticles = Belle2.PyStoreArray("MCParticles")
273  if not trackCands:
274  return
275 
276  for trackCand in trackCands:
277  is_matched = trackMatchLookUp.isAnyChargeMatchedPRRecoTrack(trackCand)
278  is_clone = trackMatchLookUp.isAnyChargeClonePRRecoTrack(trackCand)
279 
280  pt_truth = float('nan')
281  omega_truth = float('nan')
282  tan_lambda_truth = float('nan')
283  d0_truth = float('nan')
284  z0_truth = float('nan')
285 
286  mcParticle = None
287  if is_matched or is_clone:
288  # Only matched and clone tracks have a related MCParticle
289  mcParticle = trackMatchLookUp.getRelatedMCParticle(trackCand)
290  mcHelix = getHelixFromMCParticle(mcParticle)
291  omega_truth = mcHelix.getOmega()
292  tan_lambda_truth = mcHelix.getTanLambda()
293  pt_truth = mcParticle.getMomentum().Rho()
294  d0_truth = mcHelix.getD0()
295  z0_truth = mcHelix.getZ0()
296 
297  # fill the FilterProperties will all properties on this track
298  # gathered so far
299  filterProperties = FilterProperties(trackCand=trackCand,
300  mcParticle=mcParticle, mcParticles=mcParticles)
301 
302  if self.fitfit:
303  prTrackFitResult = \
304  trackMatchLookUp.getRelatedTrackFitResult(trackCand)
305  filterProperties.wasFitted = prTrackFitResult is not None
306  filterProperties.fitResult = prTrackFitResult
307  else:
308  prTrackFitResult = getSeedTrackFitResult(trackCand)
309  filterProperties.seedResult = prTrackFitResult
310 
311  # skip this track due to the filtering rules ?
312  if not self.track_filter_objecttrack_filter_object.doesPrPass(filterProperties):
313  continue
314 
315  omega_estimate = float('nan')
316  omega_variance = float('nan')
317  tan_lambda_estimate = float('nan')
318  tan_lambda_variance = float('nan')
319  d0_estimate = float('nan')
320  d0_variance = float('nan')
321  z0_estimate = float('nan')
322  pt_estimate = float('nan')
323 
324  momentum = float('nan')
325 
326  # store seed information, they are always available from the pattern reco
327  # even if the fit was no successful
328  # this information can we used when plotting fake tracks, for example
329  seed_momentum = trackCand.getMomentumSeed()
330  # Avoid zero division exception
331  seed_tan_lambda = np.divide(1.0, math.tan(seed_momentum.Theta()))
332  seed_phi = seed_momentum.Phi()
333  seed_pt = seed_momentum.Rho()
334 
335  if prTrackFitResult:
336  omega_estimate = prTrackFitResult.getOmega()
337  omega_variance = prTrackFitResult.getCov()[9]
338 
339  tan_lambda_estimate = prTrackFitResult.getCotTheta()
340  tan_lambda_variance = prTrackFitResult.getCov()[14]
341 
342  d0_estimate = prTrackFitResult.getD0()
343  d0_variance = prTrackFitResult.getCov()[0]
344 
345  z0_estimate = prTrackFitResult.getZ0()
346 
347  momentum = prTrackFitResult.getMomentum()
348  pt_estimate = momentum.Rho()
349 
350  # store properties of the seed
351  self.pr_seed_ptpr_seed_pt.append(seed_pt)
352  self.pr_seed_tan_lambdaspr_seed_tan_lambdas.append(seed_tan_lambda)
353  self.pr_seed_phipr_seed_phi.append(seed_phi)
354 
355  self.pr_bining_ptpr_bining_pt.append(pt_truth)
356 
357  # store properties resulting from this trackfit
358  isMatchedOrIsClone = is_matched or is_clone
359  self.pr_clones_and_matchespr_clones_and_matches.append(isMatchedOrIsClone)
360  self.pr_matchespr_matches.append(is_matched)
361 
362  self.pr_fakespr_fakes.append(not isMatchedOrIsClone)
363 
364  self.pr_omega_estimatespr_omega_estimates.append(omega_estimate)
365  self.pr_omega_variancespr_omega_variances.append(omega_variance)
366  self.pr_omega_truthspr_omega_truths.append(omega_truth)
367 
368  self.pr_tan_lambda_estimatespr_tan_lambda_estimates.append(tan_lambda_estimate)
369  self.pr_tan_lambda_variancespr_tan_lambda_variances.append(tan_lambda_variance)
370  self.pr_tan_lambda_truthspr_tan_lambda_truths.append(tan_lambda_truth)
371 
372  self.pr_d0_estimatespr_d0_estimates.append(d0_estimate)
373  self.pr_d0_variancespr_d0_variances.append(d0_variance)
374  self.pr_d0_truthspr_d0_truths.append(d0_truth)
375 
376  self.pr_z0_estimatespr_z0_estimates.append(z0_estimate)
377  self.pr_z0_truthspr_z0_truths.append(z0_truth)
378 
379  self.pr_pt_estimatespr_pt_estimates.append(pt_estimate)
380  self.pr_pt_truthspr_pt_truths.append(pt_truth)
381 
382  def examine_mc_tracks(self):
383  """Looks at the individual Monte Carlo tracks and store information about them"""
384 
385  trackMatchLookUp = self.trackMatchLookUptrackMatchLookUp
386 
387  # Analyse from the Monte Carlo reference side
388  mcTrackCands = Belle2.PyStoreArray(self.mcTrackCandidatesColumnNamemcTrackCandidatesColumnName)
389  mcParticles = Belle2.PyStoreArray('MCParticles')
390  if not mcTrackCands:
391  return
392 
393  multiplicity = mcTrackCands.getEntries()
394  multiplicity_primaries = multiplicity
395 
396  # measure the charge asymmetry
397  n_matched_plus = 0
398  n_matched_minus = 0
399 
400  for mcTrackCand in mcTrackCands:
401  is_matched = trackMatchLookUp.isAnyChargeMatchedMCRecoTrack(mcTrackCand)
402 
403  relatedPRtrackCand = trackMatchLookUp.getRelatedPRRecoTrack(mcTrackCand)
404  if relatedPRtrackCand:
405  is_chargeMatched = trackMatchLookUp.isChargeMatched(relatedPRtrackCand)
406  else:
407  is_chargeMatched = False
408 
409  if is_chargeMatched:
410  if mcTrackCand.getChargeSeed() > 0:
411  n_matched_plus += 1
412  else:
413  n_matched_minus += 1
414 
415  hit_efficiency = trackMatchLookUp.getRelatedEfficiency(mcTrackCand)
416  if math.isnan(hit_efficiency):
417  hit_efficiency = 0
418 
419  mcParticle = trackMatchLookUp.getRelatedMCParticle(mcTrackCand)
420  mcHelix = getHelixFromMCParticle(mcParticle)
421 
422  # fill the FilterProperties will all properties on this track
423  # gathered so far
424  filterProperties = FilterProperties(mcParticle=mcParticle,
425  mcParticles=mcParticles)
426 
427  if not self.track_filter_objecttrack_filter_object.doesMcPass(filterProperties):
428  continue
429 
430  momentum = mcParticle.getMomentum()
431  pt = momentum.Rho()
432  tan_lambda = np.divide(1.0, math.tan(momentum.Theta())) # Avoid zero division exception
433  d0 = mcHelix.getD0()
434  det_hit_ids = get_det_hit_ids(mcTrackCand)
435  ndf = calc_ndf_from_det_hit_ids(det_hit_ids)
436 
437  self.mc_matchesmc_matches.append(is_matched)
438  self.mc_charge_matchesmc_charge_matches.append(is_chargeMatched and is_matched)
439  self.mc_primariesmc_primaries.append(is_primary(mcParticle))
440  self.mc_hit_efficienciesmc_hit_efficiencies.append(hit_efficiency)
441  self.mc_ptsmc_pts.append(pt)
442  self.mc_d0smc_d0s.append(d0)
443  self.mc_tan_lambdasmc_tan_lambdas.append(tan_lambda)
444  self.mc_multiplicitiesmc_multiplicities.append(multiplicity)
445  self.mc_phimc_phi.append(momentum.Phi())
446  self.mc_ndfmc_ndf.append(ndf)
447  if not is_primary(mcParticle):
448  multiplicity_primaries -= 1
449 
450  charge_asymmetry = (n_matched_plus - n_matched_minus)/(n_matched_plus +
451  n_matched_minus) if (n_matched_plus + n_matched_minus) != 0 else 0
452  for mcTrackCand in mcTrackCands:
453  if is_primary(mcParticle):
454  self.mc_charge_asymmetrymc_charge_asymmetry.append(charge_asymmetry)
455  self.mc_charge_asymmetry_weightsmc_charge_asymmetry_weights.append(1./multiplicity_primaries)
456  else:
457  self.mc_charge_asymmetrymc_charge_asymmetry.append(0)
458  self.mc_charge_asymmetry_weightsmc_charge_asymmetry_weights.append(0)
459 
460  def terminate(self):
461  """Receive signal at the end of event processing"""
462  name = self.validation_namevalidation_name
463  contact = self.contactcontact
464 
465  # Overall figures of merit #
466 
467 
468  mc_matched_primaries = np.logical_and(self.mc_primariesmc_primaries, self.mc_matchesmc_matches)
469 
470  charge_asymmetry = np.average(self.mc_charge_asymmetrymc_charge_asymmetry, weights=self.mc_charge_asymmetry_weightsmc_charge_asymmetry_weights)
471  if len(mc_matched_primaries) > 0 and sum(mc_matched_primaries) > 0:
472  charge_efficiency = np.average(self.mc_charge_matchesmc_charge_matches, weights=mc_matched_primaries)
473  hit_efficiency = np.average(self.mc_hit_efficienciesmc_hit_efficiencies, weights=mc_matched_primaries)
474  else:
475  charge_efficiency = float('nan')
476  hit_efficiency = float('nan')
477  finding_charge_efficiency = np.average(self.mc_charge_matchesmc_charge_matches, weights=self.mc_primariesmc_primaries)
478  finding_efficiency = np.average(self.mc_matchesmc_matches, weights=self.mc_primariesmc_primaries)
479  fake_rate = 1.0 - np.mean(self.pr_clones_and_matchespr_clones_and_matches)
480  # can only be computed if there are entries
481  if len(self.pr_clones_and_matchespr_clones_and_matches) > 0 and sum(self.pr_clones_and_matchespr_clones_and_matches) > 0:
482  clone_rate = 1.0 - np.average(self.pr_matchespr_matches,
483  weights=self.pr_clones_and_matchespr_clones_and_matches)
484  else:
485  clone_rate = float('nan')
486 
487  figures_of_merit = ValidationFiguresOfMerit('%s_figures_of_merit'
488  % name)
489  figures_of_merit['finding_charge_efficiency'] = finding_charge_efficiency
490  figures_of_merit['finding_efficiency'] = finding_efficiency
491  figures_of_merit['charge_efficiency'] = charge_efficiency
492  figures_of_merit['charge_asymmetry'] = charge_asymmetry
493  figures_of_merit['fake_rate'] = fake_rate
494  figures_of_merit['clone_rate'] = clone_rate
495  figures_of_merit['hit_efficiency'] = hit_efficiency
496 
497  figures_of_merit.description = \
498  """
499 finding_efficiency - the ratio of matched Monte Carlo tracks to all primary Monte Carlo tracks <br/>
500 charge_efficiency - the ratio of matched Monte Carlo tracks with correct charge to matched primary Monte Carlo tracks <br/>
501 finding_charge_efficiency - the ratio of matched Monte Carlo tracks with correct charge to all primary Monte Carlo tracks <br/>
502 fake_rate - ratio of pattern recognition tracks that are not related to a particle
503  (background, ghost) to all pattern recognition tracks <br/>
504 clone_rate - ratio of clones divided the number of tracks that are related to a particle (clones and matches) <br/>
505 
506 """
507  figures_of_merit.check = 'Compare for degradations with respect to the reference'
508  figures_of_merit.contact = contact
509  print(figures_of_merit)
510 
511  # Validation plots #
512 
513  validation_plots = []
514  pull_analyses = []
515 
516  # Finding efficiency #
517 
518  plots = self.profiles_by_mc_parametersprofiles_by_mc_parameters(self.mc_matchesmc_matches,
519  'finding efficiency',
520  make_hist=False,
521  weights=self.mc_primariesmc_primaries)
522 
523  validation_plots.extend(plots)
524 
525  # Fake rate (all tracks not matched or clone #
526  # use TrackCand seeds for the fake track plotting #
527  # as the fit (if successful) is probably not meaningful #
528 
529  print('fake list: ' + str(self.pr_fakespr_fakes.count(1)))
530  plots = self.profiles_by_pr_parametersprofiles_by_pr_parameters(self.pr_fakespr_fakes, 'fake rate',
531  make_hist=False)
532 
533  validation_plots.extend(plots)
534 
535  # Charge efficiency of matched primary tracks #
536 
537  plots = self.profiles_by_mc_parametersprofiles_by_mc_parameters(self.mc_charge_matchesmc_charge_matches,
538  'charge efficiency for matched primary tracks',
539  weights=mc_matched_primaries)
540 
541  validation_plots.extend(plots)
542 
543  # Finding & Charge efficiency of primary tracks #
544 
545  plots = self.profiles_by_mc_parametersprofiles_by_mc_parameters(self.mc_charge_matchesmc_charge_matches,
546  'finding and charge efficiency for primary tracks',
547  weights=self.mc_primariesmc_primaries)
548 
549  validation_plots.extend(plots)
550 
551  # Charge asymmetry of primary tracks #
552 
553  plots = self.profiles_by_mc_parametersprofiles_by_mc_parameters(self.mc_charge_asymmetrymc_charge_asymmetry,
554  'charge asymmetry for primary tracks',
555  weights=self.mc_charge_asymmetry_weightsmc_charge_asymmetry_weights,
556  is_asymmetry=True)
557 
558  validation_plots.extend(plots)
559 
560  # Hit efficiency #
561 
562  plots = self.profiles_by_mc_parametersprofiles_by_mc_parameters(self.mc_hit_efficienciesmc_hit_efficiencies,
563  'hit efficiency with matched tracks',
564  weights=mc_matched_primaries)
565 
566  validation_plots.extend(plots)
567 
568  # Fit quality #
569 
570  if self.pullspulls:
571  all_but_diagonal_plots = list(PullAnalysis.default_which_plots)
572  all_but_diagonal_plots.remove("diag_profile")
573  all_but_diagonal_plots.remove("diag_scatter")
574 
575  plot_name_prefix = name + self.plot_name_postfixplot_name_postfix
576  if not self.fitfit:
577  plot_name_prefix += '_seed'
578 
579  # Omega / curvature pull
580  pr_omega_truths = np.array(self.pr_omega_truthspr_omega_truths)
581  pr_omega_estimates = np.array(self.pr_omega_estimatespr_omega_estimates)
582  pr_omega_variances = np.array(self.pr_omega_variancespr_omega_variances)
583 
584  curvature_pull_analysis = PullAnalysis('#omega', unit='1/cm',
585  plot_name_prefix=plot_name_prefix + '_omega',
586  plot_title_postfix=self.plot_title_postfixplot_title_postfix,
587  referenceFileName=self.referenceFileNamereferenceFileName)
588 
589  curvature_pull_analysis.analyse(pr_omega_truths,
590  pr_omega_estimates,
591  pr_omega_variances,
592  which_plots=all_but_diagonal_plots)
593 
594  curvature_pull_analysis.contact = contact
595  pull_analyses.append(curvature_pull_analysis)
596 
597  # Tan lambda pull
598  pr_tan_lambda_truths = np.array(self.pr_tan_lambda_truthspr_tan_lambda_truths)
599  pr_tan_lambda_estimates = np.array(self.pr_tan_lambda_estimatespr_tan_lambda_estimates)
600  pr_tan_lambda_variances = np.array(self.pr_tan_lambda_variancespr_tan_lambda_variances)
601 
602  curvature_pull_analysis = PullAnalysis('tan #lambda',
603  plot_name_prefix=plot_name_prefix + '_tan_lambda',
604  plot_title_postfix=self.plot_title_postfixplot_title_postfix,
605  referenceFileName=self.referenceFileNamereferenceFileName)
606 
607  curvature_pull_analysis.analyse(pr_tan_lambda_truths,
608  pr_tan_lambda_estimates,
609  pr_tan_lambda_variances,
610  which_plots=all_but_diagonal_plots)
611 
612  curvature_pull_analysis.contact = contact
613  pull_analyses.append(curvature_pull_analysis)
614 
615  # d0 pull
616  curvature_pull_analysis = PullAnalysis('d0',
617  plot_name_prefix=plot_name_prefix + '_d0',
618  plot_title_postfix=self.plot_title_postfixplot_title_postfix,
619  referenceFileName=self.referenceFileNamereferenceFileName)
620 
621  curvature_pull_analysis.analyse(np.array(self.pr_d0_truthspr_d0_truths),
622  np.array(self.pr_d0_estimatespr_d0_estimates),
623  np.array(self.pr_d0_variancespr_d0_variances),
624  which_plots=all_but_diagonal_plots)
625 
626  curvature_pull_analysis.contact = contact
627  pull_analyses.append(curvature_pull_analysis)
628 
629  # Resolution plots
630 
631  if self.resolutionresolution:
632  # d0 impact parameter resolution plot
633  d0_resolution_analysis = ResolutionAnalysis('d0_res',
634  self.resolution_pt_binningresolution_pt_binning,
635  'Pt',
636  plot_name_prefix=plot_name_prefix + '_d0_res',
637  plot_title_postfix=self.plot_title_postfixplot_title_postfix,
638  referenceFileName=self.referenceFileNamereferenceFileName)
639  d0_resolution_analysis.analyse(np.array(self.pr_bining_ptpr_bining_pt),
640  np.array(self.pr_d0_truthspr_d0_truths),
641  np.array(self.pr_d0_estimatespr_d0_estimates))
642  d0_resolution_analysis.contact = contact
643  pull_analyses.append(d0_resolution_analysis)
644 
645  # z0 impact parameter resolution plot
646  z0_resolution_analysis = ResolutionAnalysis('z0_res',
647  self.resolution_pt_binningresolution_pt_binning,
648  "Pt",
649  plot_name_prefix=plot_name_prefix + '_z0_res',
650  plot_title_postfix=self.plot_title_postfixplot_title_postfix,
651  referenceFileName=self.referenceFileNamereferenceFileName)
652  z0_resolution_analysis.analyse(np.array(self.pr_bining_ptpr_bining_pt),
653  np.array(self.pr_z0_truthspr_z0_truths),
654  np.array(self.pr_z0_estimatespr_z0_estimates))
655  z0_resolution_analysis.contact = contact
656  pull_analyses.append(z0_resolution_analysis)
657 
658  # omega curvature parameter resolution plot
659  omega_resolution_analysis = ResolutionAnalysis('omega_res',
660  self.resolution_pt_binningresolution_pt_binning,
661  "Pt",
662  plot_name_prefix=plot_name_prefix + '_omega_res',
663  plot_title_postfix=self.plot_title_postfixplot_title_postfix,
664  referenceFileName=self.referenceFileNamereferenceFileName)
665  omega_resolution_analysis.analyse(np.array(self.pr_bining_ptpr_bining_pt),
666  np.array(self.pr_omega_truthspr_omega_truths),
667  np.array(self.pr_omega_estimatespr_omega_estimates))
668  omega_resolution_analysis.contact = contact
669  pull_analyses.append(omega_resolution_analysis)
670 
671  # transverse momentum resolution plot
672  pt_resolution_analysis = ResolutionAnalysis('pt_res',
673  self.resolution_pt_binningresolution_pt_binning,
674  "Pt",
675  plot_name_prefix=plot_name_prefix + '_pt_res',
676  plot_title_postfix=self.plot_title_postfixplot_title_postfix,
677  referenceFileName=self.referenceFileNamereferenceFileName)
678  pt_resolution_analysis.analyse(np.array(self.pr_bining_ptpr_bining_pt),
679  np.array(self.pr_pt_truthspr_pt_truths),
680  np.array(self.pr_pt_estimatespr_pt_estimates))
681  pt_resolution_analysis.contact = contact
682  pull_analyses.append(pt_resolution_analysis)
683 
684  # Saving #
685 
686 
687  # Save everything to a ROOT file
688  output_tfile = ROOT.TFile(self.output_file_nameoutput_file_name, 'recreate')
689 
690  # Show all parameters and the fit result in the plots
691  # if viewed in the browser or the validation
692  opt_fit = 0o112
693  ROOT.gStyle.SetOptFit(opt_fit)
694 
695  figures_of_merit.write()
696 
697  for validation_plot in validation_plots:
698  validation_plot.write()
699 
700  if self.use_expert_folderuse_expert_folder:
701  expert_tdirectory = output_tfile.mkdir('expert', 'Expert')
702  expert_tdirectory.cd()
703  ROOT.gStyle.SetOptFit(opt_fit)
704 
705  for pull_analysis in pull_analyses:
706  pull_analysis.write()
707 
708  output_tfile.Close()
709 
711  self,
712  xs,
713  quantity_name,
714  unit=None,
715  parameter_names=[
716  'd_0',
717  'p_t',
718  'tan_lambda',
719  'multiplicity',
720  'phi',
721  'ndf',
722  ],
723  make_hist=True,
724  weights=None,
725  is_asymmetry=False,
726  ):
727  """Create profile histograms by MC-track parameters"""
728 
729  # apply exclusion list
730  new_parameter_names = [item for item in parameter_names if item
731  not in self.exclude_profile_mc_parameterexclude_profile_mc_parameter]
732 
733  # Profile versus the various parameters
734  profile_parameters = {
735  'd_{0}': self.mc_d0smc_d0s,
736  'p_{t}': self.mc_ptsmc_pts,
737  'tan #lambda': self.mc_tan_lambdasmc_tan_lambdas,
738  '#phi': self.mc_phimc_phi,
739  'multiplicity': self.mc_multiplicitiesmc_multiplicities,
740  'ndf': self.mc_ndfmc_ndf,
741  }
742 
743  return self.profiles_by_parameters_baseprofiles_by_parameters_base(
744  xs,
745  quantity_name,
746  new_parameter_names,
747  profile_parameters,
748  unit,
749  make_hist,
750  weights=weights,
751  is_asymmetry=is_asymmetry
752  )
753 
755  self,
756  xs,
757  quantity_name,
758  unit=None,
759  parameter_names=['Seed_p_t', 'Seed tan #lambda', 'Seed #phi'],
760  make_hist=True,
761  ):
762  """Create profile histograms by PR-track parameters"""
763 
764  # apply exclusion list
765  new_parameter_names = [item for item in parameter_names if item
766  not in self.exclude_profile_pr_parameterexclude_profile_pr_parameter]
767 
768  # Profile versus the various parameters
769  profile_parameters = {'Seed p_{t}': self.pr_seed_ptpr_seed_pt,
770  'Seed tan #lambda': self.pr_seed_tan_lambdaspr_seed_tan_lambdas,
771  'Seed #phi': self.pr_seed_phipr_seed_phi}
772 
773  return self.profiles_by_parameters_baseprofiles_by_parameters_base(
774  xs,
775  quantity_name,
776  new_parameter_names,
777  profile_parameters,
778  unit,
779  make_hist,
780  )
781 
783  self,
784  xs,
785  quantity_name,
786  parameter_names,
787  profile_parameters,
788  unit,
789  make_hist,
790  weights=None,
791  is_asymmetry=False,
792  ):
793  """Create profile histograms for generic parameters"""
794 
795  contact = self.contactcontact
796 
797  validation_plots = []
798  plot_name_prefix = self.validation_namevalidation_name + '_' + root_save_name(quantity_name) \
799  + self.plot_name_postfixplot_name_postfix
800 
801  if make_hist:
802  # Histogram of the quantity
803  histogram = ValidationPlot(plot_name_prefix, self.referenceFileNamereferenceFileName)
804  histogram.hist(xs, weights=weights)
805 
806  histogram.xlabel = quantity_name
807  histogram.description = 'Not a serious plot yet.'
808  histogram.check = ''
809  histogram.contact = contact
810  histogram.title = quantity_name + self.plot_title_postfixplot_title_postfix
811 
812  validation_plots.append(histogram)
813 
814  for (parameter_name, parameter_values) in list(profile_parameters.items()):
815  if parameter_name in parameter_names \
816  or root_save_name(parameter_name) in parameter_names:
817 
818  is_expert = not(parameter_name in self.non_expert_parametersnon_expert_parameters)
819 
820  parameter_root_name = root_save_name(parameter_name)
821 
822  # Apply some boundaries for the maximal tracking acceptance
823  # such that the plots look more instructive
824  if 'tan_lambda' in parameter_root_name:
825  lower_bound = -2.0
826  upper_bound = 5.0
827  # need different bounds for cosmics
828  if 'cosmics' in self.validation_namevalidation_name.lower() or \
829  'cosmics' in self.output_file_nameoutput_file_name.lower():
830  lower_bound = -1.5
831  upper_bound = 1.5
832  elif 'ndf' in parameter_root_name:
833  lower_bound = 0
834  upper_bound = min(200, np.max(parameter_values))
835  elif 'p_t' in parameter_root_name:
836  lower_bound = 0
837  upper_bound = 2.5
838  # need different upper_bound for cosmics
839  if 'cosmics' in self.validation_namevalidation_name.lower() or \
840  'cosmics' in self.output_file_nameoutput_file_name.lower():
841  upper_bound = 30
842  elif 'd_0' in parameter_root_name:
843  lower_bound = -0.06
844  upper_bound = 0.06
845  # need different bounds for cosmics
846  if 'cosmics' in self.validation_namevalidation_name.lower() or \
847  'cosmics' in self.output_file_nameoutput_file_name.lower():
848  lower_bound = -20
849  upper_bound = 20
850  else:
851  lower_bound = None
852  upper_bound = None
853 
854  profile_plot_name = plot_name_prefix + '_by_' \
855  + root_save_name(parameter_name)
856  profile_plot = ValidationPlot(profile_plot_name, self.referenceFileNamereferenceFileName)
857  profile_plot.profile(parameter_values,
858  xs,
859  weights=weights,
860  outlier_z_score=10.0,
861  lower_bound=lower_bound,
862  upper_bound=upper_bound,
863  y_binary=True,
864  is_expert=is_expert,
865  is_asymmetry=is_asymmetry)
866 
867  profile_plot.xlabel = compose_axis_label(parameter_name)
868  profile_plot.ylabel = compose_axis_label(quantity_name, unit)
869  profile_plot.title = quantity_name + ' by ' + parameter_name \
870  + ' profile' + self.plot_title_postfixplot_title_postfix
871 
872  profile_plot.description = \
873  'Dependence of %s of the track over the true %s' \
874  % (quantity_name, parameter_name)
875  profile_plot.check = 'Variations should be low'
876  profile_plot.contact = contact
877  validation_plots.append(profile_plot)
878 
879  return validation_plots
static std::string findFile(const std::string &path, bool silent=false)
Search for given file or directory in local or central release directory, and return absolute path if...
Definition: FileSystem.cc:148
A (simplified) python wrapper for StoreArray.
Definition: PyStoreArray.h:72
Class to provide convenient methods to look up matching information between pattern recognition and M...
def doesPrPass(self, filterProperties)
Definition: module.py:94
def doesMcPass(self, filterProperties)
Definition: module.py:98
mcParticle
cached value of the MC particle
Definition: module.py:62
wasFitted
cached value of the fitted flag
Definition: module.py:66
def __init__(self, trackCand=None, mcParticle=None, mcParticles=None, wasFitted=False, fitResult=None, seedResult=None)
Definition: module.py:56
trackCand
cached value of the track candidate
Definition: module.py:60
seedResult
cached value of the seed result
Definition: module.py:70
fitResult
cached value of the fit result
Definition: module.py:68
mcParticles
cached value of the MCParticles StoreArray
Definition: module.py:64
pr_omega_variances
list of PR-track seed omega-variance values
Definition: module.py:205
exclude_profile_mc_parameter
cached list of perigee parameters excluded from MC side plots
Definition: module.py:152
track_filter_object
cached value of the track-filter object
Definition: module.py:144
pr_seed_tan_lambdas
list of PR-track seed tan(lambda) values
Definition: module.py:196
pr_tan_lambda_estimates
list of PR-track seed tan(lambda)-estimate values
Definition: module.py:210
contact
cached value of the contact person name
Definition: module.py:133
pr_bining_pt
list of PR-track binning values
Definition: module.py:232
pr_tan_lambda_variances
list of PR-track seed tan(lambda)-variance values
Definition: module.py:212
pr_pt_estimates
list of PR-track seed pt-estimate values
Definition: module.py:229
trackCandidatesColumnName
cached name of the RecoTracks StoreArray
Definition: module.py:156
pr_pt_truths
list of PR-track seed pt-truth values
Definition: module.py:227
pulls
cached values of the track-fit pulls
Definition: module.py:137
mc_ndf
list of MC-track number of degrees of freedom
Definition: module.py:257
pr_omega_truths
list of PR-track seed omega-truth values
Definition: module.py:201
pr_seed_phi
list of PR-track seed phi values
Definition: module.py:198
output_file_name
cached value of the output ROOT TFile
Definition: module.py:141
plot_name_postfix
cached value of the suffix appended to the plot names
Definition: module.py:146
trackMatchLookUp
Track-match object that examines relation information from MCMatcherTracksModule.
Definition: module.py:182
pr_omega_estimates
list of PR-track seed omega-estimate values
Definition: module.py:203
plot_title_postfix
cached value of the suffix appended to the plot titles
Definition: module.py:148
pr_z0_estimates
list of PR-track seed z0-estimate values
Definition: module.py:224
pr_d0_variances
list of PR-track seed d0-variance values
Definition: module.py:219
def profiles_by_pr_parameters(self, xs, quantity_name, unit=None, parameter_names=['Seed_p_t', 'Seed tan #lambda', 'Seed #phi'], make_hist=True)
Definition: module.py:761
resolution_pt_binning
default binning used for resolution plots over pt
Definition: module.py:163
mc_multiplicities
list of MC-track multiplicities
Definition: module.py:255
pr_clones_and_matches
Use deques in favour of lists to prevent repeated memory allocation of cost O(n)
Definition: module.py:187
non_expert_parameters
list of parameters that determines which plots (all with corresponding x-axis) are marked as shifter ...
Definition: module.py:160
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", mcTrackCandidatesColumName="MCRecoTracks", non_expert_parameters=['p_{t}'])
Definition: module.py:125
resolution
cached value of the resolution
Definition: module.py:139
referenceFileName
If this variable is set the code will open the file with same name as the file created here and will ...
Definition: module.py:169
use_expert_folder
cached flag to use the "expert" folder for the pull and residual plots
Definition: module.py:154
def profiles_by_mc_parameters(self, xs, quantity_name, unit=None, parameter_names=['d_0', 'p_t', 'tan_lambda', 'multiplicity', 'phi', 'ndf',], make_hist=True, weights=None, is_asymmetry=False)
Definition: module.py:726
pr_seed_pt
list of PR-track seed pt values
Definition: module.py:194
pr_z0_truths
list of PR-track seed z0-truth values
Definition: module.py:222
mcTrackCandidatesColumnName
cached name of the MCRecoTracks StoreArray
Definition: module.py:158
pr_tan_lambda_truths
list of PR-track seed tan(lambda)-truth values
Definition: module.py:208
pr_d0_estimates
list of PR-track seed d0-estimate values
Definition: module.py:217
validation_name
cached value of the tracking-validation name
Definition: module.py:131
mc_charge_asymmetry_weights
list of MC-track matches charge asymmetry weights
Definition: module.py:241
mc_tan_lambdas
list of MC-track tan(lambda) values
Definition: module.py:247
mc_hit_efficiencies
list of MC-track hit efficiencies
Definition: module.py:253
pr_d0_truths
list of PR-track seed d0-truth values
Definition: module.py:215
mc_charge_matches
list of MC-track matches, including matched charge
Definition: module.py:237
mc_charge_asymmetry
list of MC-track matches charge asymmetry
Definition: module.py:239
exclude_profile_pr_parameter
cached list of perigee parameters excluded from PR side plots
Definition: module.py:150
def profiles_by_parameters_base(self, xs, quantity_name, parameter_names, profile_parameters, unit, make_hist, weights=None, is_asymmetry=False)
Definition: module.py:792