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