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