ATLAS Offline Software
Loading...
Searching...
No Matches
MuonAnalysisConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3# AnaAlgorithm import(s):
4from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
5from AnalysisAlgorithmsConfig.ConfigSequence import groupBlocks
6from AthenaCommon.SystemOfUnits import GeV
7from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType
8from TrackingAnalysisAlgorithms.TrackingAnalysisConfig import InDetTrackCalibrationConfig
9from AthenaConfiguration.Enums import LHCPeriod
10from TrigGlobalEfficiencyCorrection.TriggerLeg_DictHelpers import TriggerDict
11from Campaigns.Utils import Campaign
12from AthenaCommon.Logging import logging
13
14
15class MuonCalibrationConfig (ConfigBlock):
16 """the ConfigBlock for the muon four-momentum correction"""
17
18 def __init__ (self) :
19 super (MuonCalibrationConfig, self).__init__ ()
20 self.setBlockName('Muons')
21 self.addOption ('inputContainer', '', type=str,
22 info="the name of the input muon container. If left empty, automatically defaults "
23 "to `AnalysisMuons` for PHYSLITE or `Muons` otherwise.")
24 self.addOption ('containerName', '', type=str,
25 noneAction='error',
26 info="the name of the output container after calibration.")
27 self.addOption ('postfix', "", type=str,
28 info="a postfix to apply to decorations and algorithm names. "
29 "Typically not needed here since the calibration is common to "
30 "all muons.")
31 self.addOption ('minPt', 3.0*GeV, type=float,
32 info=r"$p_\mathrm{T}$ cut (in MeV) to apply to calibrated muons.")
33 self.addOption ('recalibratePhyslite', True, type=bool,
34 info="whether to run the `CP::MuonCalibrationAndSmearingAlg` on "
35 "PHYSLITE derivations.")
36 self.addOption ('maxEta', 2.7, type=float,
37 info=r"maximum muon $\vert\eta\vert$.")
38 self.addOption ('excludeNSWFromPrecisionLayers', False, type=bool,
39 info="only for testing purposes, turn on to ignore NSW hits and "
40 "fix a crash with older derivations (p-tag <p5834).")
41 self.addOption ('calibMode', 'correctData_CB', type=str, info='calibration mode of the `MuonCalibTool` needed to turn on the sagitta bias corrections and to select the muon track calibration type (CB or ID+MS), see https://atlas-mcp.docs.cern.ch/guidelines/muonmomentumcorrections/index.html#cpmuoncalibtool-tool.')
42 self.addOption ('decorateTruth', False, type=bool,
43 info="decorate truth particle information on the reconstructed one.")
44 self.addOption ('writeTrackD0Z0', False, type = bool,
45 info=r"save the $d_0$ significance and $z_0\sin\theta$ variables.")
46 self.addOption ('writeColumnarToolVariables', False, type=bool,
47 info="whether to add variables needed for running the columnar muon tool(s) on the output n-tuple (EXPERIMENTAL).",
48 expertMode=True)
49 self.addOption ('runTrackBiasing', False, type=bool,
50 info="EXPERIMENTAL: This enables the `InDetTrackBiasingTool`, for tracks "
51 "associated to muons. The tool does not have Run 3 recommendations yet.",
52 expertMode=True)
53
54 def instanceName (self) :
55 if self.postfix != "":
56 return self.postfix
57 else :
58 return self.containerName
59
60 def makeAlgs (self, config) :
61
62 log = logging.getLogger('MuonCalibrationConfig')
63
64 #make sure that this is sync with
65 #PhysicsAnalysis/MuonID/MuonIDAnalysis/MuonMomentumCorrections/MuonMomentumCorrections/MuonCalibTool.h#L31-37
66 if self.calibMode == 'correctData_CB':
67 calibMode = 0
68 elif self.calibMode == 'correctData_IDMS':
69 calibMode = 1
70 elif self.calibMode == 'notCorrectData_IDMS':
71 calibMode = 2
72 elif self.calibMode == 'notCorrectData_CB':
73 calibMode = 3
74 else :
75 raise ValueError ("invalid calibMode: \"" + self.calibMode + "\". Allowed values are correctData_CB, correctData_IDMS, notCorrectData_IDMS, notCorrectData_CB")
76
77 inputContainer = "AnalysisMuons" if config.isPhyslite() else "Muons"
79 inputContainer = self.inputContainer
80 config.setSourceName (self.containerName, inputContainer)
81 config.setContainerMeta (self.containerName, 'calibMode', calibMode)
82
83 # Set up a shallow copy to decorate
84 if config.wantCopy (self.containerName) :
85 alg = config.createAlgorithm( 'CP::AsgShallowCopyAlg', 'MuonShallowCopyAlg' )
86 alg.input = config.readName (self.containerName)
87 alg.output = config.copyName (self.containerName)
88 alg.outputType = 'xAOD::MuonContainer'
89
90 # Set up the eta-cut on all muons prior to everything else
91 alg = config.createAlgorithm( 'CP::AsgSelectionAlg',
92 'MuonEtaCutAlg' )
93 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
94 alg.selectionTool.maxEta = self.maxEta
95 alg.selectionDecoration = 'selectEta' + self.postfix + ',as_bits'
96 alg.particles = config.readName (self.containerName)
97 alg.preselection = config.getPreselection (self.containerName, '')
98 config.addSelection (self.containerName, '', alg.selectionDecoration)
99
100 # Set up the muon calibration and smearing algorithm:
101 alg = config.createAlgorithm( 'CP::MuonCalibrationAndSmearingAlg',
102 'MuonCalibrationAndSmearingAlg' )
103 config.addPrivateTool( 'calibrationAndSmearingTool',
104 'CP::MuonCalibTool' )
105
106 alg.calibrationAndSmearingTool.IsRun3Geo = config.geometry() >= LHCPeriod.Run3
107 alg.calibrationAndSmearingTool.calibMode = calibMode
108 if config.geometry() is LHCPeriod.Run4:
109 log.warning("Disabling NSW hits for Run4 geometry")
110 alg.calibrationAndSmearingTool.ExcludeNSWFromPrecisionLayers = True
111 else:
112 alg.calibrationAndSmearingTool.ExcludeNSWFromPrecisionLayers = self.excludeNSWFromPrecisionLayers and (config.geometry() >= LHCPeriod.Run3)
113 alg.muons = config.readName (self.containerName)
114 alg.muonsOut = config.copyName (self.containerName)
115 alg.preselection = config.getPreselection (self.containerName, '')
116 if config.isPhyslite() and not self.recalibratePhyslite :
117 alg.skipNominal = True
118
119 # Set up the the pt selection
120 if self.minPt > 0:
121 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'MuonPtCutAlg' )
122 alg.selectionDecoration = 'selectPt' + self.postfix + ',as_bits'
123 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
124 alg.particles = config.readName (self.containerName)
125 alg.selectionTool.minPt = self.minPt
126 alg.preselection = config.getPreselection (self.containerName, '')
127 config.addSelection (self.containerName, '', alg.selectionDecoration,
128 preselection = True)
129
130 # Additional decorations
132 alg = config.createAlgorithm( 'CP::AsgLeptonTrackDecorationAlg',
133 'LeptonTrackDecorator' )
134 if config.dataType() is not DataType.Data:
136 InDetTrackCalibrationConfig.makeTrackBiasingTool(config, alg)
137 InDetTrackCalibrationConfig.makeTrackSmearingTool(config, alg)
138 alg.particles = config.readName (self.containerName)
139
140 alg = config.createAlgorithm( 'CP::AsgEnergyDecoratorAlg', 'EnergyDecorator' )
141 alg.particles = config.readName (self.containerName)
142
143 config.addOutputVar (self.containerName, 'pt', 'pt')
144 config.addOutputVar (self.containerName, 'eta', 'eta', noSys=True)
145 config.addOutputVar (self.containerName, 'phi', 'phi', noSys=True)
146 config.addOutputVar (self.containerName, 'e_%SYS%', 'e')
147 config.addOutputVar (self.containerName, 'charge', 'charge', noSys=True)
148
149 if self.writeTrackD0Z0:
150 config.addOutputVar (self.containerName, 'd0_%SYS%', 'd0')
151 config.addOutputVar (self.containerName, 'd0sig_%SYS%', 'd0sig')
152 config.addOutputVar (self.containerName, 'z0_%SYS%', 'z0')
153 config.addOutputVar (self.containerName, 'z0sintheta_%SYS%', 'z0sintheta')
154 config.addOutputVar (self.containerName, 'z0sinthetasig_%SYS%', 'z0sinthetasig')
155
156 # decorate truth information on the reconstructed object:
157 if self.decorateTruth and config.dataType() is not DataType.Data:
158 config.addOutputVar (self.containerName, "truthType", "truth_type", noSys=True)
159 config.addOutputVar (self.containerName, "truthOrigin", "truth_origin", noSys=True)
160
161 config.addOutputVar (self.containerName, 'muonType', 'muonType', noSys=True, enabled=self.writeColumnarToolVariables)
162
164 """the ConfigBlock for the muon working point selection"""
165
166 def __init__ (self) :
167 super (MuonWorkingPointSelectionConfig, self).__init__ ()
168 self.setBlockName('MuonWorkingPointSelection')
169 self.addOption ('containerName', '', type=str,
170 noneAction='error',
171 info="the name of the input container.")
172 self.addOption ('selectionName', '', type=str,
173 noneAction='error',
174 info="the name of the muon selection to define (e.g. `tight` or `loose`).")
175 self.addOption ('postfix', None, type=str,
176 info="a postfix to apply to decorations and algorithm names. "
177 "Typically not needed here as `selectionName` is used internally.")
178 self.addOption ('trackSelection', True, type=bool,
179 info="whether or not to set up an instance of "
180 "`CP::AsgLeptonTrackSelectionAlg`, with the recommended $d_0$ and "
181 r"$z_0\sin\theta$ cuts.")
182 self.addOption ('maxD0Significance', 3, type=float,
183 info="maximum $d_0$ significance used for the track selection.")
184 self.addOption ('maxDeltaZ0SinTheta', 0.5, type=float,
185 info=r"maximum $\Delta z_0\sin\theta$ (in mm) used for the track selection.")
186 self.addOption ('quality', None, type=str,
187 info="the ID WP to use. Supported ID WPs: `Tight`, `Medium`, "
188 "`Loose`, `LowPt`, `HighPt`.")
189 self.addOption ('isolation', None, type=str,
190 info="the isolation WP to use. Supported isolation WPs: "
191 "`PflowLoose_VarRad`, `PflowTight_VarRad`, `Loose_VarRad`, "
192 "`Tight_VarRad`, `NonIso`.")
193 self.addOption ('addSelectionToPreselection', True, type=bool,
194 info="whether to retain only muons satisfying the working point "
195 "requirements.")
196 self.addOption ('isoDecSuffix', '', type=str,
197 info="the `isoDecSuffix` name if using close-by-corrected isolation working points.")
198 self.addOption ('excludeNSWFromPrecisionLayers', False, type=bool,
199 info="only for testing purposes, turn on to ignore NSW hits and "
200 "fix a crash with older derivations (p-tag <p5834).")
201
202 def instanceName (self) :
203 if self.postfix is not None:
204 return self.containerName + '_' + self.postfix
205 else:
206 return self.containerName + '_' + self.selectionName
207
208 def makeAlgs (self, config) :
209 log = logging.getLogger('MuonWorkingPointSelectionConfig')
210
211 from xAODMuon.xAODMuonEnums import xAODMuonEnums
212 if self.quality == 'Tight' :
213 quality = xAODMuonEnums.Quality.Tight
214 elif self.quality == 'Medium' :
215 quality = xAODMuonEnums.Quality.Medium
216 elif self.quality == 'Loose' :
217 quality = xAODMuonEnums.Quality.Loose
218 elif self.quality == 'VeryLoose' :
219 quality = xAODMuonEnums.Quality.VeryLoose
220 elif self.quality == 'HighPt' :
221 quality = 4
222 elif self.quality == 'LowPt' :
223 quality = 5
224 else :
225 raise ValueError ("invalid muon quality: \"" + self.quality +
226 "\", allowed values are Tight, Medium, Loose, " +
227 "VeryLoose, HighPt, LowPt")
228
229 # The setup below is inappropriate for Run 1
230 if config.geometry() is LHCPeriod.Run1:
231 raise ValueError ("Can't set up the MuonWorkingPointSelectionConfig with %s, there must be something wrong!" % config.geometry().value)
232
233 postfix = self.postfix
234 if postfix is None :
235 postfix = self.selectionName
236 if postfix != '' and postfix[0] != '_' :
237 postfix = '_' + postfix
238
239 # Set up the track selection algorithm:
241 alg = config.createAlgorithm( 'CP::AsgLeptonTrackSelectionAlg',
242 'MuonTrackSelectionAlg',
243 reentrant=True )
244 alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits'
245 alg.maxD0Significance = self.maxD0Significance
246 alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta
247 alg.particles = config.readName (self.containerName)
248 alg.preselection = config.getPreselection (self.containerName, '')
249 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration, preselection=self.addSelectionToPreselection)
250
251 # Setup the muon quality selection
252 alg = config.createAlgorithm( 'CP::MuonSelectionAlgV2',
253 'MuonSelectionAlg' )
254 config.addPrivateTool( 'selectionTool', 'CP::MuonSelectionTool' )
255 alg.selectionTool.MuQuality = quality
256 alg.selectionTool.IsRun3Geo = config.geometry() >= LHCPeriod.Run3
257 if config.geometry() is LHCPeriod.Run4:
258 log.warning("Disabling NSW hits for Run4 geometry")
259 alg.selectionTool.ExcludeNSWFromPrecisionLayers = True
260 else:
261 alg.selectionTool.ExcludeNSWFromPrecisionLayers = self.excludeNSWFromPrecisionLayers and (config.geometry() >= LHCPeriod.Run3)
262 alg.selectionDecoration = 'good_muon' + postfix + ',as_char'
263 alg.badMuonVetoDecoration = 'is_bad' + postfix + ',as_char'
264 alg.muons = config.readName (self.containerName)
265 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
266 config.addSelection (self.containerName, self.selectionName,
267 alg.selectionDecoration,
268 preselection=self.addSelectionToPreselection)
269 if self.quality == 'HighPt':
270 config.addOutputVar (self.containerName, 'is_bad' + postfix, 'is_bad' + postfix)
271
272 # Set up the isolation calculation algorithm:
273 if self.isolation != 'NonIso' :
274 alg = config.createAlgorithm( 'CP::MuonIsolationAlg',
275 'MuonIsolationAlg' )
276 config.addPrivateTool( 'isolationTool', 'CP::IsolationSelectionTool' )
277 alg.isolationTool.MuonWP = self.isolation
278 alg.isolationTool.IsoDecSuffix = self.isoDecSuffix
279 alg.isolationDecoration = 'isolated_muon' + postfix + ',as_char'
280 alg.muons = config.readName (self.containerName)
281 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
282 config.addSelection (self.containerName, self.selectionName,
283 alg.isolationDecoration,
284 preselection=self.addSelectionToPreselection)
285
286
288 """the ConfigBlock for the muon working point efficiency computation"""
289
290 def __init__ (self) :
291 super (MuonWorkingPointEfficiencyConfig, self).__init__ ()
292 self.setBlockName('MuonWorkingPointEfficiency')
293 self.addDependency('MuonWorkingPointSelection', required=True)
294 self.addDependency('EventSelection', required=False)
295 self.addDependency('EventSelectionMerger', required=False)
296 self.addOption ('containerName', '', type=str,
297 noneAction='error',
298 info="the name of the input container.")
299 self.addOption ('selectionName', '', type=str,
300 noneAction='error',
301 info="the name of the muon selection to define (e.g. `tight` or `loose`).")
302 self.addOption ('postfix', None, type=str,
303 info="a postfix to apply to decorations and algorithm names. "
304 "Typically not needed here as `selectionName` is used internally.")
305 self.addOption ('trackSelection', True, type=bool,
306 info="whether or not to set up an instance of "
307 "`CP::AsgLeptonTrackSelectionAlg`, with the recommended $d_0$ and "
308 r"$z_0\sin\theta$ cuts.")
309 self.addOption ('quality', None, type=str,
310 info="the ID WP to use. Supported ID WPs: `Tight`, `Medium`, "
311 "`Loose`, `LowPt`, `HighPt`.")
312 self.addOption ('isolation', None, type=str,
313 info="the isolation WP to use. Supported isolation WPs: "
314 "`PflowLoose_VarRad`, `PflowTight_VarRad`, `Loose_VarRad`, "
315 "`Tight_VarRad`, `NonIso`.")
316 self.addOption ('systematicBreakdown', False, type=bool,
317 info="enables the full breakdown of efficiency SF systematics "
318 "(1 NP per uncertainty source, instead of 1 NP in total).")
319 self.addOption ('noEffSF', False, type=bool,
320 info="disables the calculation of efficiencies and scale factors. "
321 "Experimental! Only useful to test a new WP for which scale "
322 "factors are not available.",
323 expertMode=True)
324 self.addOption ('saveDetailedSF', True, type=bool,
325 info="save all the independent detailed object scale factors.")
326 self.addOption ('saveCombinedSF', False, type=bool,
327 info="save the combined object scale factor.")
328
329 def instanceName (self) :
330 if self.postfix is not None:
331 return self.containerName + '_' + self.postfix
332 else:
333 return self.containerName + '_' + self.selectionName
334
335 def makeAlgs (self, config) :
336
337 # The setup below is inappropriate for Run 1
338 if config.geometry() is LHCPeriod.Run1:
339 raise ValueError ("Can't set up the MuonWorkingPointEfficiencyConfig with %s, there must be something wrong!" % config.geometry().value)
340
341 postfix = self.postfix
342 if postfix is None :
343 postfix = self.selectionName
344 if postfix != '' and postfix[0] != '_' :
345 postfix = '_' + postfix
346
347 sfList = []
348 # Set up the reco/ID efficiency scale factor calculation algorithm:
349 if config.dataType() is not DataType.Data and not self.noEffSF:
350 alg = config.createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
351 'MuonEfficiencyScaleFactorAlgReco' )
352 config.addPrivateTool( 'efficiencyScaleFactorTool',
353 'CP::MuonEfficiencyScaleFactors' )
354 config.setExtraInputs ({('xAOD::EventInfo', 'EventInfo.RandomRunNumber')})
355 alg.scaleFactorDecoration = 'muon_reco_effSF' + postfix + "_%SYS%"
356 alg.outOfValidity = 2 #silent
357 alg.outOfValidityDeco = 'muon_reco_bad_eff' + postfix
358 alg.efficiencyScaleFactorTool.WorkingPoint = self.quality
359 if config.geometry() >= LHCPeriod.Run3:
360 alg.efficiencyScaleFactorTool.CalibrationRelease = '251211_Preliminary_r24run3'
361 else:
362 alg.efficiencyScaleFactorTool.CalibrationRelease = '230213_Preliminary_r22run2'
363 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
364 alg.muons = config.readName (self.containerName)
365 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
367 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
368 'reco_effSF' + postfix)
369 sfList += [alg.scaleFactorDecoration]
370
371 # Set up the HighPt-specific BadMuonVeto efficiency scale factor calculation algorithm:
372 if config.dataType() is not DataType.Data and self.quality == 'HighPt' and not self.noEffSF:
373 alg = config.createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
374 'MuonEfficiencyScaleFactorAlgBMVHighPt' )
375 config.addPrivateTool( 'efficiencyScaleFactorTool',
376 'CP::MuonEfficiencyScaleFactors' )
377 alg.scaleFactorDecoration = 'muon_BadMuonVeto_effSF' + postfix + "_%SYS%"
378 alg.outOfValidity = 2 #silent
379 alg.outOfValidityDeco = 'muon_BadMuonVeto_bad_eff' + postfix
380 alg.efficiencyScaleFactorTool.WorkingPoint = 'BadMuonVeto_HighPt'
381 if config.geometry() >= LHCPeriod.Run3:
382 alg.efficiencyScaleFactorTool.CalibrationRelease = '220817_Preliminary_r22run3' # not available as part of '230123_Preliminary_r22run3'!
383 else:
384 alg.efficiencyScaleFactorTool.CalibrationRelease = '230213_Preliminary_r22run2'
385 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
386 alg.muons = config.readName (self.containerName)
387 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
388 if self.saveDetailedSF:
389 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
390 'BadMuonVeto_effSF' + postfix)
391 sfList += [alg.scaleFactorDecoration]
392
393 # Set up the isolation efficiency scale factor calculation algorithm:
394 if config.dataType() is not DataType.Data and self.isolation != 'NonIso' and not self.noEffSF:
395 alg = config.createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
396 'MuonEfficiencyScaleFactorAlgIsol' )
397 config.addPrivateTool( 'efficiencyScaleFactorTool',
398 'CP::MuonEfficiencyScaleFactors' )
399 alg.scaleFactorDecoration = 'muon_isol_effSF' + postfix + "_%SYS%"
400 alg.outOfValidity = 2 #silent
401 alg.outOfValidityDeco = 'muon_isol_bad_eff' + postfix
402 alg.efficiencyScaleFactorTool.WorkingPoint = self.isolation + 'Iso'
403 if config.geometry() >= LHCPeriod.Run3:
404 alg.efficiencyScaleFactorTool.CalibrationRelease = '251211_Preliminary_r24run3'
405 else:
406 alg.efficiencyScaleFactorTool.CalibrationRelease = '230213_Preliminary_r22run2'
407 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
408 alg.muons = config.readName (self.containerName)
409 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
410 if self.saveDetailedSF:
411 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
412 'isol_effSF' + postfix)
413 sfList += [alg.scaleFactorDecoration]
414
415 # Set up the TTVA scale factor calculation algorithm:
416 if config.dataType() is not DataType.Data and self.trackSelection and not self.noEffSF:
417 alg = config.createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
418 'MuonEfficiencyScaleFactorAlgTTVA' )
419 config.addPrivateTool( 'efficiencyScaleFactorTool',
420 'CP::MuonEfficiencyScaleFactors' )
421 alg.scaleFactorDecoration = 'muon_TTVA_effSF' + postfix + "_%SYS%"
422 alg.outOfValidity = 2 #silent
423 alg.outOfValidityDeco = 'muon_TTVA_bad_eff' + postfix
424 alg.efficiencyScaleFactorTool.WorkingPoint = 'TTVA'
425 if config.geometry() >= LHCPeriod.Run3:
426 alg.efficiencyScaleFactorTool.CalibrationRelease = '251211_Preliminary_r24run3'
427 else:
428 alg.efficiencyScaleFactorTool.CalibrationRelease = '230213_Preliminary_r22run2'
429 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
430 alg.muons = config.readName (self.containerName)
431 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
432 if self.saveDetailedSF:
433 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
434 'TTVA_effSF' + postfix)
435 sfList += [alg.scaleFactorDecoration]
436
437 if config.dataType() is not DataType.Data and not self.noEffSF and self.saveCombinedSF:
438 alg = config.createAlgorithm( 'CP::AsgObjectScaleFactorAlg',
439 'MuonCombinedEfficiencyScaleFactorAlg' )
440 alg.particles = config.readName (self.containerName)
441 alg.inScaleFactors = sfList
442 alg.outScaleFactor = 'effSF' + postfix + '_%SYS%'
443 config.addOutputVar (self.containerName, alg.outScaleFactor, 'effSF' + postfix)
444
445class MuonTriggerAnalysisSFBlock (ConfigBlock):
446
447 def __init__ (self) :
448 super (MuonTriggerAnalysisSFBlock, self).__init__ ()
449 self.addDependency('EventSelection', required=False)
450 self.addDependency('EventSelectionMerger', required=False)
451 self.addOption ('triggerChainsPerYear', {}, type=dict,
452 info="a dictionary with key (string) the year and value (list of "
453 "strings) the trigger chains.")
454 self.addOption ('muonID', '', type=str,
455 info="the muon quality WP to use.")
456 self.addOption ('saveSF', True, type=bool,
457 info="whether to decorate the trigger scale factor.")
458 self.addOption ('saveEff', False, type=bool,
459 info="whether to decorate the trigger MC efficiencies.")
460 self.addOption ('saveEffData', False, type=bool,
461 info="whether to decorate the trigger data efficiencies.")
462 self.addOption ('prefixSF', 'trigEffSF', type=str,
463 info="the decoration prefix for trigger scale factors.")
464 self.addOption ('prefixEff', 'trigEff', type=str,
465 info="the decoration prefix for MC trigger efficiencies.")
466 self.addOption ('prefixEffData', 'trigEffData', type=str,
467 info="the decoration prefix for data trigger efficiencies.")
468 self.addOption ('includeAllYearsPerRun', False, type=bool,
469 info="all configured years in the LHC run will "
470 "be included in all jobs.")
471 self.addOption ('removeHLTPrefix', True, type=bool,
472 info="remove the HLT prefix from trigger chain names.")
473 self.addOption ('containerName', '', type=str,
474 info="the input muon container, with a possible selection, in "
475 "the format `container` or `container.selection`.")
476 self.addOption ('customToolSuffix', '', type=str,
477 expertMode=True, info="EXPERIMENTAL: specify custom suffix for the public tool name")
478 self.addOption ('customInputFolder', '', type=str,
479 expertMode=True, info="EXPERIMENTAL: specify custom input folder")
480 self.addOption ('customInputFilePerYear', {}, type=dict,
481 expertMode=True, info="EXPERIMENTAL: specify custom input file per year")
482
483 def instanceName (self) :
484 return self.containerName + '_' + self.muonID
485
486 def makeAlgs (self, config) :
487
488 if config.dataType() is not DataType.Data:
489
490 # Dictionary from TrigGlobalEfficiencyCorrection/Triggers.cfg
491 # Key is trigger chain (w/o HLT prefix)
492 # Value is empty for single leg trigger or list of legs
493 triggerDict = TriggerDict()
494
496 years = [int(year) for year in self.triggerChainsPerYear.keys()]
497 else:
498 from TriggerAnalysisAlgorithms.TriggerAnalysisSFConfig import (
499 get_input_years)
500 years = get_input_years(config)
501
502 triggerYearStartBoundaries = {
503 2015: 260000,
504 2016: 290000,
505 2017: 324000,
506 2018: 348000,
507 2022: 410000,
508 2023: 450000,
509 2024: 470000,
510 }
511
512 triggerConfigs = {}
513 triggerConfigYears = {}
514 from TriggerAnalysisAlgorithms.TriggerAnalysisConfig import is_year_in_current_period
515 for year in years:
516 if not is_year_in_current_period(config, year):
517 continue
518
519 triggerChains = self.triggerChainsPerYear.get(int(year), self.triggerChainsPerYear.get(str(year), []))
520 for chain in triggerChains:
521 chain = chain.replace(" || ", "_OR_")
522 chain_noHLT = chain.replace("HLT_", "")
523 chain_out = chain_noHLT if self.removeHLTPrefix else chain
524 legs = triggerDict[chain_noHLT]
525 if not legs:
526 if chain_noHLT.startswith('mu') and chain_noHLT[2].isdigit:
527 # Need to support HLT_mu26_ivarmedium_OR_HLT_mu50
528 triggerConfigs[chain_out] = chain
529 if chain_out in triggerConfigYears.keys():
530 triggerConfigYears[chain_out].append(year)
531 else:
532 triggerConfigYears[chain_out] = [year]
533 else:
534 for leg in legs:
535 if leg.startswith('mu') and leg[2].isdigit:
536 # Need to support HLT_mu14_ivarloose
537 leg_out = leg if self.removeHLTPrefix else f"HLT_{leg}"
538 triggerConfigs[leg_out] = f"HLT_{leg}"
539 if leg_out in triggerConfigYears.keys():
540 triggerConfigYears[leg_out].append(year)
541 else:
542 triggerConfigYears[leg_out] = [year]
543
544 if not triggerConfigs:
545 return
546
547 # Make the public tool for this configuration
548 sfTool = config.createPublicTool("CP::MuonTriggerScaleFactors", f"{self.instanceName()}_SFTool{self.customToolSuffix}")
549 # Reproduce config from TrigGlobalEfficiencyAlg
550 sfTool.MuonQuality = self.muonID
551 sfTool.AllowZeroSF = True
552 sfTool.CustomInputFolder = self.customInputFolder
553 sfTool.CustomInputFilePerYear = self.customInputFilePerYear
554 sfTool.Campaign = config.campaign().value
555
556 for trig_short, trig in triggerConfigs.items():
557 alg = config.createAlgorithm('CP::MuonTriggerEfficiencyScaleFactorAlg',
558 'MuonTrigEfficiencyCorrectionsAlg_' + trig_short)
559 alg.efficiencyScaleFactorTool = f"{sfTool.getType()}/{sfTool.getName()}"
560
561 # Avoid warnings for missing triggers
562 if self.includeAllYearsPerRun:
563 alg.minRunNumber = 0
564 alg.maxRunNumber = 999999
565
566 if triggerConfigYears[trig_short][0] != years[0]:
567 alg.minRunNumber = triggerYearStartBoundaries.get(triggerConfigYears[trig_short][0], 999999)
568 if triggerConfigYears[trig_short][-1] != years[-1]:
569 alg.maxRunNumber = triggerYearStartBoundaries.get(triggerConfigYears[trig_short][-1] + 1, 999999)
570 elif config.campaign() is Campaign.MC20a: # to avoid potential corner-cases keep the default config unchanged
571 if triggerConfigYears[trig_short] == [2015]:
572 alg.maxRunNumber = 290000
573 elif triggerConfigYears[trig_short] == [2016]:
574 alg.minRunNumber = 290000
575
576 alg.trigger = trig
577
578 # Some triggers in `250731_SummerUpdate` recommendations are not supported in 2022 period F
579 if config.campaign() is Campaign.MC23a and (trig_short == "HLT_mu8noL1_FSNOSEED" or trig_short == "HLT_mu22_L1MU14FCH"):
580 alg.minRunNumber = 435816 # Start of 2022 period H
581
582 if self.saveSF:
583 alg.scaleFactorDecoration = f"muon_{self.prefixSF}_{trig_short}_%SYS%"
584 if self.saveEff:
585 alg.mcEfficiencyDecoration = f"muon_{self.prefixEff}_{trig_short}_%SYS%"
586 if self.saveEffData:
587 alg.dataEfficiencyDecoration = f"muon_{self.prefixEffData}_{trig_short}_%SYS%"
588 alg.outOfValidity = 2 #silent
589 alg.outOfValidityDeco = f"bad_eff_muontrig_{trig_short}"
590 alg.muons = config.readName (self.containerName)
591 alg.preselection = config.getPreselection (self.containerName, '')
592 if self.saveSF:
593 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, f"{self.prefixSF}_{trig_short}")
594 if self.saveEff:
595 config.addOutputVar (self.containerName, alg.mcEfficiencyDecoration, f"{self.prefixEff}_{trig_short}")
596 if self.saveEffData:
597 config.addOutputVar (self.containerName, alg.dataEfficiencyDecoration, f"{self.prefixEffData}_{trig_short}")
598
599
600class MuonLRTMergedConfig (ConfigBlock) :
601 def __init__ (self) :
602 super (MuonLRTMergedConfig, self).__init__ ()
603 self.addOption (
604 'inputMuons', 'Muons', type=str,
605 noneAction='error',
606 info="the name of the input muon container."
607 )
608 self.addOption (
609 'inputLRTMuons', 'MuonsLRT', type=str,
610 noneAction='error',
611 info="the name of the input LRT muon container."
612 )
613 self.addOption (
614 'containerName', 'Muons_LRTMerged', type=str,
615 noneAction='error',
616 info="the name of the output container after LRT merging."
617 )
618
619 def instanceName (self) :
620 return self.containerName
621
622 def makeAlgs (self, config) :
623
624 if config.isPhyslite() :
625 raise(RuntimeError("Muon LRT merging is not available in Physlite mode"))
626
627 alg = config.createAlgorithm( "CP::MuonLRTMergingAlg", "MuonLRTMergingAlg" )
628 alg.PromptMuonLocation = self.inputMuons
629 alg.LRTMuonLocation = self.inputLRTMuons
630 alg.OutputMuonLocation = self.containerName
631 alg.UseRun3WP = config.geometry() >= LHCPeriod.Run3
632 alg.CreateViewCollection = False
633
634class MuonContainerMergingConfig (ConfigBlock) :
635 def __init__ (self) :
636 super (MuonContainerMergingConfig, self).__init__ ()
637 self.addOption (
638 'inputMuonContainers', [], type=list,
639 noneAction='error',
640 info="list of container names to be merged (of type `xAOD::MuonContainer`)."
641 )
642 self.addOption (
643 'outputMuonLocation', 'MuonsMerged', type=str,
644 noneAction='error',
645 info="the name of the output muon container."
646 )
647 self.addOption (
648 'createViewCollection', True, type=bool,
649 info="whether the output container should be a view container rather than a deep copy."
650 )
651
652 def instanceName (self) :
653 return self.outputMuonLocation
654
655 def makeAlgs (self, config) :
656 alg = config.createAlgorithm( "CP::MuonContainerMergingAlg", "MuonContainerMergingAlg" )
657 alg.InputMuonContainers = self.inputMuonContainers
658 alg.OutputMuonLocation = self.outputMuonLocation
659 alg.CreateViewCollection = self.createViewCollection
660
661@groupBlocks
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:130