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