Belle II Software development
caf_cdc.py
1# disable doxygen check for this file
2# @cond
3
4
11
12"""Full CDC tracking calibration."""
13from prompt import CalibrationSettings, INPUT_DATA_FILTERS
14from prompt.utils import events_in_basf2_file, ExpRun
15import basf2
16from random import choice
17from caf.framework import Calibration
18from caf import strategies
19
20
21settings = CalibrationSettings(name="CDC Tracking",
22 expert_username="farhoumh",
23 subsystem="cdc",
24 description=__doc__,
25 input_data_formats=["raw"],
26 input_data_names=["mumu_tight_or_highm_calib", "hadron_calib", "cosmic_calib"],
27 input_data_filters={"mumu_tight_or_highm_calib":
28 [INPUT_DATA_FILTERS["Data Tag"]["mumu_tight_or_highm_calib"],
29 INPUT_DATA_FILTERS["Data Quality Tag"]["Good"],
30 INPUT_DATA_FILTERS["Magnet"]["On"]],
31 "hadron_calib":
32 [INPUT_DATA_FILTERS["Data Tag"]["hadron_calib"],
33 INPUT_DATA_FILTERS["Data Quality Tag"]["Good"],
34 INPUT_DATA_FILTERS["Magnet"]["On"]],
35 "cosmic_calib":
36 [INPUT_DATA_FILTERS["Data Tag"]["cosmic_calib"],
37 INPUT_DATA_FILTERS["Data Quality Tag"]["Good"],
38 INPUT_DATA_FILTERS["Magnet"]["On"]]},
39 depends_on=[],
40 expert_config={
41 "min_events_per_file": 500,
42 "max_events_per_file": 20000,
43 "max_events_per_file_hadron_for_tz_tw": 5000,
44 "max_events_per_file_hadron_for_xt_sr": 12000,
45 "min_events_for_tz_tw_calibration": 500000,
46 "max_events_for_tz_tw_calibration": 15000000,
47 "min_events_for_xt_sr_calibration": 1000000, # 1M
48 "max_events_for_xt_sr_calibration": 10000000, # 10M
49 "fractions_for_each_type": [0.5, 1, 0.5], # [mumu, hadron, cosmic]
50 "max_job_for_each_type": [400, 700, 400],
51 "calib_mode": "quick", # manual or predefined: quick, full
52 "calibration_procedure": {"tz0": 1, "xt0": 0, "sr_tz0": 0, "tz2": 0},
53 "payload_boundaries": [],
54 "backend_args": {"request_memory": "4 GB"},
55 "physics_mode": "yes"},
56 produced_payloads=["CDCTimeZeros", "CDCTimeWalks", "CDCXtRelations", "CDCSpaceResols"])
57
58
59def select_files(all_input_files, min_events, max_events, max_processed_events_per_file, max_job=800, min_events_per_file=500):
60 basf2.B2INFO(f"Minimum number of events: {min_events}")
61 basf2.B2INFO(f"Maximum number of events: {max_events}")
62 basf2.B2INFO(f"Conditions: ({min_events_per_file} < #Event/file < {max_processed_events_per_file}) and max_job = {max_job}")
63 # Let's iterate, taking a sample of files from the total (no repeats or replacement) until we get enough events
64 total_events = 0
65 chosen_files = []
66 njob = 0
67 while total_events < max_events and njob < max_job:
68 # If the set is empty we must have used all available files. Here we break and continue. But you may want to
69 # raise an Error...
70 if not all_input_files:
71 break
72 # Randomly select a file
73 new_file_choice = choice(all_input_files)
74 # Remove it from the list so it can't be chosen again
75 all_input_files.remove(new_file_choice)
76 # Find the number of events in the file
77 total_events_in_file = events_in_basf2_file(new_file_choice)
78 if not total_events_in_file or total_events_in_file < min_events_per_file:
79 # Uh Oh! Zero event file, skip it
80 continue
81 events_contributed = 0
82 if total_events_in_file < max_processed_events_per_file:
83 # The file contains less than the max amount we have set (entrySequences)
84 events_contributed = total_events_in_file
85 else:
86 events_contributed = max_processed_events_per_file
87 chosen_files.append(new_file_choice)
88 total_events += events_contributed
89 njob += 1
90
91 basf2.B2INFO(f"Total chosen files = {len(chosen_files)}")
92 basf2.B2INFO(f"Total events in chosen files = {total_events}")
93 if total_events < min_events:
94 basf2.B2FATAL(
95 f"There is not enough required events with setup max_processed_events_per_file = {max_processed_events_per_file}")
96 return chosen_files
97
98
99
102
103
104def get_calibrations(input_data, **kwargs):
105 import basf2
106 # from prompt.utils import filter_by_max_files_per_run
107 # read expert_config values
108 expert_config = kwargs.get("expert_config")
109 calib_mode = expert_config["calib_mode"]
110 physics_mode = expert_config["physics_mode"]
111 # max_files_per_run = expert_config["max_files_per_run"]
112 min_events_per_file = expert_config["min_events_per_file"]
113 max_events_per_file = expert_config["max_events_per_file"]
114
115 min_events_for_tz_tw = expert_config["min_events_for_tz_tw_calibration"] # for t0, tw calib.
116 max_events_for_tz_tw = expert_config["max_events_for_tz_tw_calibration"] # for t0, tw calib.
117 max_events_per_file_hadron_for_tz_tw = expert_config["max_events_per_file_hadron_for_tz_tw"]
118
119 min_events_for_xt_sr = expert_config["min_events_for_xt_sr_calibration"] # for xt, sr calib.
120 max_events_for_xt_sr = expert_config["max_events_for_xt_sr_calibration"] # for xt, sr calib.
121 max_events_per_file_hadron_for_xt_sr = expert_config["max_events_per_file_hadron_for_xt_sr"]
122
123 Max_events_per_file_for_tz_tw = [max_events_per_file, max_events_per_file_hadron_for_tz_tw, max_events_per_file]
124 Max_events_per_file_for_xt_sr = [max_events_per_file, max_events_per_file_hadron_for_xt_sr, max_events_per_file]
125
126 fraction_of_event_for_types = expert_config["fractions_for_each_type"]
127 max_jobs = expert_config["max_job_for_each_type"]
128 basf2.B2INFO(f"Number of job for each type are limited at [di-muon, hadron, cosmic]: {max_jobs}")
129 basf2.B2INFO(f"Fraction for [di-muon, hadron, cosmic]: {fraction_of_event_for_types}")
130 if len(fraction_of_event_for_types) != 3:
131 basf2.B2FATAL("fraction of event must be an array with the size of 3, with order [mumu, hadron, cosmic]")
132
133 payload_boundaries = []
134 payload_boundaries.extend([ExpRun(*boundary) for boundary in expert_config["payload_boundaries"]])
135 basf2.B2INFO(f"Payload boundaries from expert_config: {payload_boundaries}")
136
137 files_for_xt_sr_dict = {}
138 files_for_tz_tw_dict = {}
139
140 # select data file for t0 and tw calibration
141 if fraction_of_event_for_types[1] > 0:
142 basf2.B2INFO("*********************** Select Hadron data for calibration ****************")
143 min_hadron_events_for_tz_tw = fraction_of_event_for_types[1] * min_events_for_tz_tw
144 max_hadron_events_for_tz_tw = fraction_of_event_for_types[1] * max_events_for_tz_tw
145 min_hadron_events_for_xt_sr = fraction_of_event_for_types[1] * min_events_for_xt_sr
146 max_hadron_events_for_xt_sr = fraction_of_event_for_types[1] * max_events_for_xt_sr
147
148 file_to_iov_hadron = input_data["hadron_calib"]
149 # select data file for tw, t0
150 basf2.B2INFO("----> Hadron for T0 and Time walk correction")
151 chosen_files_hadron_for_tz_tw = select_files(list(file_to_iov_hadron.keys()),
152 min_hadron_events_for_tz_tw,
153 max_hadron_events_for_tz_tw,
154 max_events_per_file_hadron_for_tz_tw,
155 max_jobs[1],
156 min_events_per_file)
157 # select data file for xt, sigma
158 basf2.B2INFO("----> Hadron for XT, space resolution calib")
159 chosen_files_hadron_for_xt_sr = select_files(list(file_to_iov_hadron.keys()),
160 min_hadron_events_for_xt_sr,
161 max_hadron_events_for_xt_sr,
162 max_events_per_file_hadron_for_xt_sr,
163 max_jobs[1],
164 min_events_per_file)
165
166 files_for_xt_sr_dict["hadron_calib"] = chosen_files_hadron_for_xt_sr
167 files_for_tz_tw_dict["hadron_calib"] = chosen_files_hadron_for_tz_tw
168
169 if fraction_of_event_for_types[0] > 0:
170 basf2.B2INFO("***********************Select di-muon data for calibration ***************")
171 min_mumu_events_for_xt_sr = fraction_of_event_for_types[0] * min_events_for_xt_sr
172 max_mumu_events_for_xt_sr = fraction_of_event_for_types[0] * max_events_for_xt_sr
173 min_mumu_events_for_tz_tw = fraction_of_event_for_types[0] * min_events_for_tz_tw
174 max_mumu_events_for_tz_tw = fraction_of_event_for_types[0] * max_events_for_tz_tw
175 file_to_iov_mumu = input_data["mumu_tight_or_highm_calib"]
176 basf2.B2INFO("----> Mumu for T0 and Time walk correction")
177 chosen_files_mumu_for_tz_tw = select_files(list(file_to_iov_mumu.keys()),
178 min_mumu_events_for_tz_tw,
179 max_mumu_events_for_tz_tw,
180 max_events_per_file,
181 max_jobs[0],
182 min_events_per_file)
183
184 # select data file for xt, sigma calibration
185 basf2.B2INFO("----> Mumu for XT, space resolution calib")
186
187 chosen_files_mumu_for_xt_sr = select_files(list(file_to_iov_mumu.keys()), # input_files_mumu[:],
188 min_mumu_events_for_xt_sr,
189 max_mumu_events_for_xt_sr,
190 max_events_per_file,
191 max_jobs[0],
192 min_events_per_file)
193
194 files_for_xt_sr_dict["mumu_tight_or_highm_calib"] = chosen_files_mumu_for_xt_sr
195 files_for_tz_tw_dict["mumu_tight_or_highm_calib"] = chosen_files_mumu_for_tz_tw
196
197 ''' For cosmic data '''
198 if fraction_of_event_for_types[2] > 0:
199 basf2.B2INFO("********************* Select cosmic data for calibration *******************")
200 min_cosmic_events_for_tz_tw = fraction_of_event_for_types[2] * min_events_for_tz_tw
201 max_cosmic_events_for_tz_tw = fraction_of_event_for_types[2] * max_events_for_tz_tw
202 min_cosmic_events_for_xt_sr = fraction_of_event_for_types[2] * min_events_for_xt_sr
203 max_cosmic_events_for_xt_sr = fraction_of_event_for_types[2] * max_events_for_xt_sr
204
205 file_to_iov_cosmic = input_data["cosmic_calib"]
206
207 # Select cosmic data for tw and t0 calibration
208 basf2.B2INFO("----> Cosmic for T0 and Time walk correction")
209 if physics_mode == "no":
210 basf2.B2INFO("---->In non-physics mode, still use cosmic for t0 calibration")
211 chosen_files_cosmic_for_tz_tw = select_files(list(file_to_iov_cosmic.keys()),
212 min_cosmic_events_for_tz_tw,
213 max_cosmic_events_for_tz_tw,
214 max_events_per_file,
215 max_jobs[2],
216 min_events_per_file)
217 else:
218 basf2.B2INFO("---->In physics mode, do not use cosmic for t0 calibration")
219 chosen_files_cosmic_for_tz_tw = {}
220
221 # select cosmics data for xt and sigma calibration
222 basf2.B2INFO("----> Cosmic for XT, space resolution calib")
223 chosen_files_cosmic_for_xt_sr = select_files(list(file_to_iov_cosmic.keys()),
224 min_cosmic_events_for_xt_sr,
225 max_cosmic_events_for_xt_sr,
226 max_events_per_file,
227 max_jobs[2],
228 min_events_per_file)
229 files_for_xt_sr_dict["cosmic_calib"] = chosen_files_cosmic_for_xt_sr
230 files_for_tz_tw_dict["cosmic_calib"] = chosen_files_cosmic_for_tz_tw
231
232 basf2.B2INFO("Complete input data selection.")
233
234 # Get the overall IoV we want to cover, including the end values
235 requested_iov = kwargs.get("requested_iov", None)
236
237 from caf.utils import IoV
238 # The actual IoV we want for any prompt request is open-ended
239 output_iov = IoV(requested_iov.exp_low, requested_iov.run_low, -1, -1)
240
241 # for SingleIOV stratrgy, it's better to set the granularity to 'all' so that the collector jobs will run faster
242 collector_granularity = 'all'
243 if payload_boundaries:
244 basf2.B2INFO('Found payload_boundaries: set collector granularity to run')
245 collector_granularity = 'run'
246 if calib_mode == "full":
247 calibration_procedure = {
248 "tz0": 4,
249 "xt0": 0,
250 "sr_tz0": 0,
251 "xt1": 0,
252 "sr_tz1": 0,
253 "tw0": 1,
254 "tz1": 1,
255 "xt2": 0,
256 "sr_tz2": 0,
257 "tz2": 0
258 }
259 elif calib_mode == "quick":
260 calibration_procedure = {
261 "tz0": 3,
262 "xt0": 0,
263 "sr_tz0": 0,
264 "tw0": 1,
265 "tz1": 1,
266 "xt1": 0,
267 "sr_tz1": 0,
268 "tz2": 0
269 }
270 elif calib_mode == "manual":
271 calibration_procedure = expert_config["calibration_procedure"]
272 else:
273 basf2.B2FATAL(f"Calibration mode is not defined {calib_mode}, should be quick, full, or manual")
274 # t0
275 calib_keys = list(calibration_procedure)
276 cals = [None]*len(calib_keys)
277 basf2.B2INFO(f"Run calibration mode = {calib_mode}:")
278 print(calib_keys)
279 for i in range(len(cals)):
280 max_iter = calibration_procedure[calib_keys[i]]
281 alg = None
282 data_files = None
283 cal_name = ''.join([i for i in calib_keys[i] if not i.isdigit()])
284 if cal_name == "tz":
285 alg = [tz_algo()]
286 elif cal_name == "tw":
287 alg = [tw_algo()]
288 elif cal_name == "xt":
289 alg = [xt_algo()]
290 elif cal_name == "sr_tz":
291 alg = [sr_algo(), tz_algo()]
292 else:
293 basf2.B2FATAL(f"The calibration is not defined, check spelling: calib {i}: {calib_keys[i]}")
294
295 if cal_name == "xt" or cal_name == "sr_tz":
296 max_event = Max_events_per_file_for_xt_sr
297 data_files = files_for_xt_sr_dict
298 else:
299 max_event = Max_events_per_file_for_tz_tw
300 data_files = files_for_tz_tw_dict
301 basf2.B2INFO(f"calibration for {calib_keys[i]} with number of iteration={max_iter}")
302 cals[i] = CDCCalibration(name=calib_keys[i],
303 algorithms=alg,
304 input_file_dict=data_files,
305 max_iterations=max_iter,
306 max_events=max_event,
307 use_badWires=True if calib_keys[i] == "tz" else False,
308 collector_granularity=collector_granularity,
309 backend_args=expert_config["backend_args"],
310 dependencies=[cals[i-1]] if i > 0 else None
311 )
312 if payload_boundaries:
313 basf2.B2INFO("Found payload_boundaries: calibration strategies set to SequentialBoundaries.")
314 for i in range(len(cals)):
315 cals[i].strategies = strategies.SequentialBoundaries
316 for alg in cals:
317 for algorithm in alg.algorithms:
318 algorithm.params = {"iov_coverage": output_iov, "payload_boundaries": payload_boundaries}
319
320 else:
321 for alg in cals:
322 for algorithm in alg.algorithms:
323 algorithm.params = {"apply_iov": output_iov}
324
325 return cals
326
327
328
329def pre_collector(max_events=None, is_cosmic=False, use_badWires=False):
330 """
331 Define pre collection (reconstruction in our purpose).
332 Probably, we need only CDC and ECL data.
333 Parameters:
334 max_events [int] : number of events to be processed.
335 All events by Default.
336 Returns:
337 path : path for pre collection
338 """
339 from basf2 import create_path, register_module
340 from softwaretrigger.constants import HLT_INPUT_OBJECTS
341 reco_path = create_path()
342 if max_events is None:
343 root_input = register_module(
344 'RootInput',
345 branchNames=HLT_INPUT_OBJECTS
346 )
347 else:
348 root_input = register_module(
349 'RootInput',
350 branchNames=HLT_INPUT_OBJECTS,
351 entrySequences=[
352 f'0:{max_events}'])
353 reco_path.add_module(root_input)
354
355 gearbox = register_module('Gearbox')
356 reco_path.add_module(gearbox)
357 reco_path.add_module('Geometry', useDB=True)
358 Components = ['CDC', 'ECL']
359
360 from rawdata import add_unpackers
361 add_unpackers(reco_path, components=Components)
362
363 if is_cosmic:
364 from reconstruction import add_cosmics_reconstruction
365 # Add cdc tracking reconstruction modules
366 add_cosmics_reconstruction(path=reco_path,
367 components=Components,
368 pruneTracks=False,
369 merge_tracks=False,
370 posttracking=False)
371 else:
372 from reconstruction import add_prefilter_pretracking_reconstruction
373 from tracking import add_prefilter_tracking_reconstruction
374 from softwaretrigger.path_utils import add_prefilter_module
375
376 # Add HLTPrefilter module to the path.
377 add_prefilter_module(reco_path)
378
379 # Add modules that have to be run BEFORE track reconstruction
380 add_prefilter_pretracking_reconstruction(reco_path, components=Components)
381
382 # Add tracking reconstruction modules
383 add_prefilter_tracking_reconstruction(path=reco_path,
384 components=Components,
385 trackFitHypotheses=[211],
386 prune_temporary_tracks=False,
387 fit_tracks=True,
388 append_full_grid_cdc_eventt0=True,
389 skip_full_grid_cdc_eventt0_if_svd_time_present=False)
390 reco_path.add_module('StatisticsSummary').set_name('Sum_Tracking')
391
392 reco_path.add_module('Progress')
393
394 for module in reco_path.modules():
395 if module.name() == "TFCDC_WireHitPreparer":
396 module.param({"useBadWires": use_badWires})
397
398 return reco_path
399
400
401def collector(is_cosmic=False, granularity='all'):
402 """
403 Create a cdc calibration collector
404 Parameters:
405 bField [bool] : True if B field is on, else False
406 isCosmic [bool] : True if cosmic events,
407 else (collision) False.
408 Returns:
409 collector : collector module
410 """
411 from basf2 import register_module
412 col = register_module('CDCCalibrationCollector',
413 granularity=granularity,
414 calExpectedDriftTime=True,
415 eventT0Extraction=True,
416 isCosmic=is_cosmic
417 )
418 return col
419
420
421def tz_algo(max_rmsDt=0.25, max_badChannel=50):
422 """
423 Create a T0 calibration algorithm.
424 Returns:
425 algo : T0 algorithm
426 """
427 from ROOT import Belle2
429 algo.storeHisto(True)
430 algo.setMaxMeanDt(0.2)
431 algo.setMaxRMSDt(max_rmsDt)
432 algo.setMaxBadChannel(max_badChannel)
433 algo.setMinimumNDF(25)
434
435 return algo
436
437
438def tw_algo():
439 """
440 Create a time walk calibration algorithm.
441 Returns:
442 algo : TW algorithm
443 """
444 from ROOT import Belle2
446 algo.setStoreHisto(True)
447 algo.setMode(1)
448 return algo
449
450
451def xt_algo():
452 """
453 Create a XT calibration algorithm.
454 Parameters:
455 prefix : prefixed name for algorithm,
456 which should be consistent with one of collector..
457 Returns:
458 algo : XT algorithm
459 """
460 from ROOT import Belle2
462 algo.setStoreHisto(True)
463 algo.setLRSeparate(True)
464 algo.setThreshold(0.1)
465 return algo
466
467
468def sr_algo():
469 """
470 Create a Spacial resolution calibration algorithm.
471 Parameters:
472 prefix : prefixed name for algorithm,
473 which should be consistent with one of collector..
474 Returns:
475 algo : Spacial algorithm
476 """
477 from ROOT import Belle2
479 algo.setStoreHisto(True)
480 algo.setThreshold(0.1)
481 return algo
482
483
484class CDCCalibration(Calibration):
485 '''
486 CDCCalibration is a specialized calibration class for cdc.
487 Since collector is same in all elements, no need to specify it.
488 '''
489
490 def __init__(self,
491 name,
492 algorithms,
493 input_file_dict,
494 max_iterations=5,
495 dependencies=None,
496 max_events=[20000, 10000, 20000],
497 use_badWires=False,
498 collector_granularity='All',
499 backend_args=None):
500 """
501 """
502 for algo in algorithms:
503 algo.setHistFileName(name)
504
505 super().__init__(name=name,
506 algorithms=algorithms
507 )
508
509 from caf.framework import Collection
510
511 for skim_type, file_list in input_file_dict.items():
512 if skim_type == "cosmic_calib":
513 collection = Collection(collector=collector(is_cosmic=True,
514 granularity=collector_granularity),
515 input_files=file_list,
516 pre_collector_path=pre_collector(max_events=max_events[2],
517 is_cosmic=True,
518 use_badWires=use_badWires),
519 backend_args=backend_args)
520 elif skim_type == "hadron_calib":
521 collection = Collection(collector=collector(granularity=collector_granularity),
522 input_files=file_list,
523 pre_collector_path=pre_collector(max_events=max_events[1],
524 use_badWires=use_badWires),
525 backend_args=backend_args)
526 else:
527 collection = Collection(collector=collector(granularity=collector_granularity),
528 input_files=file_list,
529 pre_collector_path=pre_collector(max_events=max_events[0],
530 use_badWires=use_badWires),
531 backend_args=backend_args)
532
533 self.add_collection(name=skim_type, collection=collection)
534
535
536 self.max_iterations = max_iterations
537
538 if dependencies is not None:
539 for dep in dependencies:
540 self.depends_on(dep)
541
542# @endcond
Class to perform xt calibration for drift chamber.