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