Belle II Software development
caf_klm_alignment.py
1
8
9"""
10Calibration of KLM alignment. It provides calibration constants for the BKLMAlignment
11and EKLMAlignment database objects.
12"""
13
14import collections
15
16import basf2
17from caf.utils import ExpRun, IoV
18from prompt import CalibrationSettings, INPUT_DATA_FILTERS
19from prompt.utils import events_in_basf2_file
20
21
28
29from prompt.calibrations.vxdcdc_alignment import settings as vxdcdc_alignment
30
31
39settings = 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
65def 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
167def 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
350def 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.