Belle II Software development
event_generation.py
1#!/usr/bin/env python3
2
3
10
11import os
12
13import basf2
14import simulation
15import generators
16import beamparameters
17
18from tracking.run import utilities
19from tracking.run.minimal import MinimalRun
20import tracking.adjustments as adjustments
21
22import argparse
23import logging
24
25
26def get_logger():
27 return logging.getLogger(__name__)
28
29
30# Standard run for generating or reading event #
31
32
33
35 """Read generated events or generate new events"""
36
37
38 description = "Simulate events using various generator and detector setups from command line."
39
40 # Declarative section which can be redefined in a subclass
41
42
43 generator_module = None
44
45 detector_setup = "Default"
46
47 bkg_files = []
48
49 components = None
50
51 disable_deltas = False
52
53 simulation_output = None
54
55 def create_argument_parser(self, **kwds):
56 """Convert command-line arguments to basf2 argument list"""
57 argument_parser = super().create_argument_parser(**kwds)
58
59 setup_argument_group = argument_parser.add_argument_group("Detector setup arguments")
60 setup_argument_group.add_argument(
61 '-d',
62 '--detector',
63 dest='detector_setup',
64 default=argparse.SUPPRESS,
65 metavar='DETECTOR_SETUP_NAME',
66 choices=utilities.NonstrictChoices(detector_setups_by_short_name.keys()),
67 help=('Name of the detector setup to be used')
68 )
69
70 setup_argument_group.add_argument(
71 '-c',
72 '--component',
73 dest='components',
74 nargs='+',
75 default=argparse.SUPPRESS,
76 metavar='COMPONENTS',
77 action='store',
78 help=('Overrides the components of the detector setup')
79 )
80
81 generator_argument_group = argument_parser.add_argument_group("Generator arguments")
82 generator_argument_group.add_argument(
83 '-g',
84 '--generator',
85 dest='generator_module',
86 default=argparse.SUPPRESS,
87 metavar='GENERATOR_NAME',
88 choices=utilities.NonstrictChoices(valid_generator_short_names),
89 help='Name module or short name of the generator to be used.',
90 )
91
92 simulation_argument_group = argument_parser.add_argument_group("Simulation arguments")
93 simulation_argument_group.add_argument(
94 '-b',
95 '--bkg-file',
96 dest='bkg_files',
97 nargs='+',
98 default=self.bkg_files,
99 metavar='BACKGROUND_DIRECTORY',
100 help='Path to folder of files or to a file containing the background to be used. ' +
101 'Can be given multiple times.',
102 )
103
104 simulation_argument_group.add_argument(
105 '--disable-deltas',
106 action='store_true',
107 help='Disable the generation of delta rays in the simulation'
108 )
109
110 simulation_argument_group.add_argument(
111 '-so',
112 '--simulation-output',
113 nargs='?',
114 default=self.simulation_output,
116 dest='simulation_output',
117 help='Only generate and simulate the events and write them to the given output file. Skip rest of the path.'
118 )
119
120 return argument_parser
121
122 def configure(self, arguments):
123 """Configure for basf2 job; disable ROOT input if simulating events"""
124 super().configure(arguments)
125 if self.simulation_output:
126 get_logger().info("Requested to simulation run. Deactivate input file")
127
129
130 def execute(self):
131 """Run the basf2 job"""
132 if not self.simulation_output:
133 super().execute()
134 return
135
136 # Run only simulation
137 path = ReadOrGenerateEventsRun.create_path(self)
138 self.run(path)
139
140 def create_path(self):
141 """Create and configure the basf2 path"""
142 path = super().create_path()
143
144 # Gearbox & Geometry must always be registered
145 path.add_module("Gearbox")
146 path.add_module("Geometry", useDB=True)
147 if self.detector_setup:
148 detector_setup = self.detector_setup
149 detector_setup_function = detector_setups_by_short_name[detector_setup]
150 components = detector_setup_function(path)
151
152 if self.components:
153 components = self.components
154
155 # Only generate events if no input file has been provided
156 if self.root_input_fileroot_input_file is None:
157 # Check if generator means a decay file
158 if isinstance(self.generator_module, str) and utilities.find_file(self.generator_module):
159 dec_file_path = utilities.find_file(self.generator_module)
160 add_evtgen_generator(path, dec_file_path)
161 else:
162 # All other possibilities
163 utilities.extend_path(path,
164 self.generator_module,
165 generators_by_short_name,
166 allow_function_import=True)
167
168 # Only simulate if generator is setup
169 if self.root_input_fileroot_input_file is None:
170 bkg_file_paths = get_bkg_file_paths(self.bkg_files)
171
173 components=components,
174 bkgfiles=bkg_file_paths)
175
176 if self.disable_deltas:
177 adjustments.disable_deltas(path)
178
179 # Catch if no generator is added, no background should be simulated and events
180 # are not read from a file.
181 if not bkg_file_paths and self.generator_module is None:
182 raise RuntimeError('Need at least one of root_input_file,'
183 ' generator_module or bkg_files specified.')
184 else:
185 if not os.path.exists(self.root_input_fileroot_input_file):
186 raise RuntimeError("Could not find file " + str(self.root_input_fileroot_input_file) + ". Generate it with -- -so?")
187
188 # early write out if simulation output was requested
189 if self.simulation_output:
190 path.add_module('RootOutput',
191 outputFileName=self.simulation_output)
192
193 return path
194
195
197 """Generate events using the EvtGen generator"""
198
199 generator_module = "EvtGenInput"
200
201
202# Default settings and shorthand names for generator with specific settings #
203
204
205# PDG code of an electorn
206electron_pdg_code = 11
207
208# PDG code of a muon
209muon_pdg_code = 13
210
211# PDG code of a tau
212tau_pdg_code = 15
213
214# PDG code of a pion
215pion_pdg_code = 211
216
217# PDG code of a kaon
218kaon_pdg_code = 321
219
220# PDG code of a protons
221proton_pdg_code = 2212
222
223
224def add_single_gun_generator(path):
225 """Add ParticleGun with a single muon"""
226 path.add_module("ParticleGun",
227 pdgCodes=[muon_pdg_code, -muon_pdg_code],
228 nTracks=1,
229 varyNTracks=False,
230 momentumGeneration='inversePt',
231 momentumParams=[0.6, 1.4],
232 thetaGeneration='uniform',
233 thetaParams=[17., 150.])
234
235
236def add_transverse_gun_generator(path):
237 """Add ParticleGun to illuminate a region of the phase space with low efficiency"""
238 path.add_module("ParticleGun",
239 pdgCodes=[muon_pdg_code, -muon_pdg_code],
240 nTracks=1,
241 varyNTracks=False,
242 momentumGeneration='inversePt',
243 momentumParams=[0.275, 0.276],
244 thetaGeneration='uniform',
245 thetaParams=[89., 91.])
246
247
248def add_simple_gun_generator(path):
249 """Add ParticleGun firing 10 muons at medium energy"""
250 path.add_module("ParticleGun",
251 pdgCodes=[muon_pdg_code, -muon_pdg_code],
252 nTracks=10,
253 varyNTracks=False,
254 momentumGeneration='inversePt',
255 momentumParams=[0.6, 1.4],
256 thetaGeneration='uniform')
257
258
259def add_low_gun_generator(path):
260 """Add ParticleGun firing 10 muons at low energy"""
261 path.add_module("ParticleGun",
262 pdgCodes=[muon_pdg_code, -muon_pdg_code],
263 nTracks=10,
264 varyNTracks=False,
265 momentumGeneration='inversePt',
266 momentumParams=[0.4, 0.8],
267 thetaGeneration='uniform')
268
269
270def add_gun_generator(path):
271 """Add ParticleGun firing 10 muons with wide energy range"""
272 path.add_module("ParticleGun",
273 pdgCodes=[muon_pdg_code, -muon_pdg_code],
274 nTracks=10,
275 varyNTracks=False,
276 momentumGeneration='inversePt',
277 thetaGeneration='uniform',
278 thetaParams=[17., 150.])
279
280
281def add_eloss_gun_generator(path):
282 """Add ParticleGun particle gun for energy loss estimations"""
283 path.add_module("ParticleGun",
284 pdgCodes=[
285 # muon_pdg_code,
286 # -muon_pdg_code,
287 # electron_pdg_code,
288 # -electron_pdg_code,
289 pion_pdg_code,
290 -pion_pdg_code,
291 # kaon_pdg_code,
292 # -kaon_pdg_code,
293 # proton_pdg_code,
294 # -proton_pdg_code,
295 ],
296 momentumParams=[0.3, 2],
297 nTracks=10,
298 varyNTracks=False,
299 thetaGeneration='uniform',
300 # thetaParams=[17., 150.],
301 thetaParams=[89., 91],
302 )
303
304
305def add_forward_gun_generator(path):
306 """Add ParticleGun with one muon in rather forward direction"""
307 path.add_module("ParticleGun",
308 pdgCodes=[muon_pdg_code, -muon_pdg_code],
309 nTracks=1,
310 varyNTracks=False,
311 momentumGeneration='inversePt',
312 thetaGeneration='uniform',
313 thetaParams=[30., 31.])
314
315
316def add_evtgen_generator(path, dec_file_path=None):
317 """Add Y4S generator"""
319 if dec_file_path:
320 path.add_module("EvtGenInput", userDECFile=dec_file_path)
321 else:
322 path.add_module("EvtGenInput")
323
324
325def add_cosmics_generator(path):
326 """Add simple cosmics generator"""
328
329
330def add_sector_tb_generator(path, sector=1):
331 phiBounds = (240 + 60.0 * sector % 360.0, 300 + 60.0 * sector % 360.0)
332 path.add_module("ParticleGun",
333 pdgCodes=[muon_pdg_code, -muon_pdg_code],
334 nTracks=1,
335 varyNTracks=False,
336 momentumGeneration='uniform',
337 momentumParams=[2.0, 4.0],
338 phiGeneration='uniform',
339 phiParams=phiBounds,
340 thetaGeneration='uniform',
341 thetaParams=[70., 110.])
342
343
344def add_cosmics_tb_generator(path):
345 """Add simple cosmics generator resembling the test beam setup"""
346 path.add_module("Cosmics",
347 ipRequirement=1,
348 ipdr=5,
349 ipdz=15,
350 )
351
352 # Use point trigger
353 tof_mode = 1
354 # Do not add time of propagation in the scintilator
355 top_mode = False
356
357 cosmics_selector = path.add_module('CDCCosmicSelector',
358 xOfCounter=0.0,
359 yOfCounter=0.0,
360 zOfCounter=0.0,
361 TOF=tof_mode,
362 TOP=top_mode,
363 cryGenerator=False,
364 )
365
366 cosmics_selector.if_false(basf2.create_path())
367
368
369def add_cry_tb_generator(path):
370 """Add cry generator resembling the test beam setup"""
371 generators.add_cosmics_generator(path, accept_box=[0.7, 0.3, 0.3],
372 keep_box=[0.7, 0.3, 0.3],
373 pre_general_run_setup="normal")
374
375
376def add_no_generator(path):
377 """Add no generator for e.g. background only studies"""
378 # Nothing to do here since the background files are included in the add_simulation
379
380
381# Generator module names hashed by shorthand menomics. Includes
382# None as a special value for background only simulation
383generators_by_short_name = {
384 'single_gun': add_single_gun_generator,
385 'transverse_gun': add_transverse_gun_generator,
386 'simple_gun': add_simple_gun_generator,
387 'low_gun': add_low_gun_generator,
388 'forward_gun': add_forward_gun_generator,
389 'gun': add_gun_generator,
390 'eloss_gun': add_eloss_gun_generator,
391 'generic': add_evtgen_generator,
392 "EvtGenInput": add_evtgen_generator, # <- requires beam parameters
393 'cosmics': add_cosmics_generator,
394 'cosmics_tb': add_cosmics_tb_generator,
395 'cry_tb': add_cry_tb_generator,
396 'sector_tb': add_sector_tb_generator,
397 'bkg': add_no_generator,
398 'none': add_no_generator,
399}
400
401# Names of module names and short names of the generators usable in this script.
402valid_generator_short_names = list(generators_by_short_name.keys())
403
404
405# Memorandum of geometry setups #
406# ############################# #
407def setup_default_detector(path):
408 pass
409
410
411def setup_tracking_detector(path):
412 components = ["BeamPipe", "PXD", "SVD", "CDC", "MagneticField"]
413 override = [
414 ("/DetectorComponent[@name='MagneticField']//Component[@type='3d'][2]/ExcludeRadiusMax",
415 "4.20", "m", ) # Remove the second component which is the magnetic field outside the tracking volume.
416 ]
417
418 adjustments.adjust_module(path, "Gearbox", override=override)
419 adjustments.adjust_module(path, "Geometry", components=components)
420 return components
421
422
423def setup_tracking_detector_constant_b(path):
424 components = ["BeamPipe", "PXD", "SVD", "CDC", "MagneticFieldConstant4LimitedRCDC"]
425 adjustments.adjust_module(path, "Geometry", components=components)
426 return components
427
428
429def setup_cdc_cr_test(path):
430 components = ["CDC"]
431 override = [
432 # Reset the top volume to accommodate cosmics that can hit all parts of the detector
433 ("/Global/length", "8.", "m"),
434 ("/Global/width", "8.", "m"),
435 ("/Global/height", "1.5", "m"),
436
437 # Adjustments of the CDC setup
438 ("/DetectorComponent[@name='CDC']//t0FileName", "t0.dat", ""),
439 ("/DetectorComponent[@name='CDC']//xtFileName", "xt_noB_v1.dat", ""),
440 ("/DetectorComponent[@name='CDC']//GlobalPhiRotation", "1.875", "deg"),
441 # ("/DetectorComponent[@name='CDC']//bwFileName", "badwire_CDCTOP.dat", ""),
442 ]
443
444 adjustments.adjust_module(path, "Gearbox", override=override)
445 adjustments.adjust_module(path, "Geometry", components=components)
446 return components
447
448
449def setup_cdc_top_test(path):
450 components = ["CDC"]
451 override = [
452 # Reset the top volume: must be larger than the generated surface and higher than the detector
453 # It is the users responsibility to ensure a full angular coverage
454 ("/Global/length", "8.", "m"),
455 ("/Global/width", "8.", "m"),
456 ("/Global/height", "1.5", "m"),
457
458 # Adjustments of the CDC setup
459 ("/DetectorComponent[@name='CDC']//t0FileName", "t0.dat", ""),
460 ("/DetectorComponent[@name='CDC']//xtFileName", "xt_noB_v1.dat", ""),
461 # ("/DetectorComponent[@name='CDC']//bwFileName", "badwire_CDCTOP.dat", ""),
462 ("/DetectorComponent[@name='CDC']//GlobalPhiRotation", "1.875", "deg"),
463 ("/DetectorComponent[@name='MagneticField']//Component/Z", "0", ""),
464 ]
465
466 adjustments.adjust_module(path, "Gearbox",
467 override=override,
468 fileName="geometry/CDCcosmicTests.xml" # <- does something mysterious to the reconstruction...
469 )
470
471 adjustments.adjust_module(path, "Geometry", components=components)
472 return components
473
474
475detector_setups_by_short_name = {
476 "Default": setup_default_detector,
477 'TrackingDetector': setup_tracking_detector,
478 'TrackingDetectorConstB': setup_tracking_detector_constant_b,
479 'CDCCRTest': setup_cdc_cr_test,
480 'CDCTOPTest': setup_cdc_top_test,
481}
482
483
484# Heuristic to find background files #
485# ################################## #
486
487def is_bkg_file(bkg_file_path):
488 """Test if a file path points to a file containing background mixins.
489
490 Returns
491 -------
492 bool
493
494 Note
495 ----
496 Simple test only checks if file exists and ends with ".root"
497 """
498 return os.path.isfile(bkg_file_path) and bkg_file_path.endswith('.root')
499
500
501def get_bkg_file_paths(bkg_dir_or_file_paths):
502 """Unpacks the content of a single or a list of directories and/or files filtering for
503 files containing background mixins.
504
505 Parameters
506 ----------
507 bkg_dir_or_file_paths : string or iterable of strings
508 Single file or single directory in which background files are located or
509 a list of files and/or directories.
510
511 Returns
512 -------
513 list(string)
514 A list of paths to individual background files.
515 """
516
517 if isinstance(bkg_dir_or_file_paths, str):
518 bkg_dir_or_file_path = bkg_dir_or_file_paths
519 bkg_dir_or_file_paths = [bkg_dir_or_file_path]
520
521 result = []
522 for bkg_dir_or_file_path in bkg_dir_or_file_paths:
523 if is_bkg_file(bkg_dir_or_file_path):
524 bkg_file_path = bkg_dir_or_file_path
525 result.append(bkg_file_path)
526 elif os.path.isdir(bkg_dir_or_file_path):
527
528 bkg_dir_path = bkg_dir_or_file_path
529 bkg_dir_contents = os.listdir(bkg_dir_path)
530 for dir_or_file_name in bkg_dir_contents:
531 dir_or_file_path = os.path.join(bkg_dir_path, dir_or_file_name)
532 if is_bkg_file(dir_or_file_path):
533 bkg_file_path = dir_or_file_path
534 result.append(bkg_file_path)
535
536 return result
def add_beamparameters(path, name, E_cms=None, **argk)
None components
By default, do specific components.
str detector_setup
By default, use the default detector setup.
bool disable_deltas
By default, do not disable delta-ray generation.
None simulation_output
By default, do no store the simulation output.
root_input_file
generating events, so there is no ROOT input file
None generator_module
By default, do not generate events.
list bkg_files
By default, no background overlay.
def run(self, path)
Definition: minimal.py:58
None root_input_file
By default, there is no input ROOT TFile.
Definition: minimal.py:113
def add_cosmics_generator(path, components=None, global_box_size=None, accept_box=None, keep_box=None, geometry_xml_file='geometry/Beast2_phase2.xml', cosmics_data_dir='data/generators/modules/cryinput/', setup_file='generators/scripts/cry.setup', data_taking_period='gcr2017', top_in_counter=False)
Definition: generators.py:825
def add_simulation(path, components=None, bkgfiles=None, bkgOverlay=True, forceSetPXDDataReduction=False, usePXDDataReduction=True, cleanupPXDDataReduction=True, generate_2nd_cdc_hits=False, simulateT0jitter=True, isCosmics=False, FilterEvents=False, usePXDGatedMode=False, skipExperimentCheckForBG=False, save_slow_pions_in_mc=False)
Definition: simulation.py:126