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
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']
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}")
35 found.append(module.type())
37 if len(required) != len(found):
38 # Apparently at least one module is missing
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)}'")
47def add_PXDDataReduction(
50 pxd_unfiltered_digits='pxd_unfiltered_digits',
53 usePXDDataReduction=True,
54 save_slow_pions_in_mc=False,
55 save_all_charged_particles_in_mc=False):
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
67 # svd_cluster = '__ROIsvdClusters'
68 add_svd_reconstruction(path, isROIsimulation=True)
71 svd_reco_tracks = '__ROIsvdRecoTracks'
73 add_tracking_for_PXDDataReduction_simulation(path, components, svd_cluster='__ROIsvdClusters')
75 add_roiFinder(path, svd_reco_tracks)
77 if save_slow_pions_in_mc or save_all_charged_particles_in_mc:
78 path.add_module('MCPXDROICreator',
79 PXDDigitsName=pxd_unfiltered_digits,
81 createROIForSlowPionsOnly=save_slow_pions_in_mc,
82 createROIForAll=save_all_charged_particles_in_mc)
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)
93 # empty the StoreArrays which were used for the PXDDatareduction as those are not needed anymore
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']
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
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)
128 forceSetPXDDataReduction=False,
129 usePXDDataReduction=True,
130 cleanupPXDDataReduction=True,
131 generate_2nd_cdc_hits=False,
132 simulateT0jitter=True,
135 usePXDGatedMode=False,
136 skipExperimentCheckForBG=False,
137 ignoreRunNumberForBG=False,
138 save_slow_pions_in_mc=False,
139 save_all_charged_particles_in_mc=False):
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
159 path.add_module('StatisticsSummary').set_name('Sum_PreSimulation')
162 check_components(components)
164 # background mixing or overlay input before process forking
165 if bkgfiles is not None:
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)
173 bkgmixer = b2.register_module('BeamBkgMixer')
174 bkgmixer.param('backgroundFiles', bkgfiles)
176 bkgmixer.param('components', components)
177 path.add_module(bkgmixer)
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)
187 # geometry parameter database
188 if 'Gearbox' not in path:
189 gearbox = b2.register_module('Gearbox')
190 path.add_module(gearbox)
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")
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)
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)
209 # detector simulation
210 if 'FullSim' not in path:
211 g4sim = b2.register_module('FullSim')
212 path.add_module(g4sim)
214 check_simulation(path)
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
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)
227 if is_detector_present("TOP", components):
228 top_digitizer = b2.register_module('TOPDigitizer')
229 path.add_module(top_digitizer)
232 if is_detector_present("ARICH", components):
233 arich_digitizer = b2.register_module('ARICHDigitizer')
234 path.add_module(arich_digitizer)
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)
244 if is_detector_present("KLM", components):
245 klm_digitizer = b2.register_module('KLMDigitizer')
246 path.add_module(klm_digitizer)
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')
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)
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')
270 # PXD digitization, BG overlay, sorting and data reduction
271 if is_detector_present("PXD", components):
272 if forceSetPXDDataReduction:
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(
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)
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')
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(
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)
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)
319 path.add_module('StatisticsSummary').set_name('Sum_Simulation')