Belle II Software release-09-00-00
caf_vxdcdc_alignment.py
1
8"""
9
10Full Simultaneous Global and Local VXD and CDC alignment with Millepede II
11
12The input collections are:
13- cosmics (hlt skim) - mandatorry
14- hadron - for "low" momentum tracks from IP
15- mumu - mumu_2trk or mumu_tight - for high momentum tracks from IP
16- offip - tracks from outside IP (beam background, beam-gas)
17
18"""
19
20import basf2
21import ROOT
22
23from prompt import CalibrationSettings, INPUT_DATA_FILTERS
24from prompt.calibrations.caf_cdc import settings as cdc_calibration
25from prompt.calibrations.caf_svd_time import settings as svd_time_calibration
26
27from prompt.utils import events_in_basf2_file
28from caf.utils import IoV
29from caf import strategies
30
31from softwaretrigger.constants import HLT_INPUT_OBJECTS
32import rawdata as raw
33import reconstruction as reco
34import modularAnalysis as ana
35import vertex as vtx
36
37from random import choice
38from random import seed
39
40import millepede_calibration as mpc
44
45collection_names = ["cosmic", "hadron", "mumu", "offip"]
46
47default_config = {
48 "only_prompt": False,
49 "cosmic.max_processed_events_per_file": 4000,
50 "hadron.max_processed_events_per_file": 1000,
51 "mumu.max_processed_events_per_file": 5000,
52 "offip.max_processed_events_per_file": 2000,
53 "stage1.method": "decomposition"
54}
55
56quality_flags = [INPUT_DATA_FILTERS["Run Type"]["physics"],
57 INPUT_DATA_FILTERS["Data Quality Tag"]["Good Or Recoverable"],
58 INPUT_DATA_FILTERS["Magnet"]["On"]]
59
60
61settings = CalibrationSettings(name="Full VXD and CDC Alignment",
62 expert_username="bilkat",
63 description=__doc__,
64 input_data_formats=["raw"],
65 input_data_names=collection_names,
66 input_data_filters={
67 "cosmic": [INPUT_DATA_FILTERS["Data Tag"]["cosmic_calib"]] + quality_flags,
68 "mumu": [INPUT_DATA_FILTERS["Data Tag"]["mumu_tight_or_highm_calib"]] + quality_flags,
69 "hadron": [INPUT_DATA_FILTERS["Data Tag"]["hadron_calib"]] + quality_flags,
70 "offip": [INPUT_DATA_FILTERS["Data Tag"]["offip_calib"]] + quality_flags
71 },
72
73 expert_config=default_config,
74 depends_on=[cdc_calibration, svd_time_calibration])
75
76
77def select_files(all_input_files, min_events, max_processed_events_per_file):
78 """
79 Select files randomly
80
81 Parameters
82 ----------
83 all_input_files : list(str)
84 List of all input file names
85 min_events : int
86 Minimum number of events to select from files
87 max_processed_events_per_file : int
88 Maximum number of events to consider per file
89 """
90 all_input_files = all_input_files[:]
91 # Let's iterate, taking a sample of files from the total (no repeats or replacement) until we get enough events
92 total_events = 0
93 chosen_files = []
94 while total_events < min_events:
95 # If the set is empty we must have used all available files. Here we break and continue. But you may want to
96 # raise an Error...
97 if not all_input_files:
98 break
99 # Randomly select a file
100 new_file_choice = choice(all_input_files)
101 # Remove it from the list so it can't be chosen again
102 all_input_files.remove(new_file_choice)
103 # Find the number of events in the file
104 total_events_in_file = events_in_basf2_file(new_file_choice)
105 if not total_events_in_file:
106 # Uh Oh! Zero event file, skip it
107 continue
108
109 events_contributed = min(total_events_in_file, max_processed_events_per_file)
110
111 chosen_files.append(new_file_choice)
112 total_events += events_contributed
113
114 basf2.B2INFO(f"Total chosen files = {len(chosen_files)}")
115 basf2.B2INFO(f"Total events in chosen files = {total_events}")
116 if total_events < min_events:
117 basf2.B2WARNING(
118 f"There weren't enough files events selected when max_processed_events_per_file={max_processed_events_per_file}")
119 return chosen_files
120
121
122def create_std_path():
123 """
124 Returns default path for collections with standard reconstruction
125 """
126 path = basf2.create_path()
127 path.add_module('Progress')
128 path.add_module('RootInput')
129 path.add_module('Gearbox')
130 path.add_module('Geometry')
131 raw.add_unpackers(path)
132 path.add_module('SetupGenfitExtrapolation')
133 reco.add_reconstruction(
134 path,
135 pruneTracks=False,
136 skipGeometryAdding=True,
137 )
138 path.add_module('DAFRecoFitter')
139 return path
140
141
142def create_cosmics_path():
143 """
144 Returns default path for cosmic collection
145 """
146 path = basf2.create_path()
147 path.add_module('Progress')
148 path.add_module('RootInput')
149 path.add_module('Gearbox')
150 path.add_module('Geometry')
151
152 raw.add_unpackers(path)
153 path.add_module('SetupGenfitExtrapolation')
154 reco.add_cosmics_reconstruction(
155 path,
156 pruneTracks=False,
157 skipGeometryAdding=True,
158 addClusterExpertModules=False,
159 merge_tracks=True
160 )
161
162 path.add_module('SetRecoTrackMomentum', automatic=True)
163 path.add_module('DAFRecoFitter', pdgCodesToUseForFitting=[13])
164
165 ana.fillParticleList(
166 'mu+:goodForVXDCDCAlignment',
167 '[z0 <= 57. or abs(d0) >= 26.5] and abs(dz) > 0.4 and nTracks == 1',
168 path=path)
169 path.add_module('SkimFilter', particleLists=['mu+:goodForVXDCDCAlignment']).if_false(basf2.create_path())
170
171 return path
172
173
174def make_mumu_collection(
175 name,
176 files=None,
177 muon_cut='p > 1.0 and abs(dz) < 2.0 and dr < 0.5 and nTracks==2', dimuon_cut='9.5 < M and M < 11.',
178 prescale=1.):
179 """
180 Di-muons with vertex+beam constraint collection
181
182 Parameters
183 ----------
184 name : str
185 Collection name
186 files : list(str)
187 List of input data files
188 muon_cut : str
189 Cut string to select daughter muons
190 dimuon_cut : str
191 Cut string to apply for reconstructed di-muon decay
192 prescale : float
193 Process only 'prescale' fraction of events
194 """
195 path = basf2.create_path()
196 path.add_module('Progress')
197 path.add_module('RootInput')
198 if prescale != 1.:
199 path.add_module('Prescale', prescale=prescale).if_false(basf2.Path(), basf2.AfterConditionPath.END)
200
201 path.add_module('Gearbox')
202 path.add_module('Geometry')
203
204 raw.add_unpackers(path)
205
206 reco.add_reconstruction(path, pruneTracks=False)
207
208 path.add_module('DAFRecoFitter', pdgCodesToUseForFitting=[13])
209
210 ana.fillParticleList(f"mu+:{name}", muon_cut, path=path)
211 ana.reconstructDecay(f"Upsilon(4S):{name} -> mu+:{name} mu-:{name}", dimuon_cut, path=path)
212
213 vtx.raveFit(f"Upsilon(4S):{name}", 0.001, daughtersUpdate=True, silence_warning=True, path=path, constraint="ipprofile")
214
216 name,
217 files=files,
218 path=path,
219 primaryVertices=[f"Upsilon(4S):{name}"])
220
221
222def create_prompt(files, cfg):
223 """
224 Returns configured (original) prompt stage alignment
225
226 Parameters
227 ----------
228 files : list(str)
229 Dictionary with all input files by category (name)
230 cfg : dict
231 Expert config dictionary
232 """
233 mumu = select_files(files["mumu"], 0.2e6, cfg["mumu.max_processed_events_per_file"])
234 cosmic = select_files(files["cosmic"], 1e6, cfg["cosmic.max_processed_events_per_file"])
235 hadron = select_files(files["hadron"], 0.5e5, cfg["hadron.max_processed_events_per_file"])
236 offip = select_files(files["offip"], 0.2e6, cfg["offip.max_processed_events_per_file"])
237
238 cal = mpc.create(
239 name='VXDCDCalignment_prompt',
240 dbobjects=['VXDAlignment', 'CDCAlignment'],
241 collections=[
242 mpc.make_collection("cosmic", path=create_cosmics_path(), tracks=["RecoTracks"]),
243 mpc.make_collection("hadron", path=create_std_path(), tracks=["RecoTracks"]),
244 mpc.make_collection("mumu", path=create_std_path(), tracks=["RecoTracks"]),
245 mpc.make_collection("offip", path=create_std_path(), tracks=["RecoTracks"])
246 ],
247 tags=None,
248 files=dict(mumu=mumu, cosmic=cosmic, hadron=hadron, offip=offip),
249 timedep=None,
250 constraints=[
251 alignment.constraints.VXDHierarchyConstraints(type=2, pxd=True, svd=True),
252 alignment.constraints.CDCLayerConstraints(z_offset=True, z_scale=False, twist=True)
253 ],
254 fixed=alignment.parameters.vxd_sensors(rigid=False, surface2=False, surface3=False, surface4=False),
255 commands=[
256 "method diagonalization 3 0.1",
257 "scaleerrors 1. 1.",
258 "entries 1000"],
259 params=dict(minPValue=0.00001, externalIterations=0, granularity="run"),
260 min_entries=1000000)
261
262 cal.max_iterations = 5
263
264 return cal
265
266
267def create_beamspot(files, cfg):
268 """
269 Returns configured beamspot calibration
270
271 Parameters
272 ----------
273 files : list(str)
274 Dictionary with all input files by category (name)
275 cfg : dict
276 Expert config dictionary
277 """
278
279 mumu = select_files(files["mumu"], 10e6, cfg["mumu.max_processed_events_per_file"])
280
281
283
284 from ROOT.Belle2 import BeamSpotAlgorithm
285 from basf2 import create_path, register_module
286
287
289
290 from caf.framework import Calibration, Collection
291 from caf.strategies import SingleIOV
292
293 # module to be run prior the collector
294 path = create_path()
295
296 path.add_module('Progress')
297 path.add_module('RootInput')
298 path.add_module('Gearbox')
299 path.add_module('Geometry')
300 raw.add_unpackers(path)
301 path.add_module('SetupGenfitExtrapolation')
302 reco.add_reconstruction(path, skipGeometryAdding=True)
303
304 muSelection = '[p>1.0]'
305 muSelection += ' and abs(dz)<2.0 and abs(dr)<0.5'
306 muSelection += ' and nPXDHits >=1 and nSVDHits >= 8 and nCDCHits >= 20'
307 ana.fillParticleList('mu+:BS', muSelection, path=path)
308 ana.reconstructDecay('Upsilon(4S):BS -> mu+:BS mu-:BS', '9.5<M<11.5', path=path)
309
310 collector_bs = register_module('BeamSpotCollector', Y4SPListName='Upsilon(4S):BS')
311 algorithm_bs = BeamSpotAlgorithm()
312 # algorithm_bs.setOuterLoss(cfg['outerLoss'])
313 # algorithm_bs.setInnerLoss(cfg['innerLoss'])
314
315 collection_bs = Collection(collector=collector_bs,
316 input_files=mumu,
317 pre_collector_path=path)
318
319 calibration_bs = Calibration('VXDCDCalignment_beamspot', algorithms=algorithm_bs)
320 calibration_bs.add_collection("mumu", collection_bs)
321
322 calibration_bs.strategies = SingleIOV
323
324 return calibration_bs
325
326
327def create_stage1(files, cfg):
328 """
329 Returns configured stage1 alignment (full constant alignment with wires, beamspot fixed)
330
331 Parameters
332 ----------
333 files : list(str)
334 Dictionary with all input files by category (name)
335 cfg : dict
336 Expert config dictionary
337 """
338
339 mumu = select_files(files["mumu"], 1.5e6, cfg["mumu.max_processed_events_per_file"])
340 cosmic = select_files(files["cosmic"], 0.7e6, cfg["cosmic.max_processed_events_per_file"])
341 hadron_and_offip = select_files(files["hadron"] + files["offip"], int(4.0e6 / 10.), cfg["hadron.max_processed_events_per_file"])
342
343 cal = mpc.create(
344 name='VXDCDCalignment_stage1',
345 dbobjects=['VXDAlignment', 'CDCAlignment', 'BeamSpot'],
346 collections=[
347 mpc.make_collection("cosmic", path=create_cosmics_path(), tracks=["RecoTracks"]),
348 mpc.make_collection("hadron", path=create_std_path(), tracks=["RecoTracks"]),
349 make_mumu_collection(name="mumu")
350 ],
351 tags=None,
352 files=dict(mumu=mumu, cosmic=cosmic, hadron=hadron_and_offip),
353 timedep=None,
354 constraints=[
355 alignment.constraints.VXDHierarchyConstraints(type=2, pxd=True, svd=True),
356 alignment.constraints.CDCLayerConstraints(z_offset=False, z_scale=False, twist=False),
357 alignment.constraints.CDCWireConstraints(layer_rigid=True, layer_radius=[53], cdc_radius=True, hemisphere=[55])
358 ],
359 fixed=alignment.parameters.vxd_sensors(rigid=False, surface2=False, surface3=False, surface4=False)
361 commands=[
362 f"method {cfg['stage1.method']} 6 0.001",
363 "entries 1000",
364 "threads 10 10"],
365 params=dict(minPValue=0.00001, externalIterations=0, granularity="run"),
366 min_entries=500000)
367
368 # Ignore results for BeamSpot
369 std_components = ROOT.vector('string')()
370 for component in ['VXDAlignment', 'CDCAlignment']:
371 std_components.push_back(component)
372 cal.algorithms[0].algorithm.setComponents(std_components)
373
374 cal.max_iterations = 0
375
376 return cal
377
378
379def create_stage2(files, cfg):
380 """
381 Returns configured stage2 alignment (run-dependent alignment)
382
383 Parameters
384 ----------
385 files : list(str)
386 Dictionary with all input files by category (name)
387 cfg : dict
388 Expert config dictionary
389 """
390 mumu = select_files(files["mumu"], 10e6, cfg["mumu.max_processed_events_per_file"])
391 cosmic = select_files(files["cosmic"], 2e6, cfg["cosmic.max_processed_events_per_file"])
392
393 cal = mpc.create(
394 name='VXDCDCalignment_stage2',
395 dbobjects=['VXDAlignment', 'CDCAlignment', 'BeamSpot'],
396 collections=[
397 mpc.make_collection("cosmic", path=create_cosmics_path(), tracks=["RecoTracks"]),
398 make_mumu_collection(name="mumu")],
399 tags=None,
400 files=dict(mumu=mumu, cosmic=cosmic),
401 timedep=None,
402 constraints=[
403 alignment.constraints.VXDHierarchyConstraints(type=2, pxd=True, svd=True),
404 alignment.constraints.CDCLayerConstraints(z_offset=True, z_scale=False, twist=False)],
405 fixed=alignment.parameters.vxd_sensors(layers=[3, 4, 5, 6]) +
406 alignment.parameters.vxd_sensors(layers=[1, 2], rigid=False, surface2=False, surface3=False, surface4=True),
407 commands=["method inversion 6 0.001", "entries 1000", "threads 10 10"],
408 params=dict(minPValue=0.00001, externalIterations=0, granularity="run"),
409 min_entries=80000)
410
411 # Ignore results for BeamSpot
412 std_components = ROOT.vector('string')()
413 for component in ['VXDAlignment', 'CDCAlignment']:
414 std_components.push_back(component)
415 cal.algorithms[0].algorithm.setComponents(std_components)
416
417 # Sequential run-by-run strategy
418 cal.max_iterations = 0
419 cal.algorithms[0].strategy = strategies.SequentialRunByRun
420
421 return cal
422
423
426
427
428def get_calibrations(input_data, **kwargs):
429 """
430 Required function called by b2caf-prompt-run.
431 Returns full configured 4-stage final alignment for prompt
432
433 """
434 seed(1234)
435
436 cfg = kwargs['expert_config']
437 files = dict()
438
439 for colname in collection_names:
440 file_to_iov = input_data[colname]
441 input_files = list(file_to_iov.keys())
442 files[colname] = input_files
443
444 prompt = create_prompt(files, cfg)
445 beamspot = create_beamspot(files, cfg)
446 stage1 = create_stage1(files, cfg)
447 stage2 = create_stage2(files, cfg)
448
449 requested_iov = kwargs.get("requested_iov", None)
450 output_iov = IoV(requested_iov.exp_low, requested_iov.run_low, -1, -1)
451
452 for cal in [prompt, beamspot, stage1, stage2]:
453 for colname in collection_names:
454 if colname not in cal.collections.keys():
455 continue
456 max_processed_events_per_file = cfg[f'{colname}.max_processed_events_per_file']
457 basf2.set_module_parameters(
458 cal.collections[colname].pre_collector_path,
459 'RootInput',
460 entrySequences=[f'0:{max_processed_events_per_file}'], branchNames=HLT_INPUT_OBJECTS)
461
462 for algorithm in cal.algorithms:
463 algorithm.params = {"apply_iov": output_iov}
464
465 # Bugfix for Condor:
466 for cal in [prompt, stage1, stage2]:
467 from alignment.prompt_utils import fix_mille_paths_for_algo
468 fix_mille_paths_for_algo(cal.algorithms[0])
469
470 beamspot.depends_on(prompt)
471 stage1.depends_on(beamspot)
472 stage2.depends_on(stage1)
473
474 # Do not save BeamSpot payloads in the final output database
475 # because alignment is changed -> BeamSpot payloads now invalid
476 beamspot.save_payloads = False
477
478 if cfg["only_prompt"]:
479 return [prompt]
480
481 return [prompt, beamspot, stage1, stage2]
482
483
484if __name__ == '__main__':
485 get_calibrations(dict(cosmic=dict(), mumu=dict(), hadron=dict(), offip=dict()),
486 requested_iov=IoV(0, 0, -1, -1), expert_config=default_config)
487
def make_collection(name, files=None, path=None, **argk)
Definition: collections.py:20
def vxd_sensors(layers=None, rigid=True, surface=True, surface2=True, surface3=True, surface4=True, parameters=None)
Definition: parameters.py:157