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