Belle II Software  release-08-01-10
EventInspectorLookback.py
1 #!/usr/bin/env python3
2 
3 
10 
11 # Purpose:
12 # basf module to histogram useful values in RawKLM and KLMHit2d data-objects in an SROOT file.
13 #
14 
15 import basf2
16 import bklmDB
17 import math
18 import array
19 import ROOT
20 from ROOT import Belle2
21 
22 
23 class EventInspectorLookback(basf2.Module):
24  """Analyze RPC lookback-window parameter settings, fill histograms"""
25 
26 
27  BKLM_ID = 0x07000000
28 
29  EKLM_ID = 0x08000000
30 
31  BKLM_STRIP_BIT = 0
32 
33  BKLM_PLANE_BIT = 6
34 
35  BKLM_LAYER_BIT = 7
36 
37  BKLM_SECTOR_BIT = 11
38 
39  BKLM_SECTION_BIT = 14
40 
41  BKLM_MAXSTRIP_BIT = 15
42 
43  BKLM_STRIP_MASK = 0x3f
44 
45  BKLM_PLANE_MASK = (1 << BKLM_PLANE_BIT)
46 
47  BKLM_LAYER_MASK = (15 << BKLM_LAYER_BIT)
48 
49  BKLM_SECTOR_MASK = (7 << BKLM_SECTOR_BIT)
50 
51  BKLM_SECTION_MASK = (1 << BKLM_SECTION_BIT)
52 
53  BKLM_MAXSTRIP_MASK = (63 << BKLM_MAXSTRIP_BIT)
54 
55  BKLM_MODULEID_MASK = (BKLM_SECTION_MASK | BKLM_SECTOR_MASK | BKLM_LAYER_MASK)
56 
57  def __init__(self, exp, run, histName, pdfName, mode, window):
58  """Constructor
59 
60  Arguments:
61  exp (str): formatted experiment number
62  run (str): formatter run number
63  histName (str): path name of the output histogram ROOT file
64  pdfName (str): path name of the output histogram PDF file
65  mode (int): specifies the lookback-window mode
66  0: coarse window start values
67  1: coarse window width values
68  2: fine window start values
69  3: fine window width values
70  window (int, int, int): specifies the lookback-window min, max and step values
71  """
72  super().__init__()
73 
74  self.expexp = exp
75 
76  self.runrun = run
77 
78  self.histNamehistName = histName
79 
80  self.pdfNamepdfName = pdfName
81 
82  windowModes = {0: "coarse start", 1: "coarse width", 2: "fine start", 3: "fine width"}
83 
84  self.windowModewindowMode = windowModes[mode]
85 
86  self.windowMinValuewindowMinValue = window[0]
87 
88  self.windowMaxValuewindowMaxValue = window[1]
89 
90  self.windowStepValuewindowStepValue = window[2]
91  print(f"Mode = {mode} start = {window[0]} end = {window[1]} step = {window[2]}")
92 
93  def initialize(self):
94  """Handle job initialization: fill the mapping database, create histograms"""
95 
96  expRun = f'e{int(self.exp):02d}r{int(self.run)}: '
97 
98 
99  self.electIdToModuleIdelectIdToModuleId = bklmDB.fillDB()
100 
101  self.sectorFBToDCsectorFBToDC = [11, 15, 2, 6, 10, 14, 3, 7, 9, 13, 0, 4, 8, 12, 1, 5]
102 
103  self.dcToSectorFBdcToSectorFB = [10, 14, 2, 6, 11, 15, 3, 7, 12, 8, 4, 0, 13, 9, 5, 1]
104 
105  self.histogramFilehistogramFile = ROOT.TFile.Open(self.histNamehistName, "RECREATE")
106  # All histograms/scatterplots in the output file will show '# of events' only
107  ROOT.gStyle.SetOptStat(10)
108 
109  # create the rawKLM histograms
110 
111 
112  self.hist_mappedRPCTimeCalhist_mappedRPCTimeCal = ROOT.TH1F(
113  'mappedRPCTimeCal', expRun + 'RPC time distribution;t - t(trigger) (ns)', 256, -0.5, 1023.5)
114 
115  self.dict_mappedRPCTimeCalByWindowdict_mappedRPCTimeCalByWindow = {}
116 
117  self.dict_nRawKLMsdict_nRawKLMs = {}
118  for window in range(self.windowMinValuewindowMinValue, self.windowMaxValuewindowMaxValue+1, self.windowStepValuewindowStepValue):
119  label = f"mappedRPCTimeCalByWindow{window:04x}"
120  title = f"{expRun}RPC time distribution for lookback-window {self.windowMode} = {window:04x}" + \
121  ";t - t(trigger) (ns)"
122  self.dict_mappedRPCTimeCalByWindowdict_mappedRPCTimeCalByWindow[window] = ROOT.TH1F(label, title, 256, -0.5, 1023.5)
123  self.dict_nRawKLMsdict_nRawKLMs[window] = 0
124 
125  self.hist_mappedRPCTimeCalByWindowhist_mappedRPCTimeCalByWindow = self.dict_mappedRPCTimeCalByWindowdict_mappedRPCTimeCalByWindow[self.windowMinValuewindowMinValue]
126 
127  # Create the KLMHit2d-related histograms
128 
129 
130  self.hist_occupancyForwardXYhist_occupancyForwardXY = ROOT.TH2F('occupancyForwardXY',
131  expRun + 'Forward xy occupancy;x(cm);y(cm)',
132  230, -345.0, 345.0, 230, -345.0, 345.0)
133 
134  self.hist_occupancyBackwardXYhist_occupancyBackwardXY = ROOT.TH2F('occupancyBackwardXY',
135  expRun + 'Backward xy occupancy;x(cm);y(cm)',
136  230, -345.0, 345.0, 230, -345.0, 345.0)
137 
138  self.hist_occupancyXYByWindowhist_occupancyXYByWindow = self.dict_occupancyXYByWindowdict_occupancyXYByWindow[self.windowMinValuewindowMinValue]
139 
140 
141  self.dict_occupancyXYByWindowdict_occupancyXYByWindow = {}
142 
143  self.dict_nHit2dsdict_nHit2ds = {}
144  for window in range(self.windowMinValuewindowMinValue, self.windowMaxValuewindowMaxValue+1, self.windowStepValuewindowStepValue):
145  label = f"occupancyXYByWindow{window:04x}"
146  title = f"{expRun}Forward xy occupancy for lookback-window {self.windowMode} = {window:04x};x(cm);y(cm)"
147  self.dict_occupancyXYByWindowdict_occupancyXYByWindow[window] = ROOT.TH2F(label, title, 230, -345.0, 345.0, 230, -345.0, 345.0)
148  self.dict_nHit2dsdict_nHit2ds[window] = 0
149 
150 
151  self.dict_nEventsdict_nEvents = {}
152  for window in range(self.windowMinValuewindowMinValue, self.windowMaxValuewindowMaxValue+1, self.windowStepValuewindowStepValue):
153  self.dict_nEventsdict_nEvents[window] = 0
154 
155 
156  self.windowValuewindowValue = -1
157 
158  def terminate(self):
159  """Handle job termination: draw histograms, close output files"""
160 
161  canvas = ROOT.TCanvas("canvas", self.pdfNamepdfName, 1600, 1600)
162  title = f'{self.pdfName}['
163  canvas.SaveAs(title)
164  canvas.Clear()
165  canvas.Divide(1, 1)
166  canvas.GetPad(0).SetGrid(1, 1)
167  canvas.GetPad(0).Update()
168  nWindows = int((self.windowMaxValuewindowMaxValue - self.windowMinValuewindowMinValue) / self.windowStepValuewindowStepValue) + 1
169  hist_nEvents = ROOT.TH1F('nEvents',
170  f'Number of events;Lookback-window {self.windowMode} value',
171  nWindows, self.windowMinValuewindowMinValue, self.windowMaxValuewindowMaxValue+self.windowStepValuewindowStepValue)
172  hist_nEvents.SetStats(False)
173  hist_nEvents.SetMinimum(0)
174  values = array.array('d', [0]) # dummy vaue for the histogram's underflow bin
175  for key in self.dict_nEventsdict_nEvents:
176  values.append(self.dict_nEventsdict_nEvents[key])
177  values.append(0) # dummy value for the histogram's overflow bin
178  hist_nEvents.SetContent(values)
179  hist_nEvents.Draw("HIST")
180  canvas.Print(self.pdfNamepdfName, f"Title:{hist_nEvents.GetName()}")
181  hist_nRawKLMs = ROOT.TH1F('nRawKLMs',
182  f'Mean number of RawKLM hits per event;Lookback-window {self.windowMode} value',
183  nWindows, self.windowMinValuewindowMinValue, self.windowMaxValuewindowMaxValue+self.windowStepValuewindowStepValue)
184  hist_nRawKLMs.SetStats(False)
185  hist_nRawKLMs.SetMinimum(0)
186  ratios = array.array('d', [0]) # dummy ratio for the histogram's underflow bin
187  errors = array.array('d', [0]) # dummy error for the histogram's underflow bin
188  for key in self.dict_nRawKLMsdict_nRawKLMs:
189  numerator = self.dict_nRawKLMsdict_nRawKLMs[key]
190  denominator = float(self.dict_nEventsdict_nEvents[key])
191  if denominator > 0:
192  ratio = numerator / denominator
193  ratios.append(ratio)
194  errors.append(math.sqrt(ratio * (ratio + 1.0) / denominator)) # avoid 1/numerator
195  else:
196  ratios.append(0)
197  errors.append(0)
198  ratios.append(0) # dummy ratio for the histogram's overflow bin
199  errors.append(0) # dummy error for the histogram's overflow bin
200  hist_nRawKLMs.SetContent(ratios)
201  hist_nRawKLMs.SetError(errors)
202  hist_nRawKLMs.Draw("E0 X0 L")
203  hist_nRawKLMs.Draw("HIST SAME")
204  canvas.Print(self.pdfNamepdfName, f"Title:{hist_nRawKLMs.GetName()}")
205  hist_nHit2ds = ROOT.TH1F('nKLMHit2ds',
206  f'Mean number of KLMHit2ds per event;Lookback-window {self.windowMode} value',
207  nWindows, self.windowMinValuewindowMinValue, self.windowMaxValuewindowMaxValue+self.windowStepValuewindowStepValue)
208  hist_nHit2ds.SetStats(False)
209  hist_nHit2ds.SetMinimum(0)
210  ratios = array.array('d', [0]) # dummy ratio for the histogram's underflow bin
211  errors = array.array('d', [0]) # dummy error for the histogram's underflow bin
212  for key in self.dict_nHit2dsdict_nHit2ds:
213  numerator = self.dict_nHit2dsdict_nHit2ds[key]
214  denominator = float(self.dict_nEventsdict_nEvents[key])
215  if denominator > 0:
216  ratio = numerator / denominator
217  ratios.append(ratio)
218  errors.append(math.sqrt(ratio * (ratio + 1.0) / denominator)) # avoid 1/numerator
219  else:
220  ratios.append(0)
221  errors.append(0)
222  ratios.append(0) # dummy ratio for the histogram's overflow bin
223  errors.append(0) # dummy error for the histogram's overflow bin
224  hist_nHit2ds.SetContent(ratios)
225  hist_nHit2ds.SetError(errors)
226  hist_nHit2ds.Draw("E0 X0 L")
227  hist_nHit2ds.Draw("HIST SAME")
228  canvas.Print(self.pdfNamepdfName, f"Title:{hist_nHit2ds.GetName()}")
229 
230  self.hist_mappedRPCTimeCalhist_mappedRPCTimeCal.Draw()
231  canvas.Print(self.pdfNamepdfName, f"Title:{self.hist_mappedRPCTimeCal.GetName()}")
232  for key in self.dict_mappedRPCTimeCalByWindowdict_mappedRPCTimeCalByWindow:
233  theHist = self.dict_mappedRPCTimeCalByWindowdict_mappedRPCTimeCalByWindow[key]
234  theHist.Draw()
235  canvas.Print(self.pdfNamepdfName, f"Title:{theHist.GetName()}")
236 
237  self.hist_occupancyBackwardXYhist_occupancyBackwardXY.Draw("colz")
238  canvas.Print(self.pdfNamepdfName, f"Title:{self.hist_occupancyBackwardXY.GetName()}")
239  self.hist_occupancyForwardXYhist_occupancyForwardXY.Draw("colz")
240  canvas.Print(self.pdfNamepdfName, f"Title:{self.hist_occupancyForwardXY.GetName()}")
241  for key in self.dict_occupancyXYByWindowdict_occupancyXYByWindow:
242  theHist = self.dict_occupancyXYByWindowdict_occupancyXYByWindow[key]
243  theHist.Draw("colz")
244  lastTitle = f"Title:{theHist.GetName()}"
245  canvas.Print(self.pdfNamepdfName, lastTitle)
246  pdfNameLast = f'{self.pdfName}]'
247  canvas.Print(pdfNameLast, lastTitle)
248  self.histogramFilehistogramFile.Write()
249  self.histogramFilehistogramFile.Close()
250  print('Goodbye')
251 
252  def beginRun(self):
253  """Handle begin of run: print diagnostic message"""
254  EventMetaData = Belle2.PyStoreObj('EventMetaData')
255  print('beginRun', EventMetaData.getRun())
256 
257  def endRun(self):
258  """Handle end of run: print diagnostic message"""
259  EventMetaData = Belle2.PyStoreObj('EventMetaData')
260  print('endRun', EventMetaData.getRun())
261 
262  def event(self):
263  """Process one event: fill histograms"""
264 
265  EventMetaData = Belle2.PyStoreObj('EventMetaData')
266  event = EventMetaData.getEvent()
267  rawklms = Belle2.PyStoreArray('RawKLMs')
268  hit2ds = Belle2.PyStoreArray('KLMHit2ds')
269 
270  # Process the RawKLMs
271 
272  for copper in range(0, len(rawklms)):
273  rawklm = rawklms[copper]
274  if rawklm.GetNumEntries() != 1:
275  print('##0 Event', event, 'copper', copper, ' getNumEntries=', rawklm.GetNumEntries())
276  continue
277  nodeID = rawklm.GetNodeID(0) - self.BKLM_IDBKLM_ID
278  if nodeID >= self.EKLM_IDEKLM_ID - self.BKLM_IDBKLM_ID:
279  nodeID = nodeID - (self.EKLM_IDEKLM_ID - self.BKLM_IDBKLM_ID) + 4
280  if (nodeID < 0) or (nodeID > 4): # examine BKLM nodes only
281  continue
282  trigCtime = (rawklm.GetTTCtime(0) & 0x7ffffff) << 3 # (ns)
283  for finesse in range(0, 4):
284  dc = (finesse << 2) + (copper & 0x3)
285  sectorFB = self.dcToSectorFBdcToSectorFB[dc] # noqa (F841) kept for completeness
286  nWords = rawklm.GetDetectorNwords(0, finesse)
287  if nWords <= 0:
288  continue
289  bufSlot = rawklm.GetDetectorBuffer(0, finesse)
290  lastWord = bufSlot[nWords - 1]
291  windowValue = (lastWord >> 16) & 0xffff
292  if windowValue != self.windowValuewindowValue:
293  if windowValue in self.dict_nEventsdict_nEvents:
294  self.windowValuewindowValue = windowValue
295  self.hist_mappedRPCTimeCalByWindowhist_mappedRPCTimeCalByWindow = self.dict_mappedRPCTimeCalByWindowdict_mappedRPCTimeCalByWindow[windowValue]
296  self.hist_occupancyXYByWindowhist_occupancyXYByWindow = self.dict_occupancyXYByWindowdict_occupancyXYByWindow[windowValue]
297  else:
298  return # skip bogus event, incuding event with seed value of 0xcafe
299  if lastWord & 0xffff != 0:
300  print("##1 Event", event, 'copper', copper, 'finesse', finesse, 'n=', nWords, 'lastWord=', hex(lastWord))
301  if (nWords % 2) == 0:
302  print("##2 Event", event, 'copper', copper, 'finesse', finesse, 'n=', nWords, 'should be odd -- skipping')
303  continue
304  n = nWords >> 1 # number of Data-Concentrator data packets
305  self.dict_nRawKLMsdict_nRawKLMs[self.windowValuewindowValue] += n
306  # first (and only) pass over this DC's hits: histogram everything
307  for j in range(0, n):
308  word0 = bufSlot[j * 2]
309  word1 = bufSlot[j * 2 + 1]
310  ctime = word0 & 0xffff # noqa (F841) kept for completeness
311  channel = (word0 >> 16) & 0x7f
312  axis = (word0 >> 23) & 0x01
313  lane = (word0 >> 24) & 0x1f # 1..2 for scints, 8..20 for RPCs (=readout-board slot - 7)
314  flag = (word0 >> 30) & 0x03 # 1 for RPCs, 2 for scints
315  tdc = (word1 >> 16) & 0x07ff
316  isRPC = (flag == 1)
317  electId = (channel << 12) | (axis << 11) | (lane << 6) | (finesse << 4) | nodeID
318  if electId in self.electIdToModuleIdelectIdToModuleId: # BKLM mapped-channel histograms
319  if isRPC:
320  tCal = int(tdc - trigCtime) & 0x03ff # in ns, range is 0..1023
321  self.hist_mappedRPCTimeCalhist_mappedRPCTimeCal.Fill(tCal)
322  self.hist_mappedRPCTimeCalByWindowhist_mappedRPCTimeCalByWindow.Fill(tCal)
323 
324  # for normalization of the hit-counter dictionaries, now that we know that this is a valid event
325 
326  self.dict_nEventsdict_nEvents[self.windowValuewindowValue] += 1
327 
328  # Process the KLMHit2ds
329 
330  self.dict_nHit2dsdict_nHit2ds[self.windowValuewindowValue] += len(hit2ds)
331  for hit2d in hit2ds:
332  key = hit2d.getModuleID()
333  fb = (key & self.BKLM_SECTION_MASKBKLM_SECTION_MASK) >> self.BKLM_SECTION_BITBKLM_SECTION_BIT
334  x = hit2d.getGlobalPositionX()
335  y = hit2d.getGlobalPositionY()
336  if fb == 0: # backward
337  self.hist_occupancyBackwardXYhist_occupancyBackwardXY.Fill(x, y)
338  else: # forward
339  self.hist_occupancyForwardXYhist_occupancyForwardXY.Fill(x, y)
340  self.hist_occupancyXYByWindowhist_occupancyXYByWindow.Fill(x, y)
def fillDB()
Definition: bklmDB.py:16
A (simplified) python wrapper for StoreArray.
Definition: PyStoreArray.h:72
a (simplified) python wrapper for StoreObjPtr.
Definition: PyStoreObj.h:67
int EKLM_ID
COPPER base identifier for EKLM readout.
hist_mappedRPCTimeCalByWindow
reference to the RPC-time histogram for the currevent value of the lookback window parameter
hist_occupancyXYByWindow
reference to the xy scatterplot for the currevent value of the lookback window parameter
dict_nHit2ds
dictionary of the number of KLMHit2ds for each lookback-window value
hist_mappedRPCTimeCal
histogram of RPC TDC - trigger value
hist_occupancyBackwardXY
scatterplot of end view of backward BKLM for all KLMHit2ds
windowMaxValue
highest observed lookback-window value
hist_occupancyForwardXY
scatterplot of end view of forward BKLM for all KLMHit2ds
tuple BKLM_SECTION_MASK
bit mask for section [0..1]; forward is 0
dict_mappedRPCTimeCalByWindow
dictionary of histograms of RPC TDC - trigger value, keyed by lookback-window value
def __init__(self, exp, run, histName, pdfName, mode, window)
dcToSectorFB
map for data concentrator -> sectorFB
int BKLM_ID
COPPER base identifier for BKLM readout.
dict_occupancyXYByWindow
dictionary of scatterplots of end view of forward BKLM, keyed by lookback-window value
dict_nRawKLMs
dictionary of the number of RawKLM hits for each lookback-window value
windowMinValue
highest observed lookback-window value
int BKLM_SECTION_BIT
bit position for section [0..1]; forward is 0
pdfName
internal copy of the pathname of the output histogram PDF file
histogramFile
Output ROOT TFile that will contain the histograms/scatterplots.
histName
internal copy of the pathname of the output histogram ROOT file
dict_nEvents
dictionary of the number of events for each lookback-window value, for normalization
electIdToModuleId
readout <-> detector map (from the information retrieved from the conditions database)
windowMode
window mode as a string for histogram labels/titles
windowValue
cached value of the lookback-window value, to avoid unnecessary reassignments-to-same-value in event(...
sectorFBToDC
map for sectorFB -> data concentrator