Belle II Software  release-08-01-10
condition_checker.py
1 #!/usr/bin/env python3
2 
3 
10 
11 """\
12 This module contains classes for plotting calibration constants.
13 """
14 
15 __all__ = ["ConditionCheckerBase", "PXDMaskedPixelsChecker", "PXDDeadPixelsChecker", "PXDOccupancyInfoChecker"]
16 
17 from abc import ABC, abstractmethod
18 from ROOT import Belle2
19 import ROOT
20 import numpy as np
21 from hist_utils import array2hist
22 from pxd.utils import get_sensor_graphs, get_sensor_maps, sensorID_list
23 from pxd.utils import latex_r, nPixels, nVCells, nUCells
24 
25 # import basf2
26 
27 # lookup dictionary for finding a checker based on objType
28 __checker_dict__ = {
29  Belle2.PXDMaskedPixelPar: "PXDMaskedPixelsChecker",
30  Belle2.PXDDeadPixelPar: "PXDDeadPixelsChecker",
31  Belle2.PXDOccupancyInfoPar: "PXDOccupancyInfoChecker",
32  Belle2.PXDGainMapPar: "PXDGainMapChecker",
33 }
34 
35 # plot categories and styles for PXDHotPixelMasking calibration
36 type_list = ['hot', 'dead', 'hot_dead', 'occ_no_mask', 'occ_masked']
37 # label_list = ['Hot', 'Dead', 'Hot/Dead', 'Occupancy (No Mask)', 'Occupancy (With Mask)']
38 label_list = ['Hot pixels', 'Dead pixels', 'Hot/Dead pixels', 'No Mask', 'With Mask']
39 color_list = [ROOT.kRed + 1, ROOT.kAzure - 5, ROOT.kGray + 2, ROOT.kOrange + 1, ROOT.kSpring + 5]
40 max_list = [4., 4., 20., 3., 3.]
41 plot_type_dict = {
42  _type: {'label': _label, 'color': _color, 'max': _max}
43  for (_type, _label, _color, _max) in zip(type_list, label_list, color_list, max_list)
44 }
45 # marker_style = 20
46 
47 
48 # condition db object (payload) checkers
50  """Abstract base class describing interfaces to make plots from condition db."""
51 
52  def __init__(self, name, objType, tfile, rundir="", use_hist=False):
53  """
54  """
55  super().__init__()
56 
57  self.namename = name
58 
59  self._objType_objType = objType
60 
61  self.dbObjdbObj = Belle2.PyDBObj(self.namename, self.objTypeobjType.Class())
62 
63  self.eventMetaDataeventMetaData = Belle2.PyStoreObj("EventMetaData")
64 
65  self.tfiletfile = tfile
66 
67  self.rundirrundir = rundir # empty means skipping self.hists
68  if self.rundirrundir != "":
69  self.tfiletfile.cd()
70  self.tfiletfile.mkdir(self.rundirrundir)
71 
72  self.graphsgraphs = {}
73 
74  self.histshists = {}
75 
76  self.hist_title_suffixhist_title_suffix = ""
77 
78  self.runstartrunstart = 999999 # a big number that will never be used
79 
80  self.runendrunend = -1
81 
82  self.expstartexpstart = 999999
83 
84  self.use_histuse_hist = use_hist
85 
86  self.define_graphsdefine_graphs()
87 
88  @property
89  def objType(self):
90  """
91  DBObj type (read only)
92  """
93  return self._objType_objType
94 
95  @property
96  def run(self):
97  """
98  Run number
99  """
100  return self.eventMetaDataeventMetaData.getRun()
101 
102  @property
103  def exp(self):
104  """
105  Experiment number
106  """
107  return self.eventMetaDataeventMetaData.getExperiment()
108 
109  def beginRun(self):
110  """
111  Call functions at beginning of a run
112  """
113  self.define_histsdefine_hists()
114  self.fill_plotsfill_plots()
115 
116  def define_graphs(self, ytitle=""):
117  """
118  Method to define TGraph
119  Parameters:
120  ytitle (str): Label for TGraph y-axis
121  """
122  self.tfiletfile.cd()
123  self.graphsgraphs.update(get_sensor_graphs(ytitle))
124 
125  def define_hists(self, name=None, title=None, ztitle=None, **kwargs):
126  """
127  Method to define TH2
128  Parameters:
129  name (str): name for TH2, to which sensor name will be attached
130  title (str): title for TH2
131  ztitle (str): title for z-axis (color bar)
132  kwargs: additional arguments
133  """
134  if self.rundirrundir:
135  self.tfiletfile.cd(self.rundirrundir)
136  self.histshists.update(get_sensor_maps(name, title, ztitle, self.runrun, **kwargs))
137 
138  @abstractmethod
139  def get_db_content(self):
140  """
141  Abstract method to get content of a payload
142  Should return a Dictionary with sensorID.getID() as key and relaated calibration results as value
143  """
144 
145  @abstractmethod
146  def get_graph_value(self, sensor_db_content):
147  """
148  Abstract method to get a value for each TGraph
149  Parameters:
150  sensor_db_content (Any): Calibration results of a module
151  """
152 
153  def get_graph_value_from_hist(self, h2=None):
154  """
155  Method to get a value for each TGraph
156  Parameters:
157  h2 (TH2): If not none, get value from h2
158  """
159  return None
160 
161  def set_hist_content(self, h2, sensor_db_content):
162  """
163  Method to set TH2 bins
164  Parameters:
165  h2 (TH2): TH2F object for handling values of a pixel matrix
166  sensor_db_content (Any): Calibration results of a module
167  """
168  pass
169 
170  def fill_plots(self):
171  """
172  Method to fill plot objects
173  """
174  if self.exp < self.expstart:
175  self.expstart = self.exp
176  if self.run < self.runstart:
177  self.runstart = self.run
178  if self.run > self.runend:
179  self.runend = self.run
180 
181  db_content = self.get_db_content()
182  values_dict = {}
183  pre_values_dict = {}
184  for sensorID in sensorID_list:
185  sensor_db_content = db_content[sensorID.getID()]
186  if self.graphs:
187  if not self.use_hist:
188  values_dict[sensorID.getID()] = self.get_graph_value(sensor_db_content)
189  gr = self.graphs[sensorID.getID()]
190  pre_values_dict[sensorID.getID()] = gr.GetPointY(gr.GetN() - 1)
191  # Saving graph points only for different values (sensor-wise)
192  # value = self.get_graph_value(sensor_db_content)
193  # pre_value = gr.GetPointY(gr.GetN()-1)
194  # if value != pre_value:
195  # gr.SetPoint(gr.GetN(), self.run, value)
196  # Saving masked map
197  if self.rundir != "" and self.hists:
198  h2 = self.hists[sensorID.getID()]
199  self.set_hist_content(h2, sensor_db_content)
200  # Update title with total count
201  h2.SetTitle(h2.GetTitle() + self.hist_title_suffix)
202  self.tfile.cd(self.rundir)
203  h2.Write()
204  if self.use_hist:
205  values_dict[sensorID.getID()] = self.get_graph_value_from_hist(h2)
206  # Saving graph points only for different conditions
207  if values_dict != pre_values_dict:
208  for sensorID in sensorID_list:
209  gr = self.graphs[sensorID.getID()]
210  gr.SetPoint(gr.GetN(), self.run, values_dict[sensorID.getID()])
211 
212  def draw_plots(self, canvas=None, cname="", ymin=0., ymax=None, logy=False):
213  """
214  Method to draw plots on a TCanvas
215  Parameters:
216  canvas (TCanvas): ROOT TCanvas for plotting
217  canme (str): Name of the canvas
218  ymin (float): Minimum value of y-axis for plotting
219  ymax (float): Maximum of y-axis for plotting
220  logy (bool): Flag to use log scale for y-axis
221  """
222  if not canvas:
223  return
224  else:
225  canvas.Clear()
226  canvas.SetWindowSize(1000, 400)
227  canvas.SetLeftMargin(0.09)
228  canvas.SetRightMargin(0.05)
229  self.tfiletfile.cd()
230  for i, sensorID in enumerate(sensorID_list):
231  sensor_id = sensorID.getID()
232  if i == 0:
233  if ymax is None:
234  ymax = np.array([np.array(gr.GetY()) for gr in list(self.graphsgraphs.values())
235  if isinstance(gr, ROOT.TGraph)]).max()
236  self.graphsgraphs[sensor_id].SetMaximum(ymax)
237  self.graphsgraphs[sensor_id].SetMinimum(ymin)
238  self.graphsgraphs[sensor_id].Draw("AP")
239  else:
240  self.graphsgraphs[sensor_id].Draw("P")
241  if self.graphsgraphs["TLegends"]:
242  for leg in self.graphsgraphs["TLegends"]:
243  leg.Draw()
244  self.save_canvassave_canvas(canvas, cname, logy=logy)
245 
246  def save_canvas(self, canvas, cname, logy=False):
247  """
248  Save TCanvas to png/pdf format and do not write it to the ROOT file by default
249  Parameters:
250  canvas (TCanvas): ROOT TCanvas for plotting
251  canme (str): Name of the canvas
252  logy (bool): Flag to use log scale for y-axis
253  """
254  if cname:
255  canvas.SetName(cname)
256  exp_run = f"e{self.expstart:05}_r{self.runstart:04}-{self.runend:04}"
257  # Draw Belle II label
258  latex_r.DrawLatex(0.95, 0.92, "Belle II Experiment: " + exp_run.replace("_", " "))
259  # Print and write TCanvas
260  if logy:
261  canvas.SetLogy(1)
262  canvas.Update()
263  canvas.Modified()
264  canvas.Print(f"{exp_run}_{cname}_vs_run_logy.png")
265  canvas.Print(f"{exp_run}_{cname}_vs_run_logy.pdf")
266  else:
267  canvas.SetLogy(0)
268  canvas.Print(f"{exp_run}_{cname}_vs_run.png")
269  canvas.Print(f"{exp_run}_{cname}_vs_run.pdf")
270  self.tfiletfile.cd()
271 
272 
274  """
275  Checker for PXDOccupancyInfoPar
276  """
277 
278  def __init__(self, name, tfile, rundir=""):
279  """
280  """
281  super().__init__(name, Belle2.PXDOccupancyInfoPar, tfile, "")
282 
283  def define_graphs(self):
284  """
285  Method to define TGraph
286  """
287  super().define_graphs(ytitle="Occupancy (With Mask) [%]")
288 
289  def get_db_content(self):
290  content_dic = {}
291  for sensorID in sensorID_list:
292  numID = sensorID.getID()
293  raw_occ = self.dbObjdbObj.getOccupancy(numID)
294  # The default value is -1. It seems 0 will not be updated into the payload.
295  content_dic[numID] = raw_occ if raw_occ >= 0 else 0
296 
297  return content_dic
298 
299  def get_graph_value(self, sensor_db_content):
300  return sensor_db_content * 100
301 
302  def draw_plots(self, canvas=None, **kwargs):
303  """
304  Method to draw plots on a TCanvas
305  """
306  # We don't use raw occupanncy. They will be corrected after takig out dead pixels
307  pass
308  # super().draw_plots(canvas=canvas, cname="OccupancyWithMask", ymin=0., ymax=plot_type_dict["occ_masked"]["max"])
309 
310 
311 class PXDMaskedPixelsChecker(ConditionCheckerBase):
312  """
313  Checker for PXDMaskedPixelPar
314  """
315 
316  def __init__(self, name, tfile, rundir="maps"):
317  """
318  """
319  super().__init__(name, Belle2.PXDMaskedPixelPar, tfile, rundir)
320 
321  def define_graphs(self):
322  """
323  Method to define TGraph
324  """
325  super().define_graphs(ytitle="Hot pixels [%]")
326 
327  def define_hists(self):
328  """
329  Method to define TH2
330  """
331  super().define_hists(name="MaskedPixels", title="Masked Pixels", ztitle="isMasked")
332 
333  def get_db_content(self):
334  return self.dbObjdbObj.getMaskedPixelMap()
335 
336  def get_graph_value(self, sensor_db_content):
337  hotcount = len(sensor_db_content)
338 
339  self.hist_title_suffixhist_title_suffixhist_title_suffix = f" ({hotcount} pixels)"
340  return hotcount * 100. / nPixels
341 
342  def set_hist_content(self, h2, sensor_db_content):
343  # loop over all masked pixels
344  for pixelID in sensor_db_content:
345  uCell = int(pixelID / nVCells)
346  vCell = pixelID % nVCells
347  h2.SetBinContent(int(uCell + 1), int(vCell + 1), 1)
348 
349  def draw_plots(self, canvas=None, cname="PXDHotPixel", ymin=0., ymax=plot_type_dict["hot"]["max"], **kwargs):
350  """
351  Method to draw plots on a TCanvas
352  """
353  super().draw_plots(canvas=canvas, cname=cname, ymin=ymin, ymax=ymax, **kwargs)
354 
355 
357  """
358  Checker for PXDDeadPixelPar
359  """
360 
361  def __init__(self, name, tfile, rundir="maps", use_hist=True):
362  """
363  """
364  super().__init__(name, Belle2.PXDDeadPixelPar, tfile, rundir)
365 
366  def define_graphs(self):
367  """
368  Method to define TGraph
369  """
370  super().define_graphs(ytitle="Dead pixels [%]")
371 
372  def define_hists(self):
373  """
374  Method to define TH2
375  """
376  super().define_hists(name="DeadPixels", title="Dead Pixels", ztitle="isDead")
377 
378  def get_db_content(self):
379  deadsensormap = self.dbObjdbObj.getDeadSensorMap()
380  deaddrainmap = self.dbObjdbObj.getDeadDrainMap()
381  deadrowmap = self.dbObjdbObj.getDeadRowMap()
382  deadsinglesmap = self.dbObjdbObj.getDeadSinglePixelMap()
383 
384  content_dic = {}
385  for sensorID in sensorID_list:
386  numID = sensorID.getID()
387  content_dic[numID] = {}
388  if numID in deadsensormap:
389  content_dic[numID]["deadsensor"] = True
390  else:
391  content_dic[numID]["deadsensor"] = False
392  content_dic[numID]["deaddrains"] = deaddrainmap[numID]
393  content_dic[numID]["deadrows"] = deadrowmap[numID]
394  content_dic[numID]["deadsingles"] = deadsinglesmap[numID]
395  return content_dic
396 
397  def get_graph_value(self, sensor_db_content):
398  deadcount = 0
399  if sensor_db_content["deadsensor"]:
400  deadcount = nPixels
401  else:
402  n_deaddrains = len(sensor_db_content["deaddrains"])
403  n_deadrows = len(sensor_db_content["deadrows"])
404  if n_deaddrains == 0 or n_deadrows == 0:
405  # Every dead drain counts for 192 dead pixels
406  deadcount = n_deaddrains * 192
407  # Every dead row counts for 250 dead pixels
408  # This can lead to double counting of dead pixel from dead drains
409  # The double counting can be avoided by using TH2.Integral().
410  deadcount += n_deadrows * 250
411  # Every dead single pixels
412  deadcount += len(sensor_db_content["deadsingles"])
413  else: # Using a histogram to avoid double counting
414  # Just create a temporary TH2
415  h2 = list(get_sensor_maps(sensorID_list=sensorID_list[0:1]).values())[0]
416  self.set_hist_contentset_hist_contentset_hist_content(h2, sensor_db_content)
417  deadcount = h2.Integral()
418 
419  self.hist_title_suffixhist_title_suffixhist_title_suffix = f" ({deadcount} pixels)"
420  return min(deadcount, nPixels) * 100. / nPixels
421 
423  return h2.Integral() * 100. / nPixels
424 
425  def set_hist_content(self, h2, sensor_db_content):
426  # loop over all dead pixels
427  if sensor_db_content["deadsensor"]:
428  ones = np.ones(nPixels)
429  h2.SetContent(ones)
430  else:
431  for drainID in sensor_db_content["deaddrains"]:
432  for iGate in range(192):
433  uCell = drainID / 4
434  vCell = drainID % 4 + iGate * 4
435  h2.SetBinContent(int(uCell + 1), int(vCell + 1), 1)
436 
437  for vCell in sensor_db_content["deadrows"]:
438  for uCell in range(nUCells):
439  h2.SetBinContent(int(uCell + 1), int(vCell + 1), 1)
440 
441  for pixelID in sensor_db_content["deadsingles"]:
442  uCell = int(pixelID / nVCells)
443  vCell = pixelID % nVCells
444  h2.SetBinContent(int(uCell + 1), int(vCell + 1), 1)
445 
446  def draw_plots(self, canvas=None, cname="PXDDeadPixel", ymin=0., ymax=plot_type_dict["dead"]["max"], **kwargs):
447  """
448  Method to draw plots on a TCanvas
449  """
450  super().draw_plots(canvas=canvas, cname=cname, ymin=ymin, ymax=ymax, **kwargs)
451 
452 
454  """
455  Checker for PXDGainMapPar
456  """
457 
458  def __init__(self, name, tfile, rundir="maps"):
459  """
460  """
461  super().__init__(name, Belle2.PXDGainMapPar, tfile, rundir)
462 
463  def define_graphs(self):
464  """
465  Method to define TGraph
466  """
467  super().define_graphs(ytitle="Gain / MC default")
468 
469  def define_hists(self):
470  """
471  Method to define TH2
472  """
473  nU = self.dbObjdbObj.getBinsU()
474  nV = self.dbObjdbObj.getBinsV()
475  super().define_hists(name="GainMap", title="Gain / MC default", ztitle="Relative gain", nUCells=nU, nVCells=nV)
476 
477  def get_db_content(self):
478  gainMap = self.dbObjdbObj.getCalibrationMap()
479  content_dic = {}
480  for sensorID in sensorID_list:
481  numID = sensorID.getID()
482  content_dic[numID] = np.array(list(gainMap[numID]))
483  return content_dic
484 
485  def get_graph_value(self, sensor_db_content):
486  """
487  sensor_db_content (np.array): Array of gain factors of a module
488  """
489  return sensor_db_content.mean()
490 
491  def set_hist_content(self, h2, sensor_db_content):
492  array2hist(sensor_db_content.reshape(h2.GetNbinsX(), h2.GetNbinsY()), h2)
493 
494  def draw_plots(self, canvas=None, cname="PXDGain", ymin=0.5, ymax=2.5, **kwargs):
495  """
496  Method to draw plots on a TCanvas
497  """
498  super().draw_plots(canvas=canvas, cname=cname, ymin=ymin, ymax=ymax, **kwargs)
The payload telling which PXD pixel is dead (=Readout system does not receive signals)
The payload class for PXD gain corrections.
Definition: PXDGainMapPar.h:43
The payload telling which PXD pixel to mask (ignore)
The payload collecting some meta information from running the masking algorithm.
Class to access a DBObjPtr from Python.
Definition: PyDBObj.h:50
a (simplified) python wrapper for StoreObjPtr.
Definition: PyStoreObj.h:67
graphs
Dictionary of plots (TGraph) summarizing variables vs run.
use_hist
Flag to get TGraph values from a histogram (TH2F)
rundir
Directory for writing histograms of each run.
def save_canvas(self, canvas, cname, logy=False)
def draw_plots(self, canvas=None, cname="", ymin=0., ymax=None, logy=False)
def define_hists(self, name=None, title=None, ztitle=None, **kwargs)
hists
Dictionary of plots (TH1) to be saved for each run.
def __init__(self, name, objType, tfile, rundir="", use_hist=False)
def draw_plots(self, canvas=None, cname="PXDDeadPixel", ymin=0., ymax=plot_type_dict["dead"]["max"], **kwargs)
def __init__(self, name, tfile, rundir="maps", use_hist=True)
def __init__(self, name, tfile, rundir="maps")
def draw_plots(self, canvas=None, cname="PXDGain", ymin=0.5, ymax=2.5, **kwargs)
def draw_plots(self, canvas=None, cname="PXDHotPixel", ymin=0., ymax=plot_type_dict["hot"]["max"], **kwargs)
static ExpRun getRun(std::map< ExpRun, std::pair< double, double >> runs, double t)
Get exp number + run number from time.
Definition: Splitter.cc:262