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