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