Belle II Software  release-06-01-15
calibration_checker.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 """\
13 This module contains classes for plotting calibration constants.
14 """
15 
16 from abc import ABC, abstractmethod
17 from ROOT import Belle2
18 from ROOT.Belle2 import PXDMaskedPixelPar, PXDDeadPixelPar, PXDOccupancyInfoPar
19 import ROOT
20 from copy import deepcopy
21 
22 from pxd.utils import get_sensor_graphs, sensorID_list
23 # from pxd.utils import nPixels, nVCells, nUCells
24 from pxd.calibration.condition_checker import plot_type_dict
25 
26 # import basf2
27 
28 # lookup dictrionary for finding a checker based on objType
29 __cal_checker_dict__ = {
30  "PXDHotPixelMaskCalibrationChecker": set([PXDMaskedPixelPar, PXDDeadPixelPar, PXDOccupancyInfoPar]),
31 }
32 
33 
34 # Calibration checkers for summarizing info of multiple payloads
36  """
37  Abstract base class for generating plots from calibrations which save multiple payloads.
38  """
39 
40  def __init__(self, objType_list=[]):
41  """
42  Parameters:
43  objType_list (list): a list of db objects used as keys to their checker.
44  e.g., [Belle2.PXDMaskedPixelPar, Belle2.PXDDeadPixelPar]
45  """
46 
47  self.condition_checkerscondition_checkers = {}
48  for objType in objType_list:
49  self.condition_checkerscondition_checkers[objType] = None
50 
51  def initialize(self, checker_list=[]):
52  """
53  Initialize condition_checker according to condition objType
54  Parameters:
55  checker_list (list): list of objType checkers
56  """
57  checker = None
58  for checker in checker_list:
59  for objType in self.condition_checkerscondition_checkers.keys():
60  if objType == checker.objType:
61  self.condition_checkerscondition_checkers[objType] = checker
62  if self.validvalid:
63  checker.tfile.cd()
64  print(f"root file path: {checker.tfile}")
65  self.define_graphsdefine_graphs()
66 
67  @property
68  def valid(self):
69  """
70  valid flag
71  """
72  if None in self.condition_checkerscondition_checkers.values():
73  # do nothing if any of the condition checkers is missing
74  return False
75  else:
76  return True
77 
78  @abstractmethod
79  def define_graphs(self):
80  """
81  Define summary graphs
82  """
83 
84  @abstractmethod
85  def fill_graphs(self):
86  """
87  Method to fill TGraphs
88  """
89 
90  @abstractmethod
91  def draw_plots(self):
92  """
93  Generate summary plots from TGraphs of all condition checkers
94  """
95 
96  def beginRun(self):
97  """
98  function to be executed at the beginning of a run
99  """
100  if self.valid:
101  self.fill_graphs()
102 
103  def terminate(self):
104  """
105  Execute when terminating a basf2 module.
106  All required TGraphs should be ready at this stage.
107  """
108  if self.validvalid:
109  self.draw_plotsdraw_plots()
110 
111 
113  """
114  Checker for PXDHotPixelMaskCalibration
115  """
116 
117  def __init__(self):
118  """
119  Initial with related condition db object types
120  """
121  super().__init__(objType_list=[
125  ])
126 
127  def define_graphs(self):
128  """
129  Define TGraphs for filling
130  """
131 
132  self.hotdead_graphshotdead_graphs = get_sensor_graphs("Hot/Dead pixels [%]")
133 
134  self.occupancy_masked_graphsoccupancy_masked_graphs = get_sensor_graphs("Occupancy (With Mask) [%]")
135 
136  self.occupancy_no_mask_graphsoccupancy_no_mask_graphs = get_sensor_graphs("Occupancy (No Mask) [%]")
137 
138  self.sum_graphssum_graphs = {}
139  # TLegend
140  legsum = ROOT.TLegend(0.75, 0.65, 0.90, 0.85)
141  legsum.SetFillStyle(0)
142  legsum.SetBorderSize(0)
143  legsum.SetTextFont(43)
144  legsum.SetTextSize(18)
145  legocc = deepcopy(legsum)
146  self.sum_graphssum_graphs["TLegends"] = {"sum": legsum, "occ": legocc}
147 
148  for category, style_dict in plot_type_dict.items():
149  agraph = ROOT.TGraph()
150  agraph.SetLineColor(style_dict["color"])
151  agraph.SetMarkerColor(style_dict["color"])
152  agraph.SetMarkerStyle(20)
153  if category == "hot_dead":
154  agraph.GetYaxis().SetTitle('Total Hot/Dead pixels [%]')
155  agraph.SetMaximum(style_dict["max"])
156  elif category == "occ_no_mask":
157  agraph.GetYaxis().SetTitle('Average occupancy [%]')
158  agraph.SetMaximum(style_dict["max"])
159  agraph.GetXaxis().SetTitle('run #')
160  agraph.GetYaxis().SetTitleOffset(0.9)
161  agraph.SetMinimum(0.0)
162  self.sum_graphssum_graphs[category] = agraph
163 
164  def fill_graphs(self):
165  """
166  Fill TGraphs, will be executed for each run
167  """
168  dead_checker = self.condition_checkerscondition_checkers[PXDDeadPixelPar]
169  masked_graphs = self.condition_checkerscondition_checkers[PXDMaskedPixelPar].graphs
170  dead_graphs = self.condition_checkerscondition_checkers[PXDDeadPixelPar].graphs
171  occ_graphs = self.condition_checkerscondition_checkers[PXDOccupancyInfoPar].graphs
172 
173  current_run = dead_checker.run
174  saved_run = -1 # to be updated
175  saved_run_occ = -1 # to be updated
176  sum_hotfraction = 0.
177  sum_deadfraction = 0.
178  sum_hotdeadfraction = 0.
179  sum_occupancymasked = 0.
180  sum_occupancynomask = 0.
181  for sensorID in sensorID_list:
182  numID = sensorID.getID()
183  # This function is executed just after filling the condition checker graphs, so we don't need a python loop!
184  g1 = masked_graphs[numID]
185  g2 = dead_graphs[numID]
186  g3 = occ_graphs[numID]
187  # If two runs have the same constants, only results from the 1st run will be saved.
188  hotfraction = g1.GetPointY(g1.GetN() - 1)
189  deadfraction = g2.GetPointY(g2.GetN() - 1)
190  occupancymasked = g3.GetPointY(g3.GetN() - 1)
191  occupancynomask = 0.
192  # Masked hot pixels and dead pixels could be updated for different runs.
193  saved_run = max(g1.GetPointX(g1.GetN() - 1), g2.GetPointX(g2.GetN() - 1))
194  saved_run_occ = max(saved_run, g3.GetPointX(g3.GetN() - 1))
195  if current_run == saved_run:
196  self.hotdead_graphshotdead_graphs[numID].append(current_run, hotfraction + deadfraction)
197  sum_hotfraction += hotfraction
198  sum_deadfraction += deadfraction
199  sum_hotdeadfraction += hotfraction + deadfraction
200  if current_run == saved_run_occ:
201  # occupancy correction (it's overestiamting occ_masked?)
202  # By defaut, both occ and fractions are in percentage.
203  if deadfraction != 100.:
204  occupancynomask = (occupancymasked + hotfraction) / (1. - deadfraction / 100)
205  occupancymasked = occupancymasked / (1. - deadfraction / 100)
206  self.occupancy_no_mask_graphsoccupancy_no_mask_graphs[numID].append(current_run, occupancynomask)
207  self.occupancy_masked_graphsoccupancy_masked_graphs[numID].append(current_run, occupancymasked)
208  sum_occupancymasked += occupancymasked
209  sum_occupancynomask += occupancynomask
210  # print(f"hotfraction:{hotfraction}, deadfraction: {deadfraction}")
211  # print(f"occ:{occupancymasked}, occ_no_mask:{occupancynomask}")
212  # rawOcc = self.condition_checkers[PXDOccupancyInfoPar].dbObj.getOccupancy(numID)
213  # print(f"Raw occ: {rawOcc}")
214  # print(f"sum_hotfraction:{sum_hotfraction}, sum_deadfraction: {sum_deadfraction}")
215  # print(f"sum_occ:{sum_occupancymasked}, sum_occ_no_mask:{sum_occupancynomask}")
216 
217  nSensors = len(sensorID_list)
218  if current_run == saved_run:
219  self.sum_graphssum_graphs["hot"].append(current_run, sum_hotfraction / nSensors)
220  self.sum_graphssum_graphs["dead"].append(current_run, sum_deadfraction / nSensors)
221  self.sum_graphssum_graphs["hot_dead"].append(current_run, sum_hotdeadfraction / nSensors)
222 
223  nSensors = nSensors - len(dead_checker.dbObj.getDeadSensorMap())
224  if current_run == saved_run_occ and nSensors > 0:
225  self.sum_graphssum_graphs["occ_masked"].append(current_run, sum_occupancymasked / nSensors)
226  self.sum_graphssum_graphs["occ_no_mask"].append(current_run, sum_occupancynomask / nSensors)
227 
228  def draw_plots(self):
229  """
230  Generate summary plots from TGraphs of all condition checkers
231  """
232 
233  # Reuse masker checker and replace its graphs
234  # checker = deepcopy(self.condition_checkers[PXDMaskedPixelPar])
235  # checker.tfile = self.condition_checkers[PXDMaskedPixelPar].tfile
236  checker = self.condition_checkerscondition_checkers[PXDMaskedPixelPar]
237 
238  # Create a TCanvas under the file folder
239  checker.tfile.cd()
240  canvas = ROOT.TCanvas('c_pxd', 'c_pxd', 1000, 400)
241  # canvas.SetLeftMargin(0.09)
242  # canvas.SetRightMargin(0.05)
243 
244  # Hot/dead pixel masking
245  checker.graphs = self.hotdead_graphshotdead_graphs
246  checker.draw_plots(
247  canvas=canvas, cname="PXDHotnDeadPixel", ymin=0., ymax=plot_type_dict["hot_dead"]["max"])
248  checker.draw_plots(canvas=canvas, cname="PXDHotnDeadPixel", ymin=0., ymax=None, logy=True)
249  canvas.Write()
250  # Occupancy with/without Mask
251  checker.graphs = self.occupancy_masked_graphsoccupancy_masked_graphs
252  checker.draw_plots(
253  canvas=canvas, cname="PXDOccupancyWithMask", ymin=0., ymax=plot_type_dict["occ_masked"]["max"])
254  checker.draw_plots(canvas=canvas, cname="PXDOccupancyWithMask", ymin=0., ymax=None, logy=True)
255  canvas.Write()
256  checker.graphs = self.occupancy_no_mask_graphsoccupancy_no_mask_graphs
257  checker.draw_plots(
258  canvas=canvas, cname="PXDOccupancyNoMask", ymin=0., ymax=plot_type_dict["occ_no_mask"]["max"])
259  checker.draw_plots(canvas=canvas, cname="PXDOccupancyNoMask", ymin=0., ymax=None, logy=True)
260  canvas.Write()
261 
262  # checker.save_canvas(canvas, "PXDHotnDeadPixel")
263 
264  # Summary plots
265  # Total Masking plots
266  canvas.Clear()
267  g1 = self.sum_graphssum_graphs["hot_dead"]
268  g2 = self.sum_graphssum_graphs["hot"]
269  g3 = self.sum_graphssum_graphs["dead"]
270  leg = self.sum_graphssum_graphs["TLegends"]["sum"]
271  g1.Draw("AP")
272  g2.Draw("P same")
273  g3.Draw("P same")
274  leg.AddEntry(g1, 'Hot+Dead pixels', 'p')
275  leg.AddEntry(g2, 'Hot pixels', 'p')
276  leg.AddEntry(g3, 'Dead pixels', 'p')
277  leg.Draw()
278  checker.save_canvas(canvas, "PXDTotalHotnDeadPixel", logy=True)
279  canvas.Write()
280  checker.save_canvas(canvas, "PXDTotalHotnDeadPixel", logy=False)
281  # Average Occupancy plots
282  canvas.Clear()
283  g1 = self.sum_graphssum_graphs["occ_no_mask"]
284  g2 = self.sum_graphssum_graphs["occ_masked"]
285  leg = self.sum_graphssum_graphs["TLegends"]["occ"]
286  g1.Draw("AP")
287  g2.Draw("P same")
288  leg.AddEntry(g1, 'No Mask', 'p')
289  leg.AddEntry(g2, 'With Mask', 'p')
290  leg.Draw()
291  checker.save_canvas(canvas, "PXDTotalOccupancy", logy=True)
292  canvas.Write()
293  checker.save_canvas(canvas, "PXDTotalOccupancy", logy=False)
The payload telling which PXD pixel is dead (=Readout system does not receive signals)
The payload telling which PXD pixel to mask (ignore)
The payload collecting some meta information from running the masking algorithm.
condition_checkers
condition checkers which are assoicated to this calibration.
occupancy_no_mask_graphs
Dictionary to save TGraph of module occupancy (without mask)
occupancy_masked_graphs
Dictionary to save TGraph of module occupancy (with mask)
hotdead_graphs
Dictionary to save TGraphs of hot/dead pixel fractions with sensor id as key.