Belle II Software development
simulation.py
1#!/usr/bin/env python3
2
3
10
11import basf2 as b2
12from geometry import check_components, is_detector_present
13from L1trigger import add_trigger_simulation
14from pxd import add_pxd_simulation
15from svd import add_svd_simulation
16from svd import add_svd_reconstruction
17from tracking import add_tracking_for_PXDDataReduction_simulation, add_roiFinder
18
19
20def check_simulation(path):
21 """Check if the minimum number of modules required for simulation are in
22 the path and in the correct order"""
23 required = ['Gearbox', 'Geometry', 'FullSim']
24 found = []
25
26 # make a list of all required modules in the path
27 for module in path.modules():
28 module_type = module.type()
29 # if required add to list of found modules
30 if module_type in required:
31 # unless it is already in there
32 if module_type in found:
33 b2.B2ERROR(f"Duplicate module in path: {module_type}")
34 else:
35 found.append(module.type())
36
37 if len(required) != len(found):
38 # Apparently at least one module is missing
39 for r in required:
40 if r not in found:
41 b2.B2ERROR(f"No '{r}' module found but needed for simulation")
42 # We have all modules but do they have the correct order?
43 elif required != found:
44 b2.B2ERROR(f"Simulation modules in wrong order. Should be '{', '.join(required)}' but is '{', '.join(found)}'")
45
46
47def add_PXDDataReduction(
48 path,
49 components,
50 pxd_unfiltered_digits='pxd_unfiltered_digits',
51 doCleanup=True,
52 overrideDB=False,
53 usePXDDataReduction=True,
54 save_slow_pions_in_mc=False,
55 save_all_charged_particles_in_mc=False):
56 """
57 This function adds the standard simulation modules to a path.
58 @param pxd_unfiltered_digits: the name of the StoreArray containing the input PXDDigits
59 @param overrideDB: override settings from the DB with the value set in 'usePXDDataReduction'
60 @param usePXDDataReduction: if 'overrideDB==True', override settings from the DB
61 @param doCleanup: if 'doCleanup=True' temporary datastore objects are emptied
62 @param save_slow_pions_in_mc: if True, additional Regions of Interest on the PXD are created to save the PXDDigits
63 of slow pions from D* -> D pi^{\\pm} decays using the MCSlowPionPXDROICreator based on MC truth information
64 """
65
66 # SVD reconstruction
67 # svd_cluster = '__ROIsvdClusters'
68 add_svd_reconstruction(path, isROIsimulation=True)
69
70 # SVD tracking
71 svd_reco_tracks = '__ROIsvdRecoTracks'
72
73 add_tracking_for_PXDDataReduction_simulation(path, components, svd_cluster='__ROIsvdClusters')
74
75 add_roiFinder(path, svd_reco_tracks)
76
77 if save_slow_pions_in_mc or save_all_charged_particles_in_mc:
78 path.add_module('MCPXDROICreator',
79 PXDDigitsName=pxd_unfiltered_digits,
80 ROIsName='ROIs',
81 createROIForSlowPionsOnly=save_slow_pions_in_mc,
82 createROIForAll=save_all_charged_particles_in_mc)
83
84 # Filtering of PXDDigits
85 pxd_digifilter = b2.register_module('PXDdigiFilter')
86 pxd_digifilter.param('ROIidsName', 'ROIs')
87 pxd_digifilter.param('PXDDigitsName', pxd_unfiltered_digits)
88 pxd_digifilter.param('PXDDigitsInsideROIName', 'PXDDigits')
89 pxd_digifilter.param('overrideDB', overrideDB)
90 pxd_digifilter.param('usePXDDataReduction', usePXDDataReduction) # only used for overrideDB=True
91 path.add_module(pxd_digifilter)
92
93 # empty the StoreArrays which were used for the PXDDatareduction as those are not needed anymore
94 if doCleanup:
95 StoreArrays_to_clean = ['ROIs', '__ROIsvdRecoDigits', '__ROIsvdClusters', '__ROIsvdRecoTracks',
96 'SPTrackCands__ROI', 'SpacePoints__ROI',
97 # till here it are StoreArrays, the following are relations and Datastore objects
98 'SegmentNetwork__ROI', 'PXDInterceptsToROIs',
99 'RecoHitInformationsTo__ROIsvdClusters',
100 'SpacePoints__ROITo__ROIsvdClusters', '__ROIsvdClustersToMCParticles',
101 '__ROIsvdRecoDigitsToMCParticles',
102 '__ROIsvdClustersTo__ROIsvdRecoDigits', '__ROIsvdClustersToSVDTrueHits',
103 '__ROIsvdClustersTo__ROIsvdRecoTracks', '__ROIsvdRecoTracksToPXDIntercepts',
104 '__ROIsvdRecoTracksToRecoHitInformations',
105 '__ROIsvdRecoTracksToSPTrackCands__ROI']
106
107 # not only prune the pxd_unfiltered_digits, but also their relations to
108 # MCParticles, PXDDigits (the filtered ones), and PXDTrueHits
109 # Only prune the unfiltered PXD if their name is not 'PXDDigits', otherwise all PXDDigits would be lost
110 if pxd_unfiltered_digits != 'PXDDigits':
111 unfiltered_pxd_digits_arrays = [pxd_unfiltered_digits,
112 f'{pxd_unfiltered_digits}ToMCParticles',
113 f'{pxd_unfiltered_digits}ToPXDDigits',
114 f'{pxd_unfiltered_digits}ToPXDTrueHits']
115 StoreArrays_to_clean += unfiltered_pxd_digits_arrays
116
117 datastore_cleaner = b2.register_module('PruneDataStore')
118 datastore_cleaner.param('keepMatchedEntries', False)
119 datastore_cleaner.param('matchEntries', StoreArrays_to_clean)
120 path.add_module(datastore_cleaner)
121
122
123def add_simulation(
124 path,
125 components=None,
126 bkgfiles=None,
127 bkgOverlay=True,
128 forceSetPXDDataReduction=False,
129 usePXDDataReduction=True,
130 cleanupPXDDataReduction=True,
131 generate_2nd_cdc_hits=False,
132 simulateT0jitter=True,
133 isCosmics=False,
134 FilterEvents=False,
135 usePXDGatedMode=False,
136 skipExperimentCheckForBG=False,
137 ignoreRunNumberForBG=False,
138 save_slow_pions_in_mc=False,
139 save_all_charged_particles_in_mc=False):
140 """
141 This function adds the standard simulation modules to a path.
142 @param forceSetPXDDataReduction: override settings from the DB with the value set in 'usePXDDataReduction'
143 @param usePXDDataReduction: if 'forceSetPXDDataReduction==True', override settings from the DB
144 @param cleanupPXDDataReduction: if True the datastore objects used by PXDDataReduction are emptied
145 @param simulateT0jitter: if True simulate L1 trigger jitter
146 @param isCosmics: if True the filling pattern is removed from L1 jitter simulation
147 @param FilterEvents: if True only the events that pass the L1 trigger will survive simulation, the other are discarded.
148 Make sure you do need to filter events before you set the value to True.
149 @param skipExperimentCheckForBG: If True, skip the check on the experiment number consistency between the basf2
150 process and the beam background files. Note that this check should be skipped only by experts.
151 @param ignoreRunNumberForBG: If True, skip the check on the run number consistency between the basf2
152 process and the beam background files. Note that this check should be skipped only by experts.
153 @param save_slow_pions_in_mc: if True, additional Regions of Interest on the PXD are created to save the PXDDigits
154 of slow pions from D* -> D pi^{\\pm} decays using the MCSlowPionPXDROICreator based on MC truth information
155 @param save_all_charged_particles_in_mc: if True, additional Regions of Interest on the PXD are created to save the PXDDigits
156 of all charged primary MCParticles that traverse PXD
157 """
158
159 path.add_module('StatisticsSummary').set_name('Sum_PreSimulation')
160
161 # Check components.
162 check_components(components)
163
164 # background mixing or overlay input before process forking
165 if bkgfiles is not None:
166 if bkgOverlay:
167 bkginput = b2.register_module('BGOverlayInput')
168 bkginput.param('inputFileNames', bkgfiles)
169 bkginput.param('skipExperimentCheck', skipExperimentCheckForBG)
170 bkginput.param('ignoreRunNumbers', ignoreRunNumberForBG)
171 path.add_module(bkginput)
172 else:
173 bkgmixer = b2.register_module('BeamBkgMixer')
174 bkgmixer.param('backgroundFiles', bkgfiles)
175 if components:
176 bkgmixer.param('components', components)
177 path.add_module(bkgmixer)
178 if usePXDGatedMode:
179 if is_detector_present("PXD", components):
180 # PXD is sensitive to hits in interval -20us to +20us
181 bkgmixer.param('minTimePXD', -20000.0)
182 bkgmixer.param('maxTimePXD', 20000.0)
183 # Emulate injection vetos for PXD
184 pxd_veto_emulator = b2.register_module('PXDInjectionVetoEmulator')
185 path.add_module(pxd_veto_emulator)
186
187 # geometry parameter database
188 if 'Gearbox' not in path:
189 gearbox = b2.register_module('Gearbox')
190 path.add_module(gearbox)
191
192 # detector geometry
193 if 'Geometry' not in path:
194 path.add_module('Geometry', useDB=True)
195 if components is not None:
196 b2.B2WARNING("Custom detector components specified: Will still build full geometry")
197
198 # event T0 jitter simulation
199 if simulateT0jitter and 'EventT0Generator' not in path:
200 eventt0 = b2.register_module('EventT0Generator')
201 eventt0.param("isCosmics", isCosmics)
202 path.add_module(eventt0)
203
204 # create EventLevelTriggerTimeInfo if it doesn't exist in BG Overlay
205 if 'SimulateEventLevelTriggerTimeInfo' not in path:
206 eventleveltriggertimeinfo = b2.register_module('SimulateEventLevelTriggerTimeInfo')
207 path.add_module(eventleveltriggertimeinfo)
208
209 # detector simulation
210 if 'FullSim' not in path:
211 g4sim = b2.register_module('FullSim')
212 path.add_module(g4sim)
213
214 check_simulation(path)
215
216 # no checks are performed for BeamBkgMixer and the Digitizers as they are
217 # not necessary for running simulation jobs and it should be possible to
218 # have them in the path more than once
219
220 # CDC digitization
221 if is_detector_present("CDC", components):
222 cdc_digitizer = b2.register_module('CDCDigitizer')
223 cdc_digitizer.param("Output2ndHit", generate_2nd_cdc_hits)
224 path.add_module(cdc_digitizer)
225
226 # TOP digitization
227 if is_detector_present("TOP", components):
228 top_digitizer = b2.register_module('TOPDigitizer')
229 path.add_module(top_digitizer)
230
231 # ARICH digitization
232 if is_detector_present("ARICH", components):
233 arich_digitizer = b2.register_module('ARICHDigitizer')
234 path.add_module(arich_digitizer)
235
236 # ECL digitization
237 if is_detector_present("ECL", components):
238 ecl_digitizer = b2.register_module('ECLDigitizer')
239 if bkgfiles is not None:
240 ecl_digitizer.param('Background', 1)
241 path.add_module(ecl_digitizer)
242
243 # KLM digitization
244 if is_detector_present("KLM", components):
245 klm_digitizer = b2.register_module('KLMDigitizer')
246 path.add_module(klm_digitizer)
247
248 # BG Overlay for CDC, TOP, ARICH and KLM (for ECL it's done in ECLDigitizer)
249 if bkgfiles is not None and bkgOverlay:
250 m = path.add_module('BGOverlayExecutor', components=['CDC', 'TOP', 'ARICH', 'KLM'])
251 m.set_name('BGOverlayExecutor_CDC...KLM')
252
253 if is_detector_present("TRG", components):
254 if bkgfiles is not None and bkgOverlay:
255 # BG Overlay for CDCTRG. That for KLMTRG is already covered by BG Overlay
256 # for KLM. ECL and ECLTRG are already covered independently.
257 m = path.add_module('BGOverlayExecutor', components=['CDC'], CDCHitsName='CDCHits4Trg')
258 m.set_name('BGOverlayExecutor_TRGCDC')
259 add_trigger_simulation(path, simulateT0jitter=simulateT0jitter, FilterEvents=FilterEvents)
260
261 # SVD digitization, BG Overlay, sorting and zero suppression
262 if is_detector_present("SVD", components):
263 add_svd_simulation(path)
264 if bkgfiles is not None and bkgOverlay:
265 m = path.add_module('BGOverlayExecutor', components=['SVD'])
266 m.set_name('BGOverlayExecutor_SVD')
267 path.add_module('SVDShaperDigitSorter')
268 path.add_module('SVDZeroSuppressionEmulator')
269
270 # PXD digitization, BG overlay, sorting and data reduction
271 if is_detector_present("PXD", components):
272 if forceSetPXDDataReduction:
273 pxd_digits_name = ''
274 if usePXDDataReduction:
275 pxd_digits_name = 'pxd_unfiltered_digits'
276 add_pxd_simulation(path, digitsName=pxd_digits_name)
277 if bkgfiles is not None and bkgOverlay:
278 m = path.add_module('BGOverlayExecutor', components=['PXD'], PXDDigitsName=pxd_digits_name)
279 m.set_name('BGOverlayExecutor_PXD')
280 path.add_module('PXDDigitSorter', digits=pxd_digits_name)
281 if usePXDDataReduction:
282 add_PXDDataReduction(
283 path,
284 components,
285 pxd_digits_name,
286 doCleanup=cleanupPXDDataReduction,
287 overrideDB=forceSetPXDDataReduction,
288 usePXDDataReduction=usePXDDataReduction,
289 save_slow_pions_in_mc=save_slow_pions_in_mc,
290 save_all_charged_particles_in_mc=save_all_charged_particles_in_mc)
291 else:
292 # use DB conditional module to decide whether ROI finding should be activated
293 path_disableROI_Sim = b2.create_path()
294 add_pxd_simulation(path_disableROI_Sim, digitsName='PXDDigits')
295 if bkgfiles is not None and bkgOverlay:
296 m = path_disableROI_Sim.add_module('BGOverlayExecutor', components=['PXD'], PXDDigitsName='PXDDigits')
297 m.set_name('BGOverlayExecutor_PXD')
298 path_disableROI_Sim.add_module('PXDDigitSorter', digits='PXDDigits')
299
300 path_enableROI_Sim = b2.create_path()
301 add_pxd_simulation(path_enableROI_Sim, digitsName='pxd_unfiltered_digits')
302 if bkgfiles is not None and bkgOverlay:
303 m = path_enableROI_Sim.add_module('BGOverlayExecutor', components=['PXD'], PXDDigitsName='pxd_unfiltered_digits')
304 m.set_name('BGOverlayExecutor_PXD')
305 path_enableROI_Sim.add_module('PXDDigitSorter', digits='pxd_unfiltered_digits')
306 add_PXDDataReduction(
307 path_enableROI_Sim,
308 components,
309 pxd_unfiltered_digits='pxd_unfiltered_digits',
310 doCleanup=cleanupPXDDataReduction,
311 save_slow_pions_in_mc=save_slow_pions_in_mc,
312 save_all_charged_particles_in_mc=save_all_charged_particles_in_mc)
313
314 roi_condition_module_Sim = path.add_module('ROIfindingConditionFromDB')
315 roi_condition_module_Sim.if_true(path_enableROI_Sim, b2.AfterConditionPath.CONTINUE)
316 roi_condition_module_Sim.if_false(path_disableROI_Sim, b2.AfterConditionPath.CONTINUE)
317
318 # statistics summary
319 path.add_module('StatisticsSummary').set_name('Sum_Simulation')