Belle II Software development
caf_pxd_datamcgain.py
1
8
9# This steering file computes PXD calibrations for energy loss (gain). The script uses the CAF framework.
10# This script uses by default the SequentialRunByRun strategy and is designed to try to compute new calibration
11# constants regularly to follow temporal changes of the PXD.
12#
13# There are options to:
14# * obtain MC charge (typically done only once per given geometry & beam condition
15# * obtain Data charge (for studies and cross checks)
16# * derive gain calibration constants (Data/MC ratio) from Data charge computed on-the-fly and
17# MC charge from existing DB
18#
19# (0) Prerequisite
20#
21# * Hot pixel masks should be available for the given iov range, either in GT or in the specified localDB.
22#
23# (1) Preparation for Data
24#
25# The script allows you to create a list of runs that will be ignored in the calibration.
26# Use this list for known bad runs.
27# By default, "RunListCosmics" and "RunListBeam" files on KEKCC are read in to skip these runs.
28# (Best to avoid these runs for calibration unless there exist corresponding MC samples to model them.)
29#
30# Before calibration, you have to put a file to IoV mapping for Data called 'file_iov_map.pkl' in your
31# working directory. The mapping file should map file locations to IoV ranges and allows to
32# logically address data by experiment and run numbers. You can create such a map file by using
33# the tool b2caf-filemap. See also the option --help.
34#
35# b2caf-filemap -m raw -p "/hsm/belle2/bdata/Data/Raw/e0008/r*/**/*.root"
36#
37# (2) Preparation for MC
38#
39# The next step is to prepare mc runs containing PXDSimHits. That setup for the simulation
40# should mimic the situation of beam data as closely as possible.
41# In this version of calibration, mc file is created only once for the entire iov range.
42#
43# basf2 submit_create_mcruns.py -- --backend='local' --outputdir='pxd_mc_phase3' --runLow=0 --runHigh=0 --expNo=7
44#
45# The scripts submits the creation of mc runs to a CAF.backend (here local) for all for given run range. Runs in the
46# specified runs but not found in 'file_iov_map.pkl' will be skipped. The simulated runs will be collected in the folder
47# outputdir. It is best to create another mapping file for the mc data.
48#
49# b2caf-filemap -m metadata -p --output-file "dummy_mc_file_iov_map.pkl" "pxd_mc_phase3/*.root"
50#
51# (3) Run charge calibration (measurement)
52#
53# A stand-alone process to collect charge and creating Cluster Charge payloads for MC is needed before
54# running gain calibration on data. This needs to be done once for a given sample and the DB listing has to be
55# modified by hand to make it valid for the calibration range for data.
56#
57# basf2 caf_pxd_datamcgain.py -- --runLow=0 --runHigh=0 --expNo=7 --mcOnly
58#
59# Check the output in a folder 'pxd_calibration_results_e7_range_0_0' and modify outputdb/database.txt
60# such that the iov covers the entire range, e.g. 7 0 -1 -1
61#
62# One can run the above script with --dataOnly to get Cluster Charge payloads for data without computing gain calibration.
63#
64# (4) Run gain calibration
65#
66# Finally, a CAF script for the calibration needs to be started:
67#
68# basf2 caf_pxd_datamcgain.py -- --runLow=4120 --runHigh=4120 --expNo=7 /
69# --localDB="/hsm/belle2/bdata/group/detector/PXD/calibration/mcdb_median/database.txt"
70#
71# The results will be collected in a folder 'pxd_calibration_results_eE_range_XY'. In order to complete the
72# process, the check and uploads the outputdbs to a global tag (GT).
73#
74# b2conditionsdb upload pxd_calibration ./database.txt
75#
76# The option --help provides extensive help for the b2conditionsdb tool.
77
78
79import argparse
80from tracking import add_tracking_reconstruction
81from rawdata import add_unpackers
82from caf.strategies import SequentialRunByRun
83from caf.utils import CentralDatabase
84from caf.utils import LocalDatabase
85from caf.utils import ExpRun, IoV
86from caf.backends import LSF
87from caf.framework import Calibration, CAF
88import ROOT # noqa (must be before 'from ROOT.Belle2' to load ROOT.Belle2 namespace)
89from ROOT.Belle2 import PXDDataMCGainCalibrationAlgorithm
90import pickle
91import basf2 as b2
92b2.set_log_level(b2.LogLevel.INFO)
93
94
95parser = argparse.ArgumentParser(
96 description="Compute gain correction maps for PXD from beam data")
97parser.add_argument(
98 '--runLow',
99 default=0,
100 type=int,
101 help='Compute mask for specific IoV')
102parser.add_argument('--runHigh', default=-1, type=int,
103 help='Compute mask for specific IoV')
104parser.add_argument(
105 '--expNo',
106 default=3,
107 type=int,
108 help='Compute mask for specific IoV')
109parser.add_argument('--maxSubRuns', default=-1, type=int,
110 help='Maximum number of subruns to use')
111parser.add_argument('--localDB', default="", type=str,
112 help='path to local DB database.txt')
113parser.add_argument(
114 '--ignoreRuns',
115 default="DefaultFromKEKCC",
116 type=str,
117 help='Full paths to list of runs to ignore, separate multiple by a comma , ')
118parser.add_argument(
119 '--mcOnly',
120 dest='mcOnly',
121 action="store_true",
122 help='Run charge calibration for MC only, otherwise specify --dataOnly \
123 or run the full gain calibration on data using MC charge from DB')
124parser.add_argument(
125 '--dataOnly',
126 dest='dataOnly',
127 action="store_true",
128 help='Run charge calibration for Data only, otherwise specify --mcOnly \
129 or run the full gain calibration on data using MC charge from DB')
130parser.add_argument(
131 '--useTrackClusters',
132 default=0,
133 type=int,
134 help='Flag to use track matched clusters (=1) and apply theta angle projection to cluster charge (=2)')
135
136args = parser.parse_args()
137
138ROOT.gROOT.SetBatch(True)
139
140
143
144# Set the IoV range for this calibration
145iov_to_calibrate = IoV(
146 exp_low=args.expNo,
147 run_low=args.runLow,
148 exp_high=args.expNo,
149 run_high=args.runHigh)
150
151input_files = []
152pxd_ignore_run_list = []
153
154if args.mcOnly:
155 # Access files_to_iovs for MC runs
156 with open("mc_file_iov_map.pkl", 'br') as map_file:
157 files_to_iovs = pickle.load(map_file)
158 input_files = list(files_to_iovs.keys())
159 print(f'Number selected mc input files: {len(input_files)}')
160
161else:
162
163 # odd runs to ignore for data
164 pxd_ignore_run_list = [ExpRun(3, 484), ExpRun(3, 485), ExpRun(3, 486), ExpRun(3, 524), # from Phase2
165 ExpRun(
166 7, 1000), # 19 modules excluded, and 1 remaining module with high occupancy
167 # problem processing one file, anyway a short 'debug' beam run
168 ExpRun(8, 106),
169 ExpRun(8, 676), # LER beam lost at early point
170 ]
171
172 # load ignore run list
173 ignoreRuns = args.ignoreRuns
174 if "DefaultFromKEKCC" in ignoreRuns:
175 dirkekcc = f"/hsm/belle2/bdata/Data/Raw/e000{args.expNo}/"
176 ignoreRuns = dirkekcc + "RunListBeam," + dirkekcc + "RunListCosmic"
177 if ignoreRuns:
178 for flist in ignoreRuns.split(","):
179 fignore = open(flist)
180 line = fignore.readline()
181 while line:
182 run = line[0:line.find(',')]
183 pxd_ignore_run_list.append(ExpRun(args.expNo, int(run)))
184 line = fignore.readline()
185 fignore.close()
186
187 print('List of ignored runs')
188 print(pxd_ignore_run_list)
189
190 # Access files_to_iovs for beam runs
191 with open("file_iov_map.pkl", 'br') as map_file:
192 files_to_iovs = pickle.load(map_file)
193
194 input_file_iov_set = set(files_to_iovs.values())
195 for file_iov in input_file_iov_set:
196 if iov_to_calibrate.contains(file_iov):
197 subruns = [k for k, v in files_to_iovs.items() if v == file_iov]
198 input_files.extend(subruns[:args.maxSubRuns])
199
200 print(f'Number selected data input files: {len(input_files)}')
201
202
203
206
207# Charge collector for MC or data
208
209charge_collector = b2.register_module("PXDClusterChargeCollector")
210charge_collector.param("granularity", "run")
211charge_collector.param("minClusterCharge", 8)
212charge_collector.param("minClusterSize", 2)
213charge_collector.param("maxClusterSize", 6)
214charge_collector.param("nBinsU", 4)
215charge_collector.param("nBinsV", 6)
216# For gain calibration, collect charge for data and use MC charge from DB
217if not args.mcOnly and not args.dataOnly:
218 charge_collector.param(
219 "chargePayloadName",
220 "PXDMCClusterChargeMapPar") # MC from DB
221charge_collector.param("fillChargeHistogram", True)
222charge_collector.param("matchTrack", args.useTrackClusters)
223
224# The pre collector path for MC or data
225
226pre_charge_collector_path = b2.create_path()
227pre_charge_collector_path.add_module("Gearbox")
228pre_charge_collector_path.add_module("Geometry")
229if args.mcOnly:
230 # only needed when starting from PXDSimHits
231 pre_charge_collector_path.add_module("PXDDigitizer")
232 if args.useTrackClusters:
233 add_tracking_reconstruction(pre_charge_collector_path)
234 else:
235 pre_charge_collector_path.add_module("PXDClusterizer")
236else:
237 if args.useTrackClusters:
238 add_unpackers(pre_charge_collector_path, ['PXD', 'SVD', 'CDC'])
239 add_tracking_reconstruction(pre_charge_collector_path)
240 else:
241 add_unpackers(pre_charge_collector_path, ['PXD'])
242 pre_charge_collector_path.add_module("PXDClusterizer")
243
244for module in pre_charge_collector_path.modules():
245 if module.name() == 'Geeometry':
246 module.param('excludedComponents', ['ECL', 'KLM', 'TOP'])
247 if module.name() == 'PXDUnpacker':
248 module.param('SuppressErrorMask', 0xffffffff)
249 if module.name() == 'PXDPostErrorChecker':
250 module.param('CriticalErrorMask', 0)
251
252
255
256datamc_algo = PXDDataMCGainCalibrationAlgorithm()
257
258# We can play around with algo parameters
259# Minimum number of collected clusters for estimating gains
260datamc_algo.minClusters = 5000
261# Artificial noise sigma for smearing cluster charge
262datamc_algo.noiseSigma = 0.0
263# Force continue algorithm instead of c_notEnoughData, set True for Cosmics
264datamc_algo.forceContinue = False
265datamc_algo.strategy = 0 # 0: medians, 1: landau fit
266if args.mcOnly or args.dataOnly:
267 # only estimate charge MPV from median or landau fit
268 datamc_algo.doCalibration = False
269 if args.mcOnly:
270 # payload name to store on DB for MC
271 datamc_algo.chargePayloadName = "PXDMCClusterChargeMapPar"
272 if args.dataOnly:
273 # payload name to store on DB for data
274 datamc_algo.chargePayloadName = "PXDClusterChargeMapPar"
275else:
276 # do gain calibration on data against MC from DB
277 datamc_algo.doCalibration = True
278# use histogram rather than tree input to save time
279datamc_algo.useChargeHistogram = True
280# We want to use a specific collector
281datamc_algo.setPrefix("PXDClusterChargeCollector")
282
283# create calibration
284charge_cal = Calibration(
285 name="PXDDataMCGainCalibrationAlgorithm",
286 collector=charge_collector,
287 algorithms=datamc_algo,
288 input_files=input_files,
289 pre_collector_path=pre_charge_collector_path,
290 database_chain=[
291 CentralDatabase("data_reprocessing_prompt"),
292 CentralDatabase("pxd_calibration"),
293 LocalDatabase(
294 args.localDB)])
295
296# Apply the map to this calibration, now the CAF doesn't have to do it
297charge_cal.files_to_iovs = files_to_iovs
298
299# Here we set the AlgorithmStrategy
300charge_cal.strategies = SequentialRunByRun
301charge_cal.max_files_per_collector_job = 1
302
303charge_cal.algorithms[0].params["iov_coverage"] = iov_to_calibrate
304charge_cal.ignored_runs = pxd_ignore_run_list
305
306
309
310# create a CAF instance and add the calibration
311cal_fw = CAF()
312cal_fw.add_calibration(charge_cal)
313cal_fw.backend = LSF() # KEKCC batch
314# cal_fw.backend = backends.Local(max_processes=20) # interactive
315cal_fw.output_dir = f'pxd_calibration_results_e{args.expNo}_range_{args.runLow}_{args.runHigh}'
316cal_fw.run(iov=iov_to_calibrate)