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