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