Belle II Software  release-05-01-25
channelMasking_hltdqm.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 import sys
5 import os
6 import glob
7 import ROOT
8 import logging
9 from ROOT import TH1F, TH2F, TProfile, TFile
10 
11 
12 # checks if the run can be analyzed based on the HLT histograms, returns -1 if not and the number of events otherwise
13 def checkIfRunUsable(file):
14  # consider only runs identified as physics runs (possible since experiment 8)
15  h = file.FindObject('DQMInfo/rtype')
16  if not h:
17  logging.warning(fileName + ' ... histogram runtype not found')
18  return -1
19  if not h.GetTitle() == "physics":
20  logging.info(fileName + ' ... not a physics run, skipping')
21  return -1
22 
23  # check if we have enough events for meaningful masking
24  h = file.FindObject('TOP/good_hits_per_event1')
25  if not h:
26  logging.warning(fileName + ' ... histogram good_hits_per_event1 not found')
27  return -1
28  nev = int(h.GetEntries())
29  if nev < 10000:
30  logging.warning(fileName + 'run =' + str(run) + 'events =' + str(nev) + ' ... skipped, not enough events')
31  return -1
32 
33  # check if we have the necessary histogram to determine masking
34  h = file.FindObject('TOP/good_hits')
35  if not h:
36  logging.warning(fileName + ' ... histogram good_hits not found')
37  return -1
38  if h.GetEntries() == 0:
39  logging.warning(fileName + ' ... histogram good_hits has no entries ... skipped')
40  return -1
41  return nev
42 
43 
44 # creates a file with histograms for masked channels and returns the total number of masked channels in a run
45 def makeChannelMasks(file, outFileName):
46 
47  masks = [TH1F('slot_' + str(slot), 'Channel mask for slot ' + str(slot),
48  512, 0.0, 512.0) for slot in range(1, 17)]
49  masked = 0
50 
51  # dead/hot channels: <10% or >1000% of the average in this slot
52  for slot in range(1, 17):
53  h = file.FindObject('TOP/good_channel_hits_' + str(slot))
54  if not h:
55  logging.error('no good_channel_hits found for slot'+str(slot))
56  continue
57  # calculate average number of hits per channel
58  mean = 0
59  n = 0
60  for chan in range(h.GetNbinsX()):
61  y = h.GetBinContent(chan+1)
62  if y > 0:
63  mean += y
64  n += 1
65  if n > 0:
66  mean /= n
67  # define threshold for dead and hot
68  deadCut = mean / 10
69  hotCut = mean * 10
70  for chan in range(h.GetNbinsX()):
71  y = h.GetBinContent(chan+1)
72  if y <= deadCut:
73  masks[slot-1].SetBinContent(chan+1, 1) # 1 = dead
74  masked += 1
75  elif y > hotCut:
76  masks[slot-1].SetBinContent(chan+1, 2) # 2 = noisy
77  masked += 1
78 
79  # asics with window corruption
80  for slot in range(1, 17):
81  h = file.FindObject('TOP/window_vs_asic_' + str(slot))
82  if not h:
83  logging.error('Error: no window_vs_asic found for slot' + str(slot))
84  continue
85  h0 = h.ProjectionX()
86  h1 = h.ProjectionX('_tmp', 222, 245)
87  for asic in range(h.GetNbinsX()):
88  if h0.GetBinContent(asic+1) > 0:
89  r = 1 - h1.GetBinContent(asic+1) / h0.GetBinContent(asic+1)
90  if r > 0.20:
91  for chan in range(8):
92  masks[slot-1].SetBinContent(asic*8+chan+1, 2) # mark as noisy
93  masked += 1
94 
95  # save to file
96  if masked < 3000: # don't save for nonsense
97  outfile = TFile(outFileName, 'recreate')
98  for h in masks:
99  h.Write()
100  outfile.Close()
101  return masked
102 
103 
104 experiment = 10
105 experimentstring = "{:04d}".format(experiment)
106 outdir = 'masks'
107 if not os.path.exists(outdir):
108  os.makedirs(outdir)
109 fileNames = sorted(glob.glob('/group/belle2/phase3/dqm/dqmsrv1/e'+experimentstring+'/dqmhisto/hltdqm*.root'))
110 
111 logging.basicConfig(level=logging.INFO, filename="channelmasking.log")
112 logging.info("Starting channelmasking from HLT histograms")
113 logging.info("Experiment: "+str(experiment))
114 
115 numFiles = len(fileNames)
116 if numFiles is 0:
117  logging.error('No files found, exiting')
118  sys.exit()
119 
120 for fileName in fileNames:
121  run = ((fileName.split('/')[-1]).split('r')[1]).split('.')[0] # exp7-8
122  outFileName = outdir + '/channelMask_e' + experimentstring + '_r' + run + '.root'
123 
124  # skip run if we analyzed this run already
125  if os.path.exists(outFileName) or os.path.exists(outFileName.replace('masks/', 'masks/imported/')):
126  logging.debug('Output file exists for run ='+str(run)+', skipping')
127  continue
128 
129  # open file and check if it is a good physics run
130  file = TFile(fileName)
131  if not file:
132  logging.error(fileName + ' ... cannot open')
133  continue
134  file.ReadAll()
135  nev = checkIfRunUsable(file)
136  if nev == -1:
137  file.Close()
138  continue
139 
140  masked = makeChannelMasks(file, outFileName)
141  logging.info(fileName + 'run =' + str(run) + 'events =' + str(nev) + 'masked channels =' + str(masked))
142  if masked > 3000:
143  logging.critical("Result looks completely wrong, over 3000 masked channels. Inspect run "+str(run))
144  file.Close()