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