Belle II Software development
simulation.py
1#!/usr/bin/env python3
2
3
10
11import basf2 as b2
12from geometry import check_components
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 save_slow_pions_in_mc=False,
138 save_all_charged_particles_in_mc=False):
139 """
140 This function adds the standard simulation modules to a path.
141 @param forceSetPXDDataReduction: override settings from the DB with the value set in 'usePXDDataReduction'
142 @param usePXDDataReduction: if 'forceSetPXDDataReduction==True', override settings from the DB
143 @param cleanupPXDDataReduction: if True the datastore objects used by PXDDataReduction are emptied
144 @param simulateT0jitter: if True simulate L1 trigger jitter
145 @param isCosmics: if True the filling pattern is removed from L1 jitter simulation
146 @param FilterEvents: if True only the events that pass the L1 trigger will survive simulation, the other are discarded.
147 Make sure you do need to filter events before you set the value to True.
148 @param skipExperimentCheckForBG: If True, skip the check on the experiment number consistency between the basf2
149 process and the beam background files. Note that this check should be skipped only by experts.
150 @param save_slow_pions_in_mc: if True, additional Regions of Interest on the PXD are created to save the PXDDigits
151 of slow pions from D* -> D pi^{\\pm} decays using the MCSlowPionPXDROICreator based on MC truth information
152 @param save_all_charged_particles_in_mc: if True, additional Regions of Interest on the PXD are created to save the PXDDigits
153 of all charged primary MCParticles that traverse PXD
154 """
155
156 path.add_module('StatisticsSummary').set_name('Sum_PreSimulation')
157
158 # Check compoments.
159 check_components(components)
160
161 # background mixing or overlay input before process forking
162 if bkgfiles is not None:
163 if bkgOverlay:
164 bkginput = b2.register_module('BGOverlayInput')
165 bkginput.param('inputFileNames', bkgfiles)
166 bkginput.param('skipExperimentCheck', skipExperimentCheckForBG)
167 path.add_module(bkginput)
168 else:
169 bkgmixer = b2.register_module('BeamBkgMixer')
170 bkgmixer.param('backgroundFiles', bkgfiles)
171 if components:
172 bkgmixer.param('components', components)
173 path.add_module(bkgmixer)
174 if usePXDGatedMode:
175 if components is None or 'PXD' in components:
176 # PXD is sensitive to hits in intervall -20us to +20us
177 bkgmixer.param('minTimePXD', -20000.0)
178 bkgmixer.param('maxTimePXD', 20000.0)
179 # Emulate injection vetos for PXD
180 pxd_veto_emulator = b2.register_module('PXDInjectionVetoEmulator')
181 path.add_module(pxd_veto_emulator)
182
183 # geometry parameter database
184 if 'Gearbox' not in path:
185 gearbox = b2.register_module('Gearbox')
186 path.add_module(gearbox)
187
188 # detector geometry
189 if 'Geometry' not in path:
190 path.add_module('Geometry', useDB=True)
191 if components is not None:
192 b2.B2WARNING("Custom detector components specified: Will still build full geometry")
193
194 # event T0 jitter simulation
195 if simulateT0jitter and 'EventT0Generator' not in path:
196 eventt0 = b2.register_module('EventT0Generator')
197 eventt0.param("isCosmics", isCosmics)
198 path.add_module(eventt0)
199
200 # create EventLevelTriggerTimeInfo if it doesn't exist in BG Overlay
201 if 'SimulateEventLevelTriggerTimeInfo' not in path:
202 eventleveltriggertimeinfo = b2.register_module('SimulateEventLevelTriggerTimeInfo')
203 path.add_module(eventleveltriggertimeinfo)
204
205 # detector simulation
206 if 'FullSim' not in path:
207 g4sim = b2.register_module('FullSim')
208 path.add_module(g4sim)
209
210 check_simulation(path)
211
212 # no checks are performed for BeamBkgMixer and the Digitizers as they are
213 # not necessary for running simulation jobs and it should be possible to
214 # have them in the path more than once
215
216 # CDC digitization
217 if components is None or 'CDC' in components:
218 cdc_digitizer = b2.register_module('CDCDigitizer')
219 cdc_digitizer.param("Output2ndHit", generate_2nd_cdc_hits)
220 path.add_module(cdc_digitizer)
221
222 # TOP digitization
223 if components is None or 'TOP' in components:
224 top_digitizer = b2.register_module('TOPDigitizer')
225 path.add_module(top_digitizer)
226
227 # ARICH digitization
228 if components is None or 'ARICH' in components:
229 arich_digitizer = b2.register_module('ARICHDigitizer')
230 path.add_module(arich_digitizer)
231
232 # ECL digitization
233 if components is None or 'ECL' in components:
234 ecl_digitizer = b2.register_module('ECLDigitizer')
235 if bkgfiles is not None:
236 ecl_digitizer.param('Background', 1)
237 path.add_module(ecl_digitizer)
238
239 # KLM digitization
240 if components is None or 'KLM' in components:
241 klm_digitizer = b2.register_module('KLMDigitizer')
242 path.add_module(klm_digitizer)
243
244 # BG Overlay for CDC, TOP, ARICH and KLM (for ECL it's done in ECLDigitizer)
245 if bkgfiles is not None and bkgOverlay:
246 m = path.add_module('BGOverlayExecutor', components=['CDC', 'TOP', 'ARICH', 'KLM'])
247 m.set_name('BGOverlayExecutor_CDC...KLM')
248
249 if components is None or 'TRG' in components:
250 add_trigger_simulation(path, simulateT0jitter=simulateT0jitter, FilterEvents=FilterEvents)
251
252 # SVD digitization, BG Overlay, sorting and zero suppression
253 if components is None or 'SVD' in components:
254 add_svd_simulation(path)
255 if bkgfiles is not None and bkgOverlay:
256 m = path.add_module('BGOverlayExecutor', components=['SVD'])
257 m.set_name('BGOverlayExecutor_SVD')
258 path.add_module('SVDShaperDigitSorter')
259 path.add_module('SVDZeroSuppressionEmulator')
260
261 # PXD digitization, BG overlay, sorting and data reduction
262 if components is None or 'PXD' in components:
263 if forceSetPXDDataReduction:
264 pxd_digits_name = ''
265 if usePXDDataReduction:
266 pxd_digits_name = 'pxd_unfiltered_digits'
267 add_pxd_simulation(path, digitsName=pxd_digits_name)
268 if bkgfiles is not None and bkgOverlay:
269 m = path.add_module('BGOverlayExecutor', components=['PXD'], PXDDigitsName=pxd_digits_name)
270 m.set_name('BGOverlayExecutor_PXD')
271 path.add_module('PXDDigitSorter', digits=pxd_digits_name)
272 if usePXDDataReduction:
273 add_PXDDataReduction(
274 path,
275 components,
276 pxd_digits_name,
277 doCleanup=cleanupPXDDataReduction,
278 overrideDB=forceSetPXDDataReduction,
279 usePXDDataReduction=usePXDDataReduction,
280 save_slow_pions_in_mc=save_slow_pions_in_mc,
281 save_all_charged_particles_in_mc=save_all_charged_particles_in_mc)
282 else:
283 # use DB conditional module to decide whether ROI finding should be activated
284 path_disableROI_Sim = b2.create_path()
285 add_pxd_simulation(path_disableROI_Sim, digitsName='PXDDigits')
286 if bkgfiles is not None and bkgOverlay:
287 m = path_disableROI_Sim.add_module('BGOverlayExecutor', components=['PXD'], PXDDigitsName='PXDDigits')
288 m.set_name('BGOverlayExecutor_PXD')
289 path_disableROI_Sim.add_module('PXDDigitSorter', digits='PXDDigits')
290
291 path_enableROI_Sim = b2.create_path()
292 add_pxd_simulation(path_enableROI_Sim, digitsName='pxd_unfiltered_digits')
293 if bkgfiles is not None and bkgOverlay:
294 m = path_enableROI_Sim.add_module('BGOverlayExecutor', components=['PXD'], PXDDigitsName='pxd_unfiltered_digits')
295 m.set_name('BGOverlayExecutor_PXD')
296 path_enableROI_Sim.add_module('PXDDigitSorter', digits='pxd_unfiltered_digits')
297 add_PXDDataReduction(
298 path_enableROI_Sim,
299 components,
300 pxd_unfiltered_digits='pxd_unfiltered_digits',
301 doCleanup=cleanupPXDDataReduction,
302 save_slow_pions_in_mc=save_slow_pions_in_mc,
303 save_all_charged_particles_in_mc=save_all_charged_particles_in_mc)
304
305 roi_condition_module_Sim = path.add_module('ROIfindingConditionFromDB')
306 roi_condition_module_Sim.if_true(path_enableROI_Sim, b2.AfterConditionPath.CONTINUE)
307 roi_condition_module_Sim.if_false(path_disableROI_Sim, b2.AfterConditionPath.CONTINUE)
308
309 # statistics summary
310 path.add_module('StatisticsSummary').set_name('Sum_Simulation')