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