ATLAS Offline Software
Loading...
Searching...
No Matches
TrackingAnalysisConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3# AnaAlgorithm import(s):
4from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
5from AthenaCommon.SystemOfUnits import GeV
6from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType
7from AthenaConfiguration.Enums import LHCPeriod
8from Campaigns.Utils import Campaign
9from AthenaCommon.Logging import logging
10
11
12class InDetTrackCalibrationConfig (ConfigBlock):
13 """the ConfigBlock for the track impact parameter correction"""
14
15 def __init__ (self) :
16 super (InDetTrackCalibrationConfig, self).__init__ ()
17 self.setBlockName ('InDetTracks')
18 self.addOption ('inputContainer', '', type=str,
19 info="the name of the input track container.")
20 self.addOption ('containerName', '', type=str,
21 noneAction='error',
22 info="the name of the output container after calibration.")
23 self.addOption ('postfix', '', type=str,
24 info="a postfix to apply to decorations and algorithm names. Typically "
25 "not needed here since the calibration is common to all tracks.")
26 self.addOption ('runBiasing', True, type=bool,
27 info="whether to run the `InDetTrackBiasingTool`. Allows the user to "
28 "disable the tool if no recommendations are available. This should "
29 "not be used in an analysis.")
30 self.addOption ('biasD0', None, type=float,
31 info="a manual bias to $d_0$ in mm. Will be applied by the "
32 "`InDetTrackBiasingTool`. Expert option in addition to the "
33 "recommendations.",
34 expertMode=True)
35 self.addOption ('biasZ0', None, type=float,
36 info="a manual bias to $z_0$ in mm. Will be applied by the "
37 "`InDetTrackBiasingTool`. Expert option in addition to the "
38 "recommendations.",
39 expertMode=True)
40 self.addOption ('biasQoverPsagitta', None, type=float,
41 info="a manual bias to $Q/p$ in TeV$^{-1}$. Will be applied "
42 "by the `InDetTrackBiasingTool`. Expert option in addition to the "
43 "recommendations.",
44 expertMode=True)
45 self.addOption ('customRunNumber', None, type=int,
46 info="manually sets the `runNumber` in the `InDetTrackBiasingTool`. "
47 "Expert option leads to use of different recommendations. Default is "
48 "retrieved from `EventInfo`.",
49 expertMode=True)
50 self.addOption ('calibFile', None, type=str,
51 info="name of the calibration file to use for the CTIDE "
52 "calibration. Expert option to override the recommendations "
53 "based on the campaign.",
54 expertMode=True)
55 self.addOption ('smearingToolSeed', None, type=int,
56 info="random seed to be used by the `InDetTrackSmearingTool`.",
57 expertMode=True)
58 self.addOption ('minPt', 0.5*GeV, type=float,
59 info=r"the minimum $p_\mathrm{T}$ cut to apply to calibrated tracks.")
60 self.addOption ('maxEta', 2.5, type=float,
61 info=r"maximum track $\vert\eta\vert$.")
62 self.addOption ('outputTrackSummaryInfo', False, type=bool,
63 info="decorate track summary information on the reconstructed objects.")
64
65 def instanceName (self) :
66 """Return the instance name for this block"""
67 return self.containerName + self.postfix
68
69 @staticmethod
71 alg,
72 biasD0 : float=None,
73 biasZ0 : float=None,
74 biasQoverPsagitta : float=None,
75 customRunNumber : int=None) :
76 toolName = "biasingTool"
77 config.addPrivateTool(toolName, "InDet::InDetTrackBiasingTool")
78 if config.geometry() is LHCPeriod.Run3:
79 raise ValueError ('Recommendations are not yet available in Run 3.')
80 elif config.geometry() is not LHCPeriod.Run2:
81 raise ValueError ('No recommendations found for geometry \"'
82 + config.geometry().value + '\". Please check '
83 'the configuration.')
84 if biasD0:
85 alg.biasingTool.biasD0 = biasD0
86 if biasZ0:
87 alg.biasingTool.biasZ0 = biasZ0
88 if biasQoverPsagitta:
89 alg.biasingTool.biasQoverPsagitta = biasQoverPsagitta
90 if customRunNumber:
91 alg.biasingTool.runNumber = customRunNumber
92 pass
93
94 @staticmethod
96 alg,
97 seed : int=None,
98 calibFile : str=None) :
99 toolName = "smearingTool"
100 config.addPrivateTool(toolName, "InDet::InDetTrackSmearingTool")
101 if seed:
102 alg.smearingTool.Seed = seed
103 if calibFile:
104 alg.tackSmearingTool.calibFileIP_CTIDE = calibFile
105 else:
106 if config.geometry() is LHCPeriod.Run2:
107 # Run 2 recommendations (MC20)
108 alg.smearingTool.calibFileIP_CTIDE = "InDetTrackSystematicsTools/CalibData_22.0_2022-v00/d0z0_smearing_factors_Run2_v2.root"
109 elif config.geometry() is LHCPeriod.Run3:
110 if config.campaign() is Campaign.MC23a:
111 # 2022 recommendations (MC23a)
112 alg.smearingTool.calibFileIP_CTIDE = "InDetTrackSystematicsTools/CalibData_25.2_2025-v00/2022_d0z0_smearing_factors_v2.root"
113 elif config.campaign() is Campaign.MC23d:
114 # 2023 recommendations (MC23d)
115 alg.smearingTool.calibFileIP_CTIDE = "InDetTrackSystematicsTools/CalibData_25.2_2025-v00/2023_d0z0_smearing_factors_v2.root"
116 elif config.campaign() is Campaign.MC23e:
117 # 2024 recommendations (MC23e)
118 alg.smearingTool.calibFileIP_CTIDE = "InDetTrackSystematicsTools/CalibData_25.2_2025-v00/2024_d0z0_smearing_factors.root"
119 else:
120 raise ValueError ('No recommendations found for campaign \"'
121 + config.campaign().value + '\" in Run 3. '
122 'Please check that the recommendations exist.')
123 else:
124 raise ValueError ('No recommendations found for geometry \"'
125 + config.geometry().value + '\". Please check '
126 'the configuration.')
127 pass
128
129 def makeAlgs (self, config) :
130 log = logging.getLogger('InDetTrackCalibrationConfig')
131
132 inputContainer = "InDetTrackParticles"
134 inputContainer = self.inputContainer
135 config.setSourceName (self.containerName, inputContainer)
136
137 # Set up a shallow copy to decorate
138 if config.wantCopy (self.containerName) :
139 alg = config.createAlgorithm( 'CP::AsgShallowCopyAlg', 'InDetTrackShallowCopyAlg' )
140 alg.input = config.readName (self.containerName)
141 alg.output = config.copyName (self.containerName)
142
143 # Set up the eta-cut on all tracks prior to everything else
144 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'InDetTrackEtaCutAlg' )
145 alg.selectionDecoration = 'selectEta' + self.postfix + ',as_bits'
146 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
147 alg.selectionTool.maxEta = self.maxEta
148 alg.particles = config.readName (self.containerName)
149 alg.preselection = config.getPreselection (self.containerName, '')
150 config.addSelection (self.containerName, '', alg.selectionDecoration)
151
152 # Set up the biasing algorithm. The recommendations is to bias MC instead of unbiasing Data:
153 if config.dataType() is not DataType.Data:
154 if not self.runBiasing:
155 log.warning('Disabling the biasing tool for now. This should not '
156 'be used in an analysis.')
157 else:
158 alg = config.createAlgorithm( 'CP::InDetTrackBiasingAlg', 'InDetTrackBiasingAlg' )
159 self.makeTrackBiasingTool(config,
160 alg,
161 self.biasD0,
162 self.biasZ0,
165 alg.inDetTracks = config.readName (self.containerName)
166 alg.inDetTracksOut = config.copyName (self.containerName)
167 alg.preselection = config.getPreselection (self.containerName, '')
168
169 # Set up the smearing algorithm:
170 if config.dataType() is not DataType.Data:
171 alg = config.createAlgorithm( 'CP::InDetTrackSmearingAlg', 'InDetTrackSmearingAlg' )
172 self.makeTrackSmearingTool(config,
173 alg,
176 alg.inDetTracks = config.readName (self.containerName)
177 alg.inDetTracksOut = config.copyName (self.containerName)
178 alg.preselection = config.getPreselection (self.containerName, '')
179
180 if self.minPt > 0 : # Set up the the pt selection
181 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'InDetTrackPtCutAlg' )
182 alg.selectionDecoration = 'selectPt' + self.postfix + ',as_bits'
183 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
184 alg.selectionTool.minPt = self.minPt
185 alg.particles = config.readName (self.containerName)
186 alg.preselection = config.getPreselection (self.containerName, '')
187 config.addSelection (self.containerName, '', alg.selectionDecoration,
188 preselection=True)
189
190 # Multiple variables are not included in the SmartCollection (momentum, eta, charge)
191 alg = config.createAlgorithm( 'CP::InDetTrackExtraVarDecoratorAlg', 'ExtraVarDecorator' )
192 alg.inDetTracks = config.readName(self.containerName)
193
194 config.addOutputVar (self.containerName, 'pt_%SYS%', 'pt')
195 config.addOutputVar (self.containerName, 'eta_%SYS%', 'eta', noSys=True)
196 config.addOutputVar (self.containerName, 'phi', 'phi', noSys=True)
197 config.addOutputVar (self.containerName, 'charge_%SYS%', 'charge', noSys=True)
198 config.addOutputVar (self.containerName, 'qOverP', 'qOverP')
199 config.addOutputVar (self.containerName, 'd0', 'd0')
200 config.addOutputVar (self.containerName, 'z0', 'z0')
201 config.addOutputVar (self.containerName, 'vz', 'vz', noSys=True)
202
203 # decorate track summary information on the reconstructed object:
205 config.addOutputVar (self.containerName, 'numberOfInnermostPixelLayerHits', 'numberOfInnermostPixelLayerHits', noSys=True)
206 config.addOutputVar (self.containerName, 'numberOfPixelDeadSensors', 'numberOfPixelDeadSensors', noSys=True)
207 config.addOutputVar (self.containerName, 'numberOfPixelHits', 'numberOfPixelHits', noSys=True)
208 config.addOutputVar (self.containerName, 'numberOfPixelHoles', 'numberOfPixelHoles', noSys=True)
209 config.addOutputVar (self.containerName, 'numberOfPixelSharedHits', 'numberOfPixelSharedHits', noSys=True)
210 config.addOutputVar (self.containerName, 'numberOfSCTDeadSensors', 'numberOfSCTDeadSensors', noSys=True)
211 config.addOutputVar (self.containerName, 'numberOfSCTHits', 'numberOfSCTHits', noSys=True)
212 config.addOutputVar (self.containerName, 'numberOfSCTHoles', 'numberOfSCTHoles', noSys=True)
213 config.addOutputVar (self.containerName, 'numberOfSCTSharedHits', 'numberOfSCTSharedHits', noSys=True)
214 config.addOutputVar (self.containerName, 'numberOfTRTHits', 'numberOfTRTHits', noSys=True)
215 config.addOutputVar (self.containerName, 'numberOfTRTOutliers', 'numberOfTRTOutliers', noSys=True)
216
217
219 """the ConfigBlock for the track working point"""
220
221 def __init__ (self) :
222 super (InDetTrackWorkingPointConfig, self).__init__ ()
223 self.addOption ('containerName', '', type=str,
224 noneAction='error',
225 info="the name of the input container.")
226 self.addOption ('selectionName', '', type=str,
227 noneAction='error',
228 info="the name of the track selection to define (e.g. `tightPrimary` "
229 "or `loose`).")
230 self.addOption ('postfix', None, type=str,
231 info="a postfix to apply to decorations and algorithm names. "
232 "Typically not needed here as `selectionName` is used internally.")
233 self.addOption ('cutLevel', None, type=str,
234 noneAction='error',
235 info="the selection WP to use. Supported WPs for general "
236 "use: `Loose` and `TightPrimary`. For expert studies, further "
237 "WPs are available: `NoCut`, `LoosePrimary`, `LooseElectron`, "
238 "`LooseMuon`, `LooseTau`, `MinBias`, `HILoose`, `HITight`, "
239 "`HILooseOptimized`, `HITightOptimized`.",
240 expertMode=["NoCut", "LoosePrimary", "LooseElectron",
241 "LooseMuon", "LooseTau", "MinBias", "HILoose", "HITight",
242 "HILooseOptimized", "HITightOptimized"])
243 self.addOption ('additionalCuts', None, type=None,
244 info="additional cuts to modify the selection WP. Only meant for "
245 "expert studies of track selection. Passed as pairs of `cutName: value`. "
246 "For an overview of available cuts, see twiki.cern.ch/twiki/bin/viewauth/"
247 "AtlasProtected/InDetTrackSelectionTool#List_of_possible_cuts.",
248 expertMode=True)
249 self.addOption ('runTruthFilter', True, type=bool,
250 info="whether to run the `TruthFilterTool`. This tool is only compatible "
251 "with the cut levels `Loose` and `TightPrimary`.")
252 self.addOption ('calibFile', None, type=str,
253 info="name of the calibration file to use for efficiencies "
254 "in the `TruthFilter` tool. Expert option to override the "
255 "recommendations based on the campaign.",
256 expertMode=True)
257 self.addOption ('filterToolSeed', None, type=int,
258 info="random seed to be used by the `InDetTrackTruthFilterTool`.",
259 expertMode=True)
260 self.addOption ('fFakeLoose', None, type=float,
261 info="the fraction of fake tracks in the `Loose` working point. "
262 "Will be used by the `InDetTrackTruthFilterTool`. Expert option to "
263 "override the recommendations.",
264 expertMode=True)
265 self.addOption ('fFakeTight', None, type=float,
266 info="the fraction of fake tracks in the `TightPrimary` working "
267 "point. Will be used by the `InDetTrackTruthFilterTool`. Expert option "
268 "to override the recommendations.",
269 expertMode=True)
270 self.addOption ('trkEffSystScale', None, type=float,
271 info="the track efficiency systematic scale. Will be used "
272 "by the `InDetTrackTruthFilterTool`. Expert option to override the "
273 "recommendations.",
274 expertMode=True)
275 self.addOption ('addSelectionToPreselection', True, type=bool,
276 info="whether to retain only tracks satisfying the `cutLevel` "
277 "requirements.")
278
279 def instanceName (self) :
280 """Return the instance name for this block"""
281 if self.postfix is not None:
282 return self.containerName + self.selectionName + self.postfix
283 else:
284 return self.containerName + self.selectionName
285
286 def makeAlgs (self, config) :
287 log = logging.getLogger('InDetTrackWorkingPointConfig')
288
289 selectionPostfix = self.selectionName
290 if selectionPostfix != '' and selectionPostfix[0] != '_' :
291 selectionPostfix = '_' + selectionPostfix
292
293 postfix = self.postfix
294 if postfix is None :
295 postfix = self.selectionName
296 if postfix != '' and postfix[0] != '_' :
297 postfix = '_' + postfix
298
299 cutLevels = ["NoCut", "Loose", "LoosePrimary", "TightPrimary", "LooseMuon",
300 "LooseElectron", "LooseTau", "MinBias", "HILoose", "HITight",
301 "HILooseOptimized", "HITightOptimized"]
302 alg = config.createAlgorithm( 'CP::InDetTrackSelectionAlg', 'InDetTrackSelectionAlg' )
303 alg.selectionDecoration = 'selectTrack' + postfix + '_%SYS%,as_bits'
304 config.addPrivateTool( 'selectionTool', 'InDet::InDetTrackSelectionTool')
305 if self.cutLevel is None:
306 log.warning("No selection WP chosen, not setting up InDetTrackSelectionTool.")
307 elif self.cutLevel not in cutLevels:
308 raise ValueError ('Invalid cut level: \"' + self.cutLevel + '\", has '
309 'to be one of: ' + ', '.join(cutLevels))
310 elif self.cutLevel in ["Loose", "TightPrimary"]:
311 alg.selectionTool.CutLevel = self.cutLevel
312 else:
313 log.warning('Using cut level: \"' + self.cutLevel + '\" that is not '
314 'meant for general use, but only expert studies.')
315 alg.selectionTool.CutLevel = self.cutLevel
317 for cutName, value in self.additionalCuts.items():
318 setattr(alg.selectionTool, cutName, value)
319 # Set up the truth filtering algorithm:
320 if config.dataType() is not DataType.Data:
321 if not self.runTruthFilter:
322 log.warning('Disabling the TruthFilterTool.')
323 else:
324 config.addPrivateTool( 'filterTool', 'InDet::InDetTrackTruthFilterTool' )
325 config.addPrivateTool( 'filterTool.trackOriginTool', 'InDet::InDetTrackTruthOriginTool' )
326 # Set working point based on cut level
327 if self.cutLevel == "Loose":
328 alg.filterWP = "LOOSE"
329 elif self.cutLevel == "TightPrimary":
330 alg.filterWP = "TIGHT"
331 else:
332 raise ValueError ('Attempting to set TruthFilter WP based on cut level: \"'
333 + self.efficiencyWP + '\" that is not supported.')
334 # Set calibFile and fake rates based on campaign
335 if config.geometry() is LHCPeriod.Run2:
336 # Run 2 recommendations (MC20)
337 alg.filterTool.calibFileNomEff = "InDetTrackSystematicsTools/CalibData_22.0_2022-v00/TrackingRecommendations_prelim_rel22.root"
338 alg.filterTool.fFakeLoose = 0.10
339 alg.filterTool.fFakeTight = 1.00
340 elif config.geometry() is LHCPeriod.Run3:
341 if config.campaign() in [Campaign.MC23a, Campaign.MC23d, Campaign.MC23e]:
342 # 2022/23/24 recommendations (MC23a/d/e)
343 alg.filterTool.calibFileNomEff = "InDetTrackSystematicsTools/CalibData_22.0_2022-v00/TrackingRecommendations_prelim_rel22.root"
344 alg.filterTool.fFakeLoose = 0.40
345 alg.filterTool.fFakeTight = 1.00
346 elif not (self.calibFile and self.fFakeLoose and self.fFakeTight):
347 raise ValueError ('No efficiency recommendations found for campaign \"'
348 + config.campaign().value + '\" in Run 3. '
349 'Please check that the recommendations exist.')
350 elif not (self.calibFile and self.fFakeLoose and self.fFakeTight):
351 raise ValueError ('No efficiency recommendations found for geometry \"'
352 + config.geometry().value + '\". Please check '
353 'the configuration.')
354 # Set custom calibFile, fake rates, or random seed
355 if self.calibFile:
356 alg.filterTool.calibFileNomEff = self.calibFile
357 if self.fFakeLoose:
358 alg.filterTool.fFakeLoose = self.fFakeLoose
359 if self.fFakeTight:
360 alg.filterTool.fFakeTight = self.fFakeTight
362 alg.filterTool.Seed = self.filterToolSeed
364 alg.filterTool.trkEffSystScale = self.trkEffSystScale
365 alg.inDetTracks = config.readName (self.containerName)
366 alg.preselection = config.getPreselection (self.containerName, '')
367 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
368 preselection=self.addSelectionToPreselection)
makeTrackBiasingTool(config, alg, float biasD0=None, float biasZ0=None, float biasQoverPsagitta=None, int customRunNumber=None)
makeTrackSmearingTool(config, alg, int seed=None, str calibFile=None)