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