Belle II Software  release-06-02-00
analyzeGainEff.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 # ---------------------------------------------------------------------------------------
13 # TOP MCP-PMT channel-by-channel gain/efficiency anslysis using laser data
14 # Usage: basf2 analyzeGainEff.py <input_file.sroot> --arg --slot (slotNum) --arg --PMT (PMTNum)
15 # ---------------------------------------------------------------------------------------
16 
17 import basf2 as b2
18 import sys
19 import argparse
20 import re
21 
22 parser = argparse.ArgumentParser(description="analyze gain and efficiency for laser run data")
23 parser.add_argument("inputFile", nargs='*', default=["NoInputFile"],
24  help="Input sroot files. Mulitple files can be given.")
25 parser.add_argument("--interimRootFile", default="NoInterimRootFile",
26  help="Interim root file name to store timing-height 2D histograms.")
27 parser.add_argument("--outputRootFile", default="NoOutputRootFile",
28  help="Output root file name to save TTree containing fit results.")
29 parser.add_argument("--outputPDFFile", default="NoOutputPDFFile",
30  help="Output PDF file name to save fitting results for each channel.")
31 parser.add_argument("--interimFW", action="store_true", default=False,
32  help="use when analyzing interimFW data")
33 parser.add_argument("--slotID", type=int, default=0,
34  help="slot number [1-16]")
35 parser.add_argument("--PMTID", type=int, default=0,
36  help="slot number [1-32]")
37 parser.add_argument("--calChannel", type=int, default=0,
38  help="asic channel number where calibration signals are injected [0-7]")
39 parser.add_argument("--threshold", type=float, default=100.,
40  help="threshold in pulse height fitting")
41 parser.add_argument("--globalDAQ", action="store_true", default=False,
42  help="Force to assume global DAQ data.")
43 parser.add_argument("--pocketDAQ", action="store_true", default=False,
44  help="Force to assume global DAQ data.")
45 parser.add_argument("--noOfflineFE", action="store_true", default=False,
46  help="Use only online FE hits, and do not use waveform data.")
47 parser.add_argument("--useSingleCalPulse", action="store_true", default=False,
48  help="Do not require double calibration pulses, but require only the first one.")
49 parser.add_argument("--windowSelection", type=int, default=0,
50  help="Select window number (All=0,Odd=2,Even=1)")
51 parser.add_argument("--includePrimaryChargeShare", action="store_true", default=False,
52  help="don't want to use primary charge share cut,type true.")
53 parser.add_argument("--includeAllChargeShare", action="store_true", default=False,
54  help="don't want to use all charge share cut,type true.")
55 parser.add_argument("--sumChargeShare", action="store_true", default=False,
56  help="When you want to use summation of charge share for gain distribution,type true.")
57 parser.add_argument("--fitoption", action="store", default='L',
58  help="fitting with chisquare=R, loglikelihood=L")
59 parser.add_argument("--timeCut", type=float, default=1,
60  help="cut of hittiming for chargeshare")
61 parser.add_argument("--HVHigherSetting", type=int, default=0,
62  help="HV positive difference from nominal value")
63 parser.add_argument("--HVLowerSetting", type=int, default=0,
64  help="HV negative difference from nominal value")
65 args = parser.parse_args()
66 
67 if (args.inputFile[0] == "NoInputFile") and (args.interimRootFile == "NoInterimRootFile"):
68  print("Steering file to study gain/efficiency analysis from laser data.")
69  print("In the first step, 2D histograms (hit time vs pulse height) were created for all the available channels, "
70  "and saved in an interim root file.")
71  print("Then, in the second process, 1D pulse height distribution is extracted "
72  "from the 2D histogram and fitted to evaluate gain and efficiency for each channel.")
73  print("The second process is done only for one give PMT as it takes time.")
74  print("usage:")
75  print("basf2 analyzeGainEff.py [input_filename1.sroot, input_filename2.sroot, ...]")
76  print(" [--arg --interimRootFile interim_histo_output.root]")
77  print(" [--arg --outputRootFile summary_tree_output.root]")
78  print(" [--arg --outputPDFFile summary_plot.pdf]")
79  print(" [--arg --slotID slotNum(1-16)] [--arg --PMTID PMTID(1-32)]")
80  print(" [--arg --calChannel asicCh(0-7)] [--arg --threshold threshold]")
81  print(" [--arg --globalDAQ] [--arg --pocketDAQ]")
82  print(" [--arg --noOfflineFE] [--arg --useSingleCalPulse]")
83  print(" [--arg --timeCut] [--arg --fitoption]")
84  print(" [--arg --windowSelection] [--arg --sumChargeShare]")
85  print(" [--arg --includePrimaryChargeShare] [--arg --includeAllChargeShare]")
86  print(" [--arg --HVHigherSetting] [--arg --HVLowerSetting]")
87  print("*When input sroot files are missing but interim root file is given,"
88  " skip the first process to create 2D histogram and start fitting.")
89  print("*Both the slot and PMT numbers are mandatory to proceed to the second processes.")
90  print("*default calibration asic channel is " + str(args.calChannel))
91  print("*default threshold is " + str(args.threshold))
92  print()
93  sys.exit()
94 
95 inputFiles = args.inputFile
96 interimRoot = args.interimRootFile
97 outputRoot = args.outputRootFile
98 outputPDF = args.outputPDFFile
99 isInterimFW = args.interimFW
100 slotId = args.slotID
101 pmtId = args.PMTID
102 calChannel = args.calChannel
103 threshold = args.threshold
104 isGlobalDAQ = False
105 isGlobalDAQForced = args.globalDAQ
106 isPocketDAQForced = args.pocketDAQ
107 isOfflineFEDisabled = args.noOfflineFE
108 windowSelect = args.windowSelection
109 includePrimaryChargeShare = args.includePrimaryChargeShare
110 includeAllChargeShare = args.includeAllChargeShare
111 sumChargeShare = args.sumChargeShare
112 HVHigherSetting = args.HVHigherSetting
113 HVLowerSetting = args.HVLowerSetting
114 fitOption = args.fitoption
115 timeCut = args.timeCut
116 
117 useSingleCalPulse = (True if isOfflineFEDisabled else args.useSingleCalPulse)
118 skipFirst = ((inputFiles[0] == "NoInputFile") and (interimRoot != "NoInterimRootFile"))
119 skipSecond = ((slotId < 1) or (slotId > 16) or (pmtId < 1) or (pmtId > 32))
120 pmtStr = "s" + ('%02d' % slotId) + "_PMT" + ('%02d' % pmtId)
121 if isGlobalDAQForced and isPocketDAQForced:
122  print("ERROR : both of --GlobalDAQ or --PocketDAQ can not be given.")
123  sys.exit()
124 if skipFirst and skipSecond:
125  print("ERROR : unable to run either of the first and second process:"
126  "no input sroot file and no valid slot ID or PMT ID")
127  sys.exit()
128 if (calChannel < 0) or (calChannel > 7):
129  print("ERROR : invalid asic channel with calibration pulses : " + str(calChannel))
130  sys.exit()
131 
132 if includePrimaryChargeShare and includeAllChargeShare:
133  print("ERROR : both of --includePrimaryChargeShare and --includeAllChargeShare can not be given.")
134  sys.exit()
135 
136 if sumChargeShare and includeAllChargeShare:
137  print("ERROR : both of --sumChargeShare and --includeAllChargeShare can not be given."
138  "While both of --sumChargeShare and --includePrimaryChargeShare can be given.")
139  sys.exit()
140 
141 if HVHigherSetting and HVLowerSetting:
142  print("ERROR : both of --HVHigherSetting and --HVLowerSetting can not be given.")
143  sys.exit()
144 
145 # data base
146 b2.reset_database()
147 path_to_db = "/group/belle2/group/detector/TOP/calibration/combined/Combined_TBCrun417x_LocaT0run4855_AfterRelease01/localDB/"
148 b2.use_local_database(path_to_db + '/localDB.txt', path_to_db)
149 
150 inputBase = inputFiles[0] if not skipFirst else interimRoot
151 dotPos = inputBase.rfind('.')
152 outputBase = inputBase[0:dotPos] if (dotPos > 0) else inputBase
153 
154 if re.search(r"run[0-9]+_slot[0-1][0-9]", inputFiles[0]):
155  outputBase = re.search(r"run[0-9]+_slot[0-1][0-9]", inputFiles[0]).group()
156 elif re.search(r"(top|cosmic|cdc|ecl|klm|test|debug|beam)\.[0-9]+\.[0-9]+", inputFiles[0]):
157  isGlobalDAQ = True
158  outputBase = re.search(r"(top|cosmic|cdc|ecl|klm|test|debug|beam)\.[0-9]+\.[0-9]+", inputFiles[0]).group()
159 
160 if interimRoot == "NoInterimRootFile":
161  interimRoot = outputBase + "_gain_histo.root"
162 if outputRoot == "NoOutputRootFile":
163  outputRoot = outputBase + "_gain_" + pmtStr + ".root"
164 if outputPDF == "NoOutputPDFFile":
165  outputPDF = outputBase + "_" + pmtStr
166 
167 if isGlobalDAQForced and (not isPocketDAQForced):
168  isGlobalDAQ = True
169 elif (not isGlobalDAQForced) and isPocketDAQForced:
170  isGlobalDAQ = False
171 
172 if not skipFirst:
173  print("*first process : " + str(inputFiles) + " --> " + interimRoot)
174 else:
175  print("*first process is skipped...")
176 if not skipSecond:
177  print("*second process : " + interimRoot + " --> " + outputRoot + ", " + outputPDF)
178 else:
179  print("*second process is skipped")
180 print("*Is global DAQ? : " + str(isGlobalDAQ))
181 print("*Offline FE : " + ("enabled" if not isOfflineFEDisabled else "disabled"))
182 print("*use double pulse : " + str(not useSingleCalPulse))
183 print("*cal. asic channel: " + str(calChannel))
184 print("*threshold : " + str(threshold))
185 print()
186 print("start process...")
187 
188 
189 if not skipFirst:
190  # Define a global tag
191  b2.use_central_database('data_reprocessing_proc8')
192 
193  # Create path
194  first = b2.create_path()
195 
196  srootInput = b2.register_module('SeqRootInput')
197  srootInput.param('inputFileNames', inputFiles)
198  first.add_module(srootInput)
199 
200  # HistoManager
201  histoman = b2.register_module('HistoManager')
202  histoman.param('histoFileName', interimRoot)
203  first.add_module(histoman)
204 
205  # conversion from RawCOPPER or RawDataBlock to RawDetector objects
206  if not isGlobalDAQ:
207  converter = b2.register_module('Convert2RawDet')
208  first.add_module(converter)
209 
210  # geometry parameters
211  gearbox = b2.register_module('Gearbox')
212  first.add_module(gearbox)
213 
214  # Geometry (only TOP needed)
215  geometry = b2.register_module('Geometry')
216  first.add_module(geometry)
217 
218  # Unpacking
219  unpack = b2.register_module('TOPUnpacker')
220  if isInterimFW:
221  unpack.param('swapBytes', True)
222  unpack.param('dataFormat', 0x0301)
223  first.add_module(unpack)
224 
225  # Add multiple hits by running feature extraction offline
226  if not isOfflineFEDisabled:
227  featureExtractor = b2.register_module('TOPWaveformFeatureExtractor')
228  first.add_module(featureExtractor)
229 
230  # Convert to TOPDigits
231  converter = b2.register_module('TOPRawDigitConverter')
232  converter.param('useSampleTimeCalibration', False)
233  converter.param('useChannelT0Calibration', False)
234  converter.param('useModuleT0Calibration', False)
235  converter.param('useCommonT0Calibration', False)
236  converter.param('calibrationChannel', calChannel) # if set, cal pulses will be flagged
237  converter.param('calpulseHeightMin', 100) # in [ADC counts]
238  converter.param('calpulseHeightMax', 650) # in [ADC counts]
239  converter.param('calpulseWidthMin', 0.8) # in [ns]
240  converter.param('calpulseWidthMax', 2.4) # in [ns]
241  first.add_module(converter)
242 
243  flagSetter = b2.register_module('TOPXTalkChargeShareSetter')
244  flagSetter.param('sumChargeShare', sumChargeShare)
245  flagSetter.param('timeCut', timeCut) # in [nsec]
246  first.add_module(flagSetter)
247 
248  laserHitSelector = b2.register_module('TOPLaserHitSelector')
249  laserHitSelector.param('useDoublePulse', (not useSingleCalPulse))
250  laserHitSelector.param('minHeightFirstCalPulse', 300) # in [ADC counts]
251  laserHitSelector.param('minHeightSecondCalPulse', 100) # in [ADC counts]
252  laserHitSelector.param('nominalDeltaT', 25.5) # in [ns]
253  laserHitSelector.param('nominalDeltaTRange', 2) # in [ns]
254  laserHitSelector.param('windowSelect', windowSelect)
255  laserHitSelector.param('includePrimaryChargeShare', includePrimaryChargeShare)
256  laserHitSelector.param('includeAllChargeShare', includeAllChargeShare)
257  # laserHitSelector.param('timeHistogramBinning', [100,-150,-50]) # number of bins, lower limit, upper limit
258  first.add_module(laserHitSelector)
259 
260  # Print progress
261  progress = b2.register_module('Progress')
262  first.add_module(progress)
263 
264  # Process events
265  b2.process(first)
266 
267 if not skipSecond:
268  # second process
269  print("start the second process...")
270  second = b2.create_path()
271 
272  eventinfosetter = b2.register_module('EventInfoSetter')
273  eventinfosetter.param('evtNumList', [1])
274  second.add_module(eventinfosetter)
275 
276  # HistoManager
277  histoman2 = b2.register_module('HistoManager')
278  histoman2.param('histoFileName', outputRoot)
279  second.add_module(histoman2)
280 
281  analysis = b2.register_module('TOPGainEfficiencyCalculator')
282  analysis.param('inputFile', interimRoot)
283  analysis.param('outputPDFFile', outputPDF)
284  analysis.param('targetSlotId', slotId)
285  analysis.param('targetPmtId', pmtId)
286  analysis.param('hvDiff', HVHigherSetting - HVLowerSetting)
287  analysis.param('threshold', threshold)
288  analysis.param('fitoption', fitOption)
289  second.add_module(analysis)
290 
291  b2.process(second)
292 
293 # Print statistics
294 print(b2.statistics)