Belle II Software  release-08-01-10
caf_klm_alignment.py
1 
8 
9 """
10 Calibration of KLM alignment. It provides calibration constants for the BKLMAlignment
11 and EKLMAlignment database objects.
12 """
13 
14 import collections
15 
16 import basf2
17 from caf.utils import ExpRun, IoV
18 from prompt import CalibrationSettings, INPUT_DATA_FILTERS
19 from prompt.utils import events_in_basf2_file
20 
21 
28 
29 from prompt.calibrations.vxdcdc_alignment import settings as vxdcdc_alignment
30 
31 
39 settings = CalibrationSettings(name="KLM alignmnent",
40  expert_username="oskin",
41  description=__doc__,
42  input_data_formats=["raw"],
43  input_data_names=["raw_physics", "raw_cosmic"],
44  input_data_filters={
45  'raw_physics': [INPUT_DATA_FILTERS['Run Type']['physics'],
46  INPUT_DATA_FILTERS['Data Tag']['mumu_tight_or_highm_calib'],
47  INPUT_DATA_FILTERS['Data Quality Tag']['Good Or Recoverable']],
48  'raw_cosmic': [INPUT_DATA_FILTERS['Run Type']['physics'],
49  INPUT_DATA_FILTERS['Data Tag']['cosmic_calib'],
50  INPUT_DATA_FILTERS['Data Quality Tag']['Good Or Recoverable']]
51  },
52  depends_on=[vxdcdc_alignment],
53  expert_config={
54  "required_events": 5000000,
55  "required_events_experiment": 500000,
56  "events_per_file": 1000,
57  "millepede_entries": 1000000,
58  "millepede_entries_exp7": 500000
59  })
60 
61 
62 
63 
64 
65 def select_input_files(file_to_iov_physics, file_to_iov_cosmic,
66  reduced_file_to_iov_physics, reduced_file_to_iov_cosmic,
67  required_events, required_events_experiment,
68  events_per_file):
69  """
70  Parameters:
71  files_to_iov_physics (dict): Dictionary {run : IOV} for physics data.
72  files_to_iov_cosmic (dict): Dictionary {run : IOV} for cosmic data.
73  reduced_file_to_iov_physics (dict): Selected physics data.
74  reduced_file_to_iov_cosmic (dict): Selected cosmic data.
75  required_events (int): Required total number of events.
76  required_events_experiment (int): Required number of events
77  per experiment.
78  events_per_file (int): Number of events per file. If non-positive, then
79  the number is not limited.
80  """
81  experiment_numbers = set()
82  run_to_files_physics = collections.defaultdict(list)
83  for input_file, file_iov in file_to_iov_physics.items():
84  run = ExpRun(exp=file_iov.exp_low, run=file_iov.run_low)
85  run_to_files_physics[run].append(input_file)
86  experiment_numbers.add(file_iov.exp_low)
87  run_to_files_cosmic = collections.defaultdict(list)
88  for input_file, file_iov in file_to_iov_cosmic.items():
89  run = ExpRun(exp=file_iov.exp_low, run=file_iov.run_low)
90  run_to_files_cosmic[run].append(input_file)
91  experiment_numbers.add(file_iov.exp_low)
92  max_files_per_run = 0
93  for files in run_to_files_physics.values():
94  files_per_run = len(files)
95  if files_per_run > max_files_per_run:
96  max_files_per_run = files_per_run
97  for files in run_to_files_cosmic.values():
98  files_per_run = len(files)
99  if files_per_run > max_files_per_run:
100  max_files_per_run = files_per_run
101  files_per_run = 0
102  collected_events = 0
103  collected_events_experiment = collections.defaultdict(int)
104  select_events_experiment = collections.defaultdict(bool)
105  for exp in experiment_numbers:
106  collected_events_experiment[exp] = 0
107  select_events_experiment[exp] = True
108  while files_per_run < max_files_per_run:
109  for run, files in run_to_files_physics.items():
110  if not select_events_experiment[run.exp]:
111  continue
112  if files_per_run >= len(files):
113  continue
114  input_file = files[files_per_run]
115  events = events_in_basf2_file(input_file)
116  # Reject files without events.
117  if events == 0:
118  continue
119  if events_per_file > 0 and events > events_per_file:
120  events = events_per_file
121  collected_events = collected_events + events
122  collected_events_experiment[run.exp] = \
123  collected_events_experiment[run.exp] + events
124  reduced_file_to_iov_physics[input_file] = IoV(*run, *run)
125  basf2.B2INFO(f'File {input_file} with {events} events is selected.')
126  for run, files in run_to_files_cosmic.items():
127  if not select_events_experiment[run.exp]:
128  continue
129  if files_per_run >= len(files):
130  continue
131  input_file = files[files_per_run]
132  events = events_in_basf2_file(input_file)
133  # Reject files without events.
134  if events == 0:
135  continue
136  if events_per_file > 0 and events > events_per_file:
137  events = events_per_file
138  collected_events = collected_events + events
139  collected_events_experiment[run.exp] = \
140  collected_events_experiment[run.exp] + events
141  reduced_file_to_iov_cosmic[input_file] = IoV(*run, *run)
142  basf2.B2INFO(f'File {input_file} with {events} events is selected.')
143  files_per_run = files_per_run + 1
144  if collected_events >= required_events:
145  all_experiments_selected = True
146  for exp in experiment_numbers:
147  if collected_events_experiment[exp] >= \
148  required_events_experiment:
149  select_events_experiment[exp] = False
150  else:
151  all_experiments_selected = False
152  if all_experiments_selected:
153  break
154  basf2.B2INFO(f'The total number of collected events is {collected_events}.')
155 
156 
165 
166 
167 def get_calibrations(input_data, **kwargs):
168  """
169  Parameters:
170  input_data (dict): Should contain every name from the 'input_data_names' variable as a key.
171  Each value is a dictionary with {"/path/to/file_e1_r5.root": IoV(1,5,1,5), ...}. Useful for
172  assigning to calibration.files_to_iov
173 
174  **kwargs: Configuration options to be sent in. Since this may change we use kwargs as a way to help prevent
175  backwards compatibility problems. But you could use the correct arguments in b2caf-prompt-run for this
176  release explicitly if you want to.
177 
178  Currently only kwargs["requested_iov"] is used. This is the output IoV range that your payloads should
179  correspond to. Generally your highest ExpRun payload should be open ended e.g. IoV(3,4,-1,-1)
180 
181  Returns:
182  list(caf.framework.Calibration): All of the calibration objects we want to assign to the CAF process
183  """
184  # Set up config options
185 
186  # Expert configuration.
187  expert_config = kwargs.get("expert_config")
188 
189  # In this script we want to use one sources of input data.
190  # Get the input files from the input_data variable
191  file_to_iov_physics = input_data["raw_physics"]
192  file_to_iov_cosmic = input_data["raw_cosmic"]
193 
194  # Select input files.
195  reduced_file_to_iov_physics = collections.OrderedDict()
196  reduced_file_to_iov_cosmic = collections.OrderedDict()
197  select_input_files(file_to_iov_physics, file_to_iov_cosmic,
198  reduced_file_to_iov_physics, reduced_file_to_iov_cosmic,
199  expert_config["required_events"],
200  expert_config["required_events_experiment"],
201  expert_config["events_per_file"])
202 
203  input_files_physics = sorted(list(reduced_file_to_iov_physics.keys()))
204  basf2.B2INFO(f"Total number of 'physics' files actually used as input = {len(input_files_physics)}")
205 
206  input_files_cosmic = sorted(list(reduced_file_to_iov_cosmic.keys()))
207  basf2.B2INFO(f"Total number of 'cosmic' files actually used as input = {len(input_files_cosmic)}")
208 
209  if not input_files_physics and not input_files_cosmic:
210  raise Exception("No valid input files found!")
211 
212  # Get the overall IoV we our process should cover. Includes the end values that we may want to ignore since our output
213  # IoV should be open ended. We could also use this as part of the input data selection in some way.
214  requested_iov = kwargs["requested_iov"]
215 
216  from caf.utils import IoV
217  # The actual value our output IoV payload should have. Notice that we've set it open ended.
218  output_iov = IoV(requested_iov.exp_low, requested_iov.run_low, -1, -1)
219 
220 
222 
223  from ROOT import Belle2
224  from ROOT.Belle2 import KLMChannelIndex, KLMElementNumbers
225  from alignment import MillepedeCalibration
226 
227  # Create the algorithm.
228  millepede = MillepedeCalibration(['BKLMAlignment', 'EKLMAlignment', 'EKLMSegmentAlignment'])
229 
230  # Fix module parameters.
231  index = KLMChannelIndex(KLMChannelIndex.c_IndexLevelLayer)
232  index2 = KLMChannelIndex(KLMChannelIndex.c_IndexLevelLayer)
233  while (index != index2.end()):
234  module = index.getKLMModuleNumber()
235  if (index.getSubdetector() == KLMElementNumbers.c_BKLM):
236  for ipar in [1, 2, 3, 4, 5, 6]:
237  # Free parameters are idU, dV, dGamma.
238  if ipar in [1, 2, 6]:
239  continue
240  millepede.fixGlobalParam(Belle2.BKLMAlignment.getGlobalUniqueID(),
241  module, ipar)
242  else:
243  for ipar in [1, 2, 6]:
244  # No parameters are fixed; if necessary, uncomment the following:
245  # millepede.fixGlobalParam(Belle2.EKLMAlignment.getGlobalUniqueID(),
246  # module, ipar)
247  continue
248  index.increment()
249 
250  # Fix EKLM segment parameters.
251  index.setIndexLevel(KLMChannelIndex.c_IndexLevelStrip)
252  index2.setIndexLevel(KLMChannelIndex.c_IndexLevelStrip)
253  index = index2.beginEKLM()
254  index.useEKLMSegments()
255  while (index != index2.endEKLM()):
256  segment = index.getEKLMSegmentNumber()
257  for ipar in [2, 6]:
258  millepede.fixGlobalParam(
260  segment, ipar)
261  index.increment()
262 
263  cal_klm = millepede.create('KLMAlignment', [])
264  # Number of entries is set in algorithm strategy now.
265  millepede.algo.ignoreUndeterminedParams(True)
266  millepede.algo.invertSign()
267 
268 
270 
271  from caf.framework import Collection
272 
273 
275 
276  from klm_calibration_utils import get_alignment_pre_collector_path_physics, get_alignment_pre_collector_path_cosmic
277 
278  entries = ''
279  if expert_config["events_per_file"] > 0:
280  last_entry = expert_config["events_per_file"]
281  entries = f'0:{last_entry}'
282 
283  if input_files_physics:
284  coll_physics = get_collector("raw_physics")
285  rec_path_physics = get_alignment_pre_collector_path_physics(entry_sequence=entries)
286  # remove cdcdedxpid module
287  tmp = basf2.create_path()
288  for m in rec_path_physics.modules():
289  if m.name() not in ["CDCDedxPID", "TOPBunchFinder", "VXDDedxPID"]:
290  tmp.add_module(m)
291  elif m.name() == "CDCDedxPID":
292  basf2.B2INFO('removed CDCDedxPID')
293  elif m.name() == "TOPBunchFinder":
294  basf2.B2INFO('removed TOPBunchFinder')
295  rec_path_physics = tmp
296 
297  collection_physics = Collection(collector=coll_physics,
298  input_files=input_files_physics,
299  pre_collector_path=rec_path_physics)
300 
301  cal_klm.add_collection(name="physics", collection=collection_physics)
302 
303  if input_files_cosmic:
304  coll_cosmic = get_collector("raw_cosmic")
305  rec_path_cosmic = get_alignment_pre_collector_path_cosmic(entry_sequence=entries)
306  # remove cdcdedxpid module
307  tmp = basf2.create_path()
308  for m in rec_path_cosmic.modules():
309  if m.name() not in ["CDCDedxPID", "TOPBunchFinder", "VXDDedxPID"]:
310  tmp.add_module(m)
311  elif m.name() == "CDCDedxPID":
312  basf2.B2INFO('removed CDCDedxPID')
313  elif m.name() == "TOPBunchFinder":
314  basf2.B2INFO('removed TOPBunchFinder')
315  rec_path_cosmic = tmp
316 
317  collection_cosmic = Collection(collector=coll_cosmic,
318  input_files=input_files_cosmic,
319  pre_collector_path=rec_path_cosmic)
320 
321  cal_klm.add_collection(name="cosmic", collection=collection_cosmic)
322 
323 
325 
326  from klm_alignment import KLMAlignment
327 
328  cal_klm.algorithms = [millepede.algo]
329 
330  # Bugfix for Condor:
331  from alignment.prompt_utils import fix_mille_paths_for_algo
332  for algorithm in cal_klm.algorithms:
333  fix_mille_paths_for_algo(algorithm)
334 
335  for algorithm in cal_klm.algorithms:
336  algorithm.strategy = KLMAlignment
337  algorithm.params = {
338  "has_experiment_settings": True,
339  "iov_coverage": output_iov,
340  "millepede_entries": expert_config["millepede_entries"],
341  "millepede_entries_exp7": expert_config["millepede_entries_exp7"]
342  }
343 
344  # You must return all calibrations you want to run in the prompt process, even if it's only one
345  return [cal_klm]
346 
347 
348 
349 
350 def get_collector(input_data_name):
351  """
352  Return the correct MillepedeCollector module setup for each data type.
353  Placed here so it can be different for prompt compared to standard.
354  """
355 
356  if input_data_name == "raw_physics":
357  return basf2.register_module(
358  'MillepedeCollector',
359  components=['BKLMAlignment', 'EKLMAlignment',
360  'EKLMSegmentAlignment'],
361  useGblTree=True,
362  minPValue=1e-5)
363  elif input_data_name == "raw_cosmic":
364  return basf2.register_module(
365  'MillepedeCollector',
366  components=['BKLMAlignment', 'EKLMAlignment',
367  'EKLMSegmentAlignment'],
368  useGblTree=True,
369  minPValue=1e-5)
370 
371  raise Exception("Unknown input data name used when setting up collector")
static unsigned short getGlobalUniqueID()
Get global unique identifier.
Definition: BKLMAlignment.h:63
static unsigned short getGlobalUniqueID()
Get global unique identifier.