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