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