Belle II Software  release-06-01-15
makeTOPDigitNtuple.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 # ---------------------------------------------------------------------------------------
13 # Unpack raw data with InterimFE and production FW to TOPDigits and convert into ntuple
14 # Usage: basf2 makeTOPDigitNtuple.py <input_file.sroot>
15 # ---------------------------------------------------------------------------------------
16 
17 import basf2 as b2
18 import sys
19 import argparse
20 import re
21 from ROOT import TMath
22 from plotInterimFEDataNtuple import plotInterimFEDataNtupleSummary
23 
24 parser = argparse.ArgumentParser(description="Create ntuple file and plots from interimFE data")
25 parser.add_argument("inputFile", nargs='?', default="NoInputFile",
26  help="input sroot file name")
27 parser.add_argument("--outputFile", default="NoOutputFile",
28  help="output root file name")
29 parser.add_argument("--calChannel", type=int, default=0,
30  help="asic channel number where calibration signals are injected [0-7]")
31 parser.add_argument(
32  "--lookbackWindows",
33  type=int,
34  default=0,
35  help="lookback windows to redefine rawTime so that t=0 is beginning of search windows."
36  " Give 0 (default) not to allow the redefinition.")
37 parser.add_argument("--heightThreshold", type=int, default=40,
38  help="pulse height threshold in offline feature extraction in a unit of ADC counts")
39 parser.add_argument("--noOfflineFE", action="store_true", default=False,
40  help="Use only online FE hits, and do not use waveform data")
41 parser.add_argument("--saveWaveform", action="store_true", default=False,
42  help="Save waveform data in the output ntuple file")
43 parser.add_argument("--globalDAQ", action="store_true", default=False,
44  help="Force to assume global DAQ data.")
45 parser.add_argument("--pocketDAQ", action="store_true", default=False,
46  help="Force to assume global DAQ data.")
47 parser.add_argument("--skipPlot", action="store_true", default=False,
48  help="Skip making summary plot.")
49 parser.add_argument("--interim", action="store_true", default=False,
50  help="process data taken with interimFE FW")
51 args = parser.parse_args()
52 
53 if args.inputFile == "NoInputFile":
54  print("Create a flat ntuple file from sroot data file(s) taken with interimFE firmware and plots for quick data quality check.")
55  print("usage:")
56  print("basf2 makeTOPDigitNtuple.py (input_filename.sroot) [--arg --outputFile output_ntuple.root]")
57  print(" [--arg --calChannel asicCh]")
58  print(" [--arg --lookbackWindows windows]")
59  print(" [--arg --heightThreshold threshold]")
60  print(" [--arg --noOfflineFE] [--arg --saveWaveform]")
61  print(" [--arg --globalDAQ] [--arg --pocketDAQ]")
62  print(" [--arg --skipPlot] [--arg --interim]")
63  print("*Switching of local/global run and output file name is automatically given as folows if it is not specified:")
64  print(" runXXXXXX_slotYY_ntuple.root (local run with PocketDAQ)")
65  print(" or top.XXXXX.YYYYYY_ntuple.root (global run with global DAQ)")
66  print("*Deafult asic channel number with calibration signals is 0 (can be changed with \"--calChannel\" option.)")
67  print("*Deafult the number of lookback windows is 0, with which timing correction is not applied.")
68  print(" (rawTime is corrected so that it is measured with respect to the start of search windows when lookbackWindows>0.)")
69  print("*Option \"--noOfflineFE\" : disable offline FE from waveform data.")
70  print(" Calculation of reference timing is based on hit in calibration channel.")
71  print("* \"--saveWaveform\" : save waveform data in the output ntuple file")
72  print("* \"--globalDAQ\" : force to assume global DAQ data as an input file")
73  print("* \"--pocketDAQ\" : force to assume pocketDAQ data as an input file")
74  print("* \"--skipPlot\" : only processing sroot data file, do not create summary plot")
75  print("* \"--interim\" : process data taken with interimFE FW")
76  sys.exit()
77 
78 inputFile = args.inputFile
79 isGlobalDAQ = False
80 isOfflineFEDisabled = args.noOfflineFE
81 isGlobalDAQForced = args.globalDAQ
82 isPocketDAQForced = args.pocketDAQ
83 calCh = args.calChannel
84 lookbackWindows = args.lookbackWindows
85 heightThreshold = args.heightThreshold
86 isInterimFE = args.interim
87 if calCh < 0 or calCh > 7:
88  print("ERROR : invalid calibration asic channel :" + str(calCh))
89  print(" (should be [0-7])")
90  sys.exit()
91 
92 if re.search(r"run[0-9]+_slot[0-1][0-9]", inputFile):
93  outputRoot = re.search(r"run[0-9]+_slot[0-1][0-9]", inputFile).group() + "_ntuple.root"
94 elif re.search(r"(top|cosmic|cdc|ecl|klm|test|debug|beam|physics|hlttest)\.[0-9]+\.[0-9]+", inputFile):
95  isGlobalDAQ = True
96  outputRoot = re.search(
97  r"(top|cosmic|cdc|ecl|klm|test|debug|beam|physics|hlttest)\.[0-9]+\.[0-9]+",
98  inputFile).group() + "_ntuple.root"
99 else:
100  outputRoot = inputFile + "_ntuple.root"
101 
102 if args.outputFile != "NoOutputFile":
103  outputRoot = args.outputFile
104 
105 if isGlobalDAQForced and (not isPocketDAQForced):
106  isGlobalDAQ = True
107 elif (not isGlobalDAQForced) and isPocketDAQForced:
108  isGlobalDAQ = False
109 elif isGlobalDAQForced and isPocketDAQForced:
110  print("ERROR : both of --GlobalDAQ or --PocketDAQ can not be given.")
111  sys.exit()
112 
113 print(inputFile + " --> " + outputRoot)
114 print("Is global DAQ? : " + str(isGlobalDAQ))
115 print("OfflineFE : " + str(not isOfflineFEDisabled))
116 print("Save waveform? : " + str(args.saveWaveform))
117 print("Cal. asic ch : " + str(calCh))
118 print("# of lookback windows : " + str(lookbackWindows))
119 print()
120 print("start process...")
121 
122 # Define a global tag (note: the one given bellow will become out-dated!)
123 b2.use_central_database('data_reprocessing_proc8')
124 
125 # Create path
126 main = b2.create_path()
127 
128 roinput = b2.register_module('SeqRootInput')
129 roinput.param('inputFileName', inputFile)
130 main.add_module(roinput)
131 
132 # HistoManager
133 histoman = b2.register_module('HistoManager')
134 histoman.param('histoFileName', outputRoot)
135 main.add_module(histoman)
136 
137 # conversion from RawCOPPER or RawDataBlock to RawDetector objects
138 if not isGlobalDAQ:
139  converter = b2.register_module('Convert2RawDet')
140  main.add_module(converter)
141 
142 # Initialize TOP geometry parameters (creation of Geant geometry is not needed)
143 main.add_module('TOPGeometryParInitializer')
144 
145 # Unpacking (format auto detection works now)
146 unpack = b2.register_module('TOPUnpacker')
147 if isInterimFE: # need to be tested
148  unpack.param('swapBytes', True)
149  unpack.param('dataFormat', 0x0301)
150 main.add_module(unpack)
151 
152 # Add multiple hits by running feature extraction offline
153 if not isOfflineFEDisabled:
154  featureExtractor = b2.register_module('TOPWaveformFeatureExtractor')
155  featureExtractor.param('threshold', heightThreshold)
156  featureExtractor.param('hysteresis', TMath.CeilNint(heightThreshold * 0.4 - 0.00001))
157  main.add_module(featureExtractor)
158 
159 # Convert to TOPDigits
160 converter = b2.register_module('TOPRawDigitConverter')
161 converter.param('useSampleTimeCalibration', False)
162 converter.param('useChannelT0Calibration', False)
163 converter.param('useModuleT0Calibration', False)
164 converter.param('useCommonT0Calibration', False)
165 converter.param('lookBackWindows', lookbackWindows)
166 converter.param('storageDepth', 508)
167 converter.param('calibrationChannel', calCh) # if set, cal pulses will be flagged
168 # converter.param('calpulseHeightMin', 450) # in [ADC counts]
169 converter.param('calpulseHeightMin', 300) # in [ADC counts]
170 converter.param('calpulseHeightMax', 900) # in [ADC counts]
171 converter.param('calpulseWidthMin', 1.2) # in [ns]
172 converter.param('calpulseWidthMax', 2.8) # in [ns]
173 main.add_module(converter)
174 
175 xtalk = b2.register_module('TOPXTalkChargeShareSetter')
176 main.add_module(xtalk)
177 
178 ntuple = b2.register_module('TOPInterimFENtuple')
179 ntuple.param('saveWaveform', (args.saveWaveform))
180 if isInterimFE:
181  ntuple.param('useDoublePulse', (not isOfflineFEDisabled))
182 ntuple.param('calibrationChannel', calCh)
183 # ntuple.param('minHeightFirstCalPulse', 450) # in [ADC counts]
184 # ntuple.param('minHeightSecondCalPulse', 450) # in [ADC counts]
185 ntuple.param('minHeightFirstCalPulse', 300) # in [ADC counts]
186 ntuple.param('minHeightSecondCalPulse', 300) # in [ADC counts]
187 ntuple.param('nominalDeltaT', 21.5) # in [ns]
188 ntuple.param('nominalDeltaTRange', 2) # in [ns]
189 ntuple.param('globalRefSlotNum', 1)
190 ntuple.param('globalRefAsicNum', 0)
191 ntuple.param('timePerWin', 23.581939) # in [ns]
192 main.add_module(ntuple)
193 
194 # Print progress
195 progress = b2.register_module('Progress')
196 main.add_module(progress)
197 
198 # Process events
199 b2.process(main)
200 
201 # Print statistics
202 print(b2.statistics)
203 
204 if isInterimFE and not args.skipPlot:
205  plotInterimFEDataNtupleSummary(outputRoot, 2, isOfflineFEDisabled)