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 ('applyD0Bias', True, type=bool,
46 info=r"whether to apply the $d_0$ bias from the calibration map in the "
47 "`InDetTrackBiasingTool`. Overrides the default set by the configuration.")
48 self.addOption ('applyZ0Bias', False, type=bool,
49 info=r"whether to apply the $z_0$ bias from the calibration map in the "
50 "`InDetTrackBiasingTool`. Overrides the default set by the configuration.")
51 self.addOption ('applyQoverPBias', False, type=bool,
52 info=r"whether to apply the $q/p$ sagitta bias from the calibration map in the "
53 "`InDetTrackBiasingTool`. Overrides the default set by the configuration.")
54 self.addOption ('customRunNumber', None, type=int,
55 info="manually sets the `runNumber` in the `InDetTrackBiasingTool`. "
56 "Expert option leads to use of different recommendations. Default is "
57 "retrieved from `EventInfo`.",
58 expertMode=True)
59 self.addOption ('calibFile', None, type=str,
60 info="name of the calibration file to use for the CTIDE "
61 "calibration. Expert option to override the recommendations "
62 "based on the campaign.",
63 expertMode=True)
64 self.addOption ('minPt', 0.5*GeV, type=float,
65 info=r"the minimum $p_\mathrm{T}$ cut (in MeV) to apply to calibrated tracks.")
66 self.addOption ('maxEta', 2.5, type=float,
67 info=r"maximum track $\vert\eta\vert$.")
68 self.addOption ('outputTrackSummaryInfo', False, type=bool,
69 info="decorate track summary information on the reconstructed objects.")
70
71 def instanceName (self) :
72 """Return the instance name for this block"""
73 return self.containerName + self.postfix
74
75 @staticmethod
77 alg,
78 biasD0 : float=None,
79 biasZ0 : float=None,
80 biasQoverPsagitta : float=None,
81 customRunNumber : int=None,
82 applyD0Bias : bool=True,
83 applyZ0Bias : bool=False,
84 applyQoverPBias : bool=False) :
85 from InDetTrackSystematicsTools.InDetTrackSystematicsToolsConfig import (
86 InDetTrackBiasingCalibKwargs,
87 )
88 toolName = "biasingTool"
89 config.addPrivateTool(toolName, "InDet::InDetTrackBiasingTool")
90
91 calib = InDetTrackBiasingCalibKwargs(config.flags)
92 alg.biasingTool.calibFiles = calib['calibFiles']
93 if 'runNumberBounds' in calib:
94 alg.biasingTool.runNumberBounds = calib['runNumberBounds']
95
96 if biasD0:
97 alg.biasingTool.biasD0 = biasD0
98 if biasZ0:
99 alg.biasingTool.biasZ0 = biasZ0
100 if biasQoverPsagitta:
101 alg.biasingTool.biasQoverPsagitta = biasQoverPsagitta
102 if customRunNumber:
103 alg.biasingTool.runNumber = customRunNumber
104 # By default only the d0 bias is applied; z0 and q/p biasing can be enabled
105 # via the applyZ0Bias / applyQoverPBias options once those maps are validated.
106 alg.biasingTool.applyD0Bias = applyD0Bias
107 alg.biasingTool.applyZ0Bias = applyZ0Bias
108 alg.biasingTool.applyQoverPBias = applyQoverPBias
109 alg.biasingTool.isMC = config.dataType() is not DataType.Data
110 pass
111
112 @staticmethod
114 alg,
115 calibFile : str=None) :
116 toolName = "smearingTool"
117 config.addPrivateTool(toolName, "InDet::InDetTrackSmearingTool")
118 if calibFile:
119 alg.tackSmearingTool.calibFileIP_CTIDE = calibFile
120 else:
121 if config.geometry() is LHCPeriod.Run2:
122 # Run 2 recommendations (MC20)
123 alg.smearingTool.calibFileIP_CTIDE = "InDetTrackSystematicsTools/CalibData_22.0_2022-v00/d0z0_smearing_factors_Run2_v2.root"
124 elif config.geometry() is LHCPeriod.Run3:
125 if config.campaign() is Campaign.MC23a:
126 # 2022 recommendations (MC23a)
127 alg.smearingTool.calibFileIP_CTIDE = "InDetTrackSystematicsTools/CalibData_25.2_2025-v00/2022_d0z0_smearing_factors_v2.root"
128 elif config.campaign() is Campaign.MC23d:
129 # 2023 recommendations (MC23d)
130 alg.smearingTool.calibFileIP_CTIDE = "InDetTrackSystematicsTools/CalibData_25.2_2025-v00/2023_d0z0_smearing_factors_v2.root"
131 elif config.campaign() is Campaign.MC23e:
132 # 2024 recommendations (MC23e)
133 alg.smearingTool.calibFileIP_CTIDE = "InDetTrackSystematicsTools/CalibData_25.2_2025-v00/2024_d0z0_smearing_factors.root"
134 else:
135 raise ValueError ('No recommendations found for campaign \"'
136 + config.campaign().value + '\" in Run 3. '
137 'Please check that the recommendations exist.')
138 else:
139 raise ValueError ('No recommendations found for geometry \"'
140 + config.geometry().value + '\". Please check '
141 'the configuration.')
142 pass
143
144 def makeAlgs (self, config) :
145 log = logging.getLogger('InDetTrackCalibrationConfig')
146
147 inputContainer = "InDetTrackParticles"
149 inputContainer = self.inputContainer
150 config.setSourceName (self.containerName, inputContainer)
151
152 # Set up a shallow copy to decorate
153 if config.wantCopy (self.containerName) :
154 alg = config.createAlgorithm( 'CP::AsgShallowCopyAlg', 'InDetTrackShallowCopyAlg' )
155 alg.input = config.readName (self.containerName)
156 alg.output = config.copyName (self.containerName)
157
158 # Set up the eta-cut on all tracks prior to everything else
159 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'InDetTrackEtaCutAlg' )
160 alg.selectionDecoration = 'selectEta' + self.postfix + ',as_bits'
161 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
162 alg.selectionTool.maxEta = self.maxEta
163 alg.particles = config.readName (self.containerName)
164 alg.preselection = config.getPreselection (self.containerName, '')
165 config.addSelection (self.containerName, '', alg.selectionDecoration)
166
167 # Set up the biasing algorithm. The recommendations is to bias MC instead of unbiasing Data:
168 if config.dataType() is not DataType.Data:
169 if not self.runBiasing:
170 log.warning('Disabling the biasing tool for now. This should not '
171 'be used in an analysis.')
172 else:
173 alg = config.createAlgorithm( 'CP::InDetTrackBiasingAlg', 'InDetTrackBiasingAlg' )
174 self.makeTrackBiasingTool(config,
175 alg,
176 self.biasD0,
177 self.biasZ0,
183 alg.inDetTracks = config.readName (self.containerName)
184 alg.inDetTracksOut = config.copyName (self.containerName)
185 alg.preselection = config.getPreselection (self.containerName, '')
186
187 # Set up the smearing algorithm:
188 if config.dataType() is not DataType.Data:
189 alg = config.createAlgorithm( 'CP::InDetTrackSmearingAlg', 'InDetTrackSmearingAlg' )
190 self.makeTrackSmearingTool(config,
191 alg,
193 alg.inDetTracks = config.readName (self.containerName)
194 alg.inDetTracksOut = config.copyName (self.containerName)
195 alg.preselection = config.getPreselection (self.containerName, '')
196
197 if self.minPt > 0 : # Set up the the pt selection
198 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'InDetTrackPtCutAlg' )
199 alg.selectionDecoration = 'selectPt' + self.postfix + ',as_bits'
200 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
201 alg.selectionTool.minPt = self.minPt
202 alg.particles = config.readName (self.containerName)
203 alg.preselection = config.getPreselection (self.containerName, '')
204 config.addSelection (self.containerName, '', alg.selectionDecoration,
205 preselection=True)
206
207 # Multiple variables are not included in the SmartCollection (momentum, eta, charge)
208 alg = config.createAlgorithm( 'CP::InDetTrackExtraVarDecoratorAlg', 'ExtraVarDecorator' )
209 alg.inDetTracks = config.readName(self.containerName)
210
211 config.addOutputVar (self.containerName, 'pt_%SYS%', 'pt')
212 config.addOutputVar (self.containerName, 'eta_%SYS%', 'eta', noSys=True)
213 config.addOutputVar (self.containerName, 'phi', 'phi', noSys=True)
214 config.addOutputVar (self.containerName, 'charge_%SYS%', 'charge', noSys=True)
215 config.addOutputVar (self.containerName, 'qOverP', 'qOverP')
216 config.addOutputVar (self.containerName, 'd0', 'd0')
217 config.addOutputVar (self.containerName, 'z0', 'z0')
218 config.addOutputVar (self.containerName, 'vz', 'vz', noSys=True, auxType='float')
219
220 # decorate track summary information on the reconstructed object:
222 config.addOutputVar (self.containerName, 'numberOfInnermostPixelLayerHits', 'numberOfInnermostPixelLayerHits', noSys=True, auxType='unsigned_char')
223 config.addOutputVar (self.containerName, 'numberOfPixelDeadSensors', 'numberOfPixelDeadSensors', noSys=True, auxType='unsigned_char')
224 config.addOutputVar (self.containerName, 'numberOfPixelHits', 'numberOfPixelHits', noSys=True, auxType='unsigned_char')
225 config.addOutputVar (self.containerName, 'numberOfPixelHoles', 'numberOfPixelHoles', noSys=True, auxType='unsigned_char')
226 config.addOutputVar (self.containerName, 'numberOfPixelSharedHits', 'numberOfPixelSharedHits', noSys=True, auxType='unsigned_char')
227 config.addOutputVar (self.containerName, 'numberOfSCTDeadSensors', 'numberOfSCTDeadSensors', noSys=True, auxType='unsigned_char')
228 config.addOutputVar (self.containerName, 'numberOfSCTHits', 'numberOfSCTHits', noSys=True, auxType='unsigned_char')
229 config.addOutputVar (self.containerName, 'numberOfSCTHoles', 'numberOfSCTHoles', noSys=True, auxType='unsigned_char')
230 config.addOutputVar (self.containerName, 'numberOfSCTSharedHits', 'numberOfSCTSharedHits', noSys=True, auxType='unsigned_char')
231 config.addOutputVar (self.containerName, 'numberOfTRTHits', 'numberOfTRTHits', noSys=True, auxType='unsigned_char')
232 config.addOutputVar (self.containerName, 'numberOfTRTOutliers', 'numberOfTRTOutliers', noSys=True, auxType='unsigned_char')
233
234
236 """the ConfigBlock for the track working point"""
237
238 def __init__ (self) :
239 super (InDetTrackWorkingPointConfig, self).__init__ ()
240 self.addOption ('containerName', '', type=str,
241 noneAction='error',
242 info="the name of the input container.")
243 self.addOption ('selectionName', '', type=str,
244 noneAction='error',
245 info="the name of the track selection to define (e.g. `tightPrimary` "
246 "or `loose`).")
247 self.addOption ('postfix', None, type=str,
248 info="a postfix to apply to decorations and algorithm names. "
249 "Typically not needed here as `selectionName` is used internally.")
250 self.addOption ('cutLevel', None, type=str,
251 noneAction='error',
252 info="the selection WP to use. Supported WPs for general "
253 "use: `Loose` and `TightPrimary`. For expert studies, further "
254 "WPs are available: `NoCut`, `LoosePrimary`, `LooseElectron`, "
255 "`LooseMuon`, `LooseTau`, `MinBias`, `HILoose`, `HITight`, "
256 "`HILooseOptimized`, `HITightOptimized`.",
257 expertMode=["NoCut", "LoosePrimary", "LooseElectron",
258 "LooseMuon", "LooseTau", "MinBias", "HILoose", "HITight",
259 "HILooseOptimized", "HITightOptimized"])
260 self.addOption ('additionalCuts', None, type=dict,
261 info="additional cuts to modify the selection WP. Only meant for "
262 "expert studies of track selection. Passed as pairs of `cutName: value`. "
263 "For an overview of available cuts, see twiki.cern.ch/twiki/bin/viewauth/"
264 "AtlasProtected/InDetTrackSelectionTool#List_of_possible_cuts.",
265 expertMode=True)
266 self.addOption ('vertexContainer', None, type=str,
267 info="A vertex collection to be used by the additionalCuts. The leading "
268 "primary vertex is passed to the selectionTool to calculate the distance "
269 "in the maxZ0 cuts.",
270 expertMode=True)
271 self.addOption ('runTruthFilter', True, type=bool,
272 info="whether to run the `TruthFilterTool`. This tool is only compatible "
273 "with the cut levels `Loose` and `TightPrimary`.")
274 self.addOption ('calibFile', None, type=str,
275 info="name of the calibration file to use for efficiencies "
276 "in the `TruthFilter` tool. Expert option to override the "
277 "recommendations based on the campaign.",
278 expertMode=True)
279 self.addOption ('filterToolSeed', None, type=int,
280 info="random seed to be used by the `InDetTrackTruthFilterTool`.",
281 expertMode=True)
282 self.addOption ('fFakeLoose', None, type=float,
283 info="the fraction of fake tracks in the `Loose` working point. "
284 "Will be used by the `InDetTrackTruthFilterTool`. Expert option to "
285 "override the recommendations.",
286 expertMode=True)
287 self.addOption ('fFakeTight', None, type=float,
288 info="the fraction of fake tracks in the `TightPrimary` working "
289 "point. Will be used by the `InDetTrackTruthFilterTool`. Expert option "
290 "to override the recommendations.",
291 expertMode=True)
292 self.addOption ('trkEffSystScale', None, type=float,
293 info="the track efficiency systematic scale. Will be used "
294 "by the `InDetTrackTruthFilterTool`. Expert option to override the "
295 "recommendations.",
296 expertMode=True)
297 self.addOption ('addSelectionToPreselection', True, type=bool,
298 info="whether to retain only tracks satisfying the `cutLevel` "
299 "requirements.")
300
301 def instanceName (self) :
302 """Return the instance name for this block"""
303 if self.postfix is not None:
304 return self.containerName + self.selectionName + self.postfix
305 else:
306 return self.containerName + self.selectionName
307
308 def makeAlgs (self, config) :
309 log = logging.getLogger('InDetTrackWorkingPointConfig')
310
311 selectionPostfix = self.selectionName
312 if selectionPostfix != '' and selectionPostfix[0] != '_' :
313 selectionPostfix = '_' + selectionPostfix
314
315 postfix = self.postfix
316 if postfix is None :
317 postfix = self.selectionName
318 if postfix != '' and postfix[0] != '_' :
319 postfix = '_' + postfix
320
321 cutLevels = ["NoCut", "Loose", "LoosePrimary", "TightPrimary", "LooseMuon",
322 "LooseElectron", "LooseTau", "MinBias", "HILoose", "HITight",
323 "HILooseOptimized", "HITightOptimized"]
324 alg = config.createAlgorithm( 'CP::InDetTrackSelectionAlg', 'InDetTrackSelectionAlg' )
325 alg.selectionDecoration = 'selectTrack' + postfix + '_%SYS%,as_bits'
326 config.addPrivateTool( 'selectionTool', 'InDet::InDetTrackSelectionTool')
327 if self.cutLevel is None:
328 log.warning("No selection WP chosen, not setting up InDetTrackSelectionTool.")
329 elif self.cutLevel not in cutLevels:
330 raise ValueError ('Invalid cut level: \"' + self.cutLevel + '\", has '
331 'to be one of: ' + ', '.join(cutLevels))
332 elif self.cutLevel in ["Loose", "TightPrimary"]:
333 alg.selectionTool.CutLevel = self.cutLevel
334 else:
335 log.warning('Using cut level: \"' + self.cutLevel + '\" that is not '
336 'meant for general use, but only expert studies.')
337 alg.selectionTool.CutLevel = self.cutLevel
338
340 for cutName, value in self.additionalCuts.items():
341 setattr(alg.selectionTool, cutName, value)
342 if self.vertexContainer is not None:
343 alg.vertices = self.vertexContainer
344
345 # Set up the truth filtering algorithm:
346 if config.dataType() is not DataType.Data:
348 if config.isPhyslite():
349 log.warning ('The TruthFilterTool is not compatible with Physlite mode. '
350 'This tool is skipped for now. Please set \"runTruthFilter: '
351 'False\" to get rid of this warning.')
352 else:
353 config.addPrivateTool( 'filterTool', 'InDet::InDetTrackTruthFilterTool' )
354 config.addPrivateTool( 'filterTool.trackOriginTool', 'InDet::InDetTrackTruthOriginTool' )
355 # Set working point based on cut level
356 if self.cutLevel == "Loose":
357 alg.filterWP = "LOOSE"
358 elif self.cutLevel == "TightPrimary":
359 alg.filterWP = "TIGHT"
360 else:
361 raise ValueError ('Attempting to set TruthFilter WP based on cut level: \"'
362 + self.efficiencyWP + '\" that is not supported.')
363 # Set calibFile and fake rates based on campaign
364 if config.geometry() is LHCPeriod.Run2:
365 # Run 2 recommendations (MC20)
366 alg.filterTool.calibFileNomEff = "InDetTrackSystematicsTools/CalibData_22.0_2022-v00/TrackingRecommendations_prelim_rel22.root"
367 alg.filterTool.fFakeLoose = 0.10
368 alg.filterTool.fFakeTight = 1.00
369 elif config.geometry() is LHCPeriod.Run3:
370 if config.campaign() in [Campaign.MC23a, Campaign.MC23d, Campaign.MC23e]:
371 # 2022/23/24 recommendations (MC23a/d/e)
372 alg.filterTool.calibFileNomEff = "InDetTrackSystematicsTools/CalibData_22.0_2022-v00/TrackingRecommendations_prelim_rel22.root"
373 alg.filterTool.fFakeLoose = 0.40
374 alg.filterTool.fFakeTight = 1.00
375 elif not (self.calibFile and self.fFakeLoose and self.fFakeTight):
376 raise ValueError ('No efficiency recommendations found for campaign \"'
377 + config.campaign().value + '\" in Run 3. '
378 'Please check that the recommendations exist.')
379 elif not (self.calibFile and self.fFakeLoose and self.fFakeTight):
380 raise ValueError ('No efficiency recommendations found for geometry \"'
381 + config.geometry().value + '\". Please check '
382 'the configuration.')
383 # Set custom calibFile, fake rates, or random seed
384 if self.calibFile:
385 alg.filterTool.calibFileNomEff = self.calibFile
386 if self.fFakeLoose:
387 alg.filterTool.fFakeLoose = self.fFakeLoose
388 if self.fFakeTight:
389 alg.filterTool.fFakeTight = self.fFakeTight
391 alg.filterTool.Seed = self.filterToolSeed
393 alg.filterTool.trkEffSystScale = self.trkEffSystScale
394 alg.inDetTracks = config.readName (self.containerName)
395 alg.preselection = config.getPreselection (self.containerName, '')
396 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
397 preselection=self.addSelectionToPreselection)
makeTrackBiasingTool(config, alg, float biasD0=None, float biasZ0=None, float biasQoverPsagitta=None, int customRunNumber=None, bool applyD0Bias=True, bool applyZ0Bias=False, bool applyQoverPBias=False)