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