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