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="select muon input container, by default set to Muons")
22 self.addOption ('containerName', '', type=str,
23 noneAction='error',
24 info="the name of the output container after calibration.")
25 self.addOption ('postfix', "", type=str,
26 info="a postfix to apply to decorations and algorithm names. "
27 "Typically not needed here since the calibration is common to "
28 "all muons.")
29 self.addOption ('minPt', 3.0*GeV, type=float,
30 info="pT cut to apply to calibrated muons, in MeV. "
31 "The default is 3.0 GeV.")
32 self.addOption ('recalibratePhyslite', True, type=bool,
33 info="whether to run the CP::EgammaCalibrationAndSmearingAlg on "
34 "PHYSLITE derivations. The default is True.")
35 self.addOption ('maxEta', 2.7, type=float,
36 info="maximum muon |eta| (float). The default is 2.7.")
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="save the d0 significance and z0sinTheta variables so they can be written out")
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 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 "z_0 sin(theta) cuts. The default is True.")
182 self.addOption ('maxD0Significance', 3, type=float,
183 info="maximum d0 significance used for the trackSelection"
184 "The default is 3")
185 self.addOption ('maxDeltaZ0SinTheta', 0.5, type=float,
186 info="maximum Delta z0sinTheta in mm used for the trackSelection"
187 "The default is 0.5 mm")
188 self.addOption ('quality', None, type=str,
189 info="the ID WP (string) to use. Supported ID WPs: Tight, Medium, "
190 "Loose, LowPt, HighPt.")
191 self.addOption ('isolation', None, type=str,
192 info="the isolation WP (string) to use. Supported isolation WPs: "
193 "PflowLoose_VarRad, PflowTight_VarRad, Loose_VarRad, "
194 "Tight_VarRad, NonIso.")
195 self.addOption ('addSelectionToPreselection', True, type=bool,
196 info="whether to retain only muons satisfying the working point "
197 "requirements. The default is True.")
198 self.addOption ('isoDecSuffix', '', type=str,
199 info="isoDecSuffix if using close-by-corrected isolation working points.")
200 self.addOption ('systematicBreakdown', False, type=bool,
201 info="enables the full breakdown of efficiency SF systematics "
202 "(1 NP per uncertainty source, instead of 1 NP in total). "
203 "The default is False.")
204 self.addOption ('onlyRecoEffSF', False, type=bool,
205 info="same as noEffSF, but retains the ID scale factor. "
206 "Experimental! only useful for CI tests. The default is False.",
207 expertMode=True)
208 self.addOption ('noEffSF', False, type=bool,
209 info="disables the calculation of efficiencies and scale factors. "
210 "Experimental! only useful to test a new WP for which scale "
211 "factors are not available. The default is False.",
212 expertMode=True)
213 self.addOption ('saveDetailedSF', True, type=bool,
214 info="save all the independent detailed object scale factors. "
215 "The default is True.")
216 self.addOption ('saveCombinedSF', False, type=bool,
217 info="save the combined object scale factor. "
218 "The default is False.")
219 self.addOption ('excludeNSWFromPrecisionLayers', False, type=bool,
220 info="only for testing purposes, turn on to ignore NSW hits and "
221 "fix a crash with older derivations (p-tag <p5834)")
222
223 def instanceName (self) :
224 if self.postfix is not None:
225 return self.containerName + '_' + self.postfix
226 else:
227 return self.containerName + '_' + self.selectionName
228
229 def makeAlgs (self, config) :
230 log = logging.getLogger('MuonWorkingPointConfig')
231
232 from xAODMuon.xAODMuonEnums import xAODMuonEnums
233 if self.quality == 'Tight' :
234 quality = xAODMuonEnums.Quality.Tight
235 elif self.quality == 'Medium' :
236 quality = xAODMuonEnums.Quality.Medium
237 elif self.quality == 'Loose' :
238 quality = xAODMuonEnums.Quality.Loose
239 elif self.quality == 'VeryLoose' :
240 quality = xAODMuonEnums.Quality.VeryLoose
241 elif self.quality == 'HighPt' :
242 quality = 4
243 elif self.quality == 'LowPt' :
244 quality = 5
245 else :
246 raise ValueError ("invalid muon quality: \"" + self.quality +
247 "\", allowed values are Tight, Medium, Loose, " +
248 "VeryLoose, HighPt, LowPt")
249
250 # The setup below is inappropriate for Run 1
251 if config.geometry() is LHCPeriod.Run1:
252 raise ValueError ("Can't set up the MuonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
253
254 postfix = self.postfix
255 if postfix is None :
256 postfix = self.selectionName
257 if postfix != '' and postfix[0] != '_' :
258 postfix = '_' + postfix
259
260 # Set up the track selection algorithm:
262 alg = config.createAlgorithm( 'CP::AsgLeptonTrackSelectionAlg',
263 'MuonTrackSelectionAlg',
264 reentrant=True )
265 alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits'
266 alg.maxD0Significance = self.maxD0Significance
267 alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta
268 alg.particles = config.readName (self.containerName)
269 alg.preselection = config.getPreselection (self.containerName, '')
270 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration, preselection=self.addSelectionToPreselection)
271
272 # Setup the muon quality selection
273 alg = config.createAlgorithm( 'CP::MuonSelectionAlgV2',
274 'MuonSelectionAlg' )
275 config.addPrivateTool( 'selectionTool', 'CP::MuonSelectionTool' )
276 alg.selectionTool.MuQuality = quality
277 alg.selectionTool.IsRun3Geo = config.geometry() >= LHCPeriod.Run3
278 if config.geometry() is LHCPeriod.Run4:
279 log.warning("Disabling NSW hits for Run4 geometry")
280 alg.selectionTool.ExcludeNSWFromPrecisionLayers = True
281 else:
282 alg.selectionTool.ExcludeNSWFromPrecisionLayers = self.excludeNSWFromPrecisionLayers and (config.geometry() >= LHCPeriod.Run3)
283 alg.selectionDecoration = 'good_muon' + postfix + ',as_char'
284 alg.badMuonVetoDecoration = 'is_bad' + postfix + ',as_char'
285 alg.muons = config.readName (self.containerName)
286 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
287 config.addSelection (self.containerName, self.selectionName,
288 alg.selectionDecoration,
289 preselection=self.addSelectionToPreselection)
290 if self.quality == 'HighPt':
291 config.addOutputVar (self.containerName, 'is_bad' + postfix, 'is_bad' + postfix)
292
293 # Set up the isolation calculation algorithm:
294 if self.isolation != 'NonIso' :
295 alg = config.createAlgorithm( 'CP::MuonIsolationAlg',
296 'MuonIsolationAlg' )
297 config.addPrivateTool( 'isolationTool', 'CP::IsolationSelectionTool' )
298 alg.isolationTool.MuonWP = self.isolation
299 alg.isolationTool.IsoDecSuffix = self.isoDecSuffix
300 alg.isolationDecoration = 'isolated_muon' + postfix + ',as_char'
301 alg.muons = config.readName (self.containerName)
302 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
303 config.addSelection (self.containerName, self.selectionName,
304 alg.isolationDecoration,
305 preselection=self.addSelectionToPreselection)
306
307 sfList = []
308 # Set up the reco/ID efficiency scale factor calculation algorithm:
309 if config.dataType() is not DataType.Data and (not self.noEffSF or self.onlyRecoEffSF):
310 alg = config.createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
311 'MuonEfficiencyScaleFactorAlgReco' )
312 config.addPrivateTool( 'efficiencyScaleFactorTool',
313 'CP::MuonEfficiencyScaleFactors' )
314 alg.scaleFactorDecoration = 'muon_reco_effSF' + postfix + "_%SYS%"
315 alg.outOfValidity = 2 #silent
316 alg.outOfValidityDeco = 'muon_reco_bad_eff' + postfix
317 alg.efficiencyScaleFactorTool.WorkingPoint = self.quality
318 if config.geometry() >= LHCPeriod.Run3:
319 alg.efficiencyScaleFactorTool.CalibrationRelease = '250418_Preliminary_r24run3'
320 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
321 alg.muons = config.readName (self.containerName)
322 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
324 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
325 'reco_effSF' + postfix)
326 sfList += [alg.scaleFactorDecoration]
327
328 # Set up the HighPt-specific BadMuonVeto efficiency scale factor calculation algorithm:
329 if config.dataType() is not DataType.Data and self.quality == 'HighPt' and not self.onlyRecoEffSF and not self.noEffSF:
330 alg = config.createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
331 'MuonEfficiencyScaleFactorAlgBMVHighPt' )
332 config.addPrivateTool( 'efficiencyScaleFactorTool',
333 'CP::MuonEfficiencyScaleFactors' )
334 alg.scaleFactorDecoration = 'muon_BadMuonVeto_effSF' + postfix + "_%SYS%"
335 alg.outOfValidity = 2 #silent
336 alg.outOfValidityDeco = 'muon_BadMuonVeto_bad_eff' + postfix
337 alg.efficiencyScaleFactorTool.WorkingPoint = 'BadMuonVeto_HighPt'
338 if config.geometry() >= LHCPeriod.Run3:
339 alg.efficiencyScaleFactorTool.CalibrationRelease = '220817_Preliminary_r22run3' # not available as part of '230123_Preliminary_r22run3'!
340 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
341 alg.muons = config.readName (self.containerName)
342 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
343 if self.saveDetailedSF:
344 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
345 'BadMuonVeto_effSF' + postfix)
346 sfList += [alg.scaleFactorDecoration]
347
348 # Set up the isolation efficiency scale factor calculation algorithm:
349 if config.dataType() is not DataType.Data and self.isolation != 'NonIso' and not self.onlyRecoEffSF and not self.noEffSF:
350 alg = config.createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
351 'MuonEfficiencyScaleFactorAlgIsol' )
352 config.addPrivateTool( 'efficiencyScaleFactorTool',
353 'CP::MuonEfficiencyScaleFactors' )
354 alg.scaleFactorDecoration = 'muon_isol_effSF' + postfix + "_%SYS%"
355 alg.outOfValidity = 2 #silent
356 alg.outOfValidityDeco = 'muon_isol_bad_eff' + postfix
357 alg.efficiencyScaleFactorTool.WorkingPoint = self.isolation + 'Iso'
358 if config.geometry() >= LHCPeriod.Run3:
359 alg.efficiencyScaleFactorTool.CalibrationRelease = '250418_Preliminary_r24run3'
360 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
361 alg.muons = config.readName (self.containerName)
362 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
363 if self.saveDetailedSF:
364 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
365 'isol_effSF' + postfix)
366 sfList += [alg.scaleFactorDecoration]
367
368 # Set up the TTVA scale factor calculation algorithm:
369 if config.dataType() is not DataType.Data and self.trackSelection and not self.onlyRecoEffSF and not self.noEffSF:
370 alg = config.createAlgorithm( 'CP::MuonEfficiencyScaleFactorAlg',
371 'MuonEfficiencyScaleFactorAlgTTVA' )
372 config.addPrivateTool( 'efficiencyScaleFactorTool',
373 'CP::MuonEfficiencyScaleFactors' )
374 alg.scaleFactorDecoration = 'muon_TTVA_effSF' + postfix + "_%SYS%"
375 alg.outOfValidity = 2 #silent
376 alg.outOfValidityDeco = 'muon_TTVA_bad_eff' + postfix
377 alg.efficiencyScaleFactorTool.WorkingPoint = 'TTVA'
378 if config.geometry() >= LHCPeriod.Run3:
379 alg.efficiencyScaleFactorTool.CalibrationRelease = '250418_Preliminary_r24run3'
380 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
381 alg.muons = config.readName (self.containerName)
382 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
383 if self.saveDetailedSF:
384 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
385 'TTVA_effSF' + postfix)
386 sfList += [alg.scaleFactorDecoration]
387
388 if config.dataType() is not DataType.Data and not self.noEffSF and self.saveCombinedSF:
389 alg = config.createAlgorithm( 'CP::AsgObjectScaleFactorAlg',
390 'MuonCombinedEfficiencyScaleFactorAlg' )
391 alg.particles = config.readName (self.containerName)
392 alg.inScaleFactors = sfList
393 alg.outScaleFactor = 'effSF' + postfix + '_%SYS%'
394 config.addOutputVar (self.containerName, alg.outScaleFactor, 'effSF' + postfix)
395
396class MuonTriggerAnalysisSFBlock (ConfigBlock):
397
398 def __init__ (self) :
399 super (MuonTriggerAnalysisSFBlock, self).__init__ ()
400
401 self.addOption ('triggerChainsPerYear', {}, type=None,
402 info="a dictionary with key (string) the year and value (list of "
403 "strings) the trigger chains. The default is {} (empty dictionary).")
404 self.addOption ('muonID', '', type=str,
405 info="the muon quality WP (string) to use.")
406 self.addOption ('saveSF', True, type=bool,
407 info="define whether we decorate the trigger scale factor "
408 "The default is True.")
409 self.addOption ('saveEff', False, type=bool,
410 info="define whether we decorate the trigger MC efficiencies "
411 "The default is False.")
412 self.addOption ('saveEffData', False, type=bool,
413 info="define whether we decorate the trigger data efficiencies "
414 "The default is False.")
415 self.addOption ('prefixSF', 'trigEffSF', type=str,
416 info="the decoration prefix for trigger scale factors, "
417 "the default is 'trigEffSF'")
418 self.addOption ('prefixEff', 'trigEff', type=str,
419 info="the decoration prefix for MC trigger efficiencies, "
420 "the default is 'trigEff'")
421 self.addOption ('prefixEffData', 'trigEffData', type=str,
422 info="the decoration prefix for data trigger efficiencies, "
423 "the default is 'trigEffData'")
424 self.addOption ('includeAllYearsPerRun', False, type=bool,
425 info="if True, all configured years in the LHC run will "
426 "be included in all jobs. The default is False.")
427 self.addOption ('removeHLTPrefix', True, type=bool,
428 info="remove the HLT prefix from trigger chain names, "
429 "The default is True.")
430 self.addOption ('containerName', '', type=str,
431 info="the input muon container, with a possible selection, in "
432 "the format container or container.selection.")
433
434 def instanceName (self) :
435 return self.containerName + '_' + self.muonID
436
437 def makeAlgs (self, config) :
438
439 if config.dataType() is not DataType.Data:
440
441 # Dictionary from TrigGlobalEfficiencyCorrection/Triggers.cfg
442 # Key is trigger chain (w/o HLT prefix)
443 # Value is empty for single leg trigger or list of legs
444 triggerDict = TriggerDict()
445
447 years = [int(year) for year in self.triggerChainsPerYear.keys()]
448 else:
449 from TriggerAnalysisAlgorithms.TriggerAnalysisSFConfig import (
450 get_input_years)
451 years = get_input_years(config)
452
453 triggerYearStartBoundaries = {
454 2015: 260000,
455 2016: 290000,
456 2017: 324000,
457 2018: 348000,
458 2022: 410000,
459 2023: 450000,
460 2024: 470000,
461 }
462
463 triggerConfigs = {}
464 triggerConfigYears = {}
465 from TriggerAnalysisAlgorithms.TriggerAnalysisConfig import is_year_in_current_period
466 for year in years:
467 if not is_year_in_current_period(config, year):
468 continue
469
470 triggerChains = self.triggerChainsPerYear.get(int(year), self.triggerChainsPerYear.get(str(year), []))
471 for chain in triggerChains:
472 chain = chain.replace(" || ", "_OR_")
473 chain_noHLT = chain.replace("HLT_", "")
474 chain_out = chain_noHLT if self.removeHLTPrefix else chain
475 legs = triggerDict[chain_noHLT]
476 if not legs:
477 if chain_noHLT.startswith('mu') and chain_noHLT[2].isdigit:
478 # Need to support HLT_mu26_ivarmedium_OR_HLT_mu50
479 triggerConfigs[chain_out] = chain
480 if chain_out in triggerConfigYears.keys():
481 triggerConfigYears[chain_out].append(year)
482 else:
483 triggerConfigYears[chain_out] = [year]
484 else:
485 for leg in legs:
486 if leg.startswith('mu') and leg[2].isdigit:
487 # Need to support HLT_mu14_ivarloose
488 leg_out = leg if self.removeHLTPrefix else f"HLT_{leg}"
489 triggerConfigs[leg_out] = f"HLT_{leg}"
490 if leg_out in triggerConfigYears.keys():
491 triggerConfigYears[leg_out].append(year)
492 else:
493 triggerConfigYears[leg_out] = [year]
494
495 for trig_short, trig in triggerConfigs.items():
496 alg = config.createAlgorithm('CP::MuonTriggerEfficiencyScaleFactorAlg',
497 'MuonTrigEfficiencyCorrectionsAlg_' + trig_short)
498 config.addPrivateTool( 'efficiencyScaleFactorTool',
499 'CP::MuonTriggerScaleFactors' )
500
501 # Reproduce config from TrigGlobalEfficiencyAlg
502 alg.efficiencyScaleFactorTool.MuonQuality = self.muonID
503 alg.efficiencyScaleFactorTool.AllowZeroSF = True
504
505 # Avoid warnings for missing triggers
506 if self.includeAllYearsPerRun:
507 alg.minRunNumber = 0
508 alg.maxRunNumber = 999999
509
510 if triggerConfigYears[trig_short][0] != years[0]:
511 alg.minRunNumber = triggerYearStartBoundaries.get(triggerConfigYears[trig_short][0], 999999)
512 if triggerConfigYears[trig_short][-1] != years[-1]:
513 alg.maxRunNumber = triggerYearStartBoundaries.get(triggerConfigYears[trig_short][-1] + 1, 999999)
514 elif config.campaign() is Campaign.MC20a: # to avoid potential corner-cases keep the default config unchanged
515 if triggerConfigYears[trig_short] == [2015]:
516 alg.maxRunNumber = 290000
517 elif triggerConfigYears[trig_short] == [2016]:
518 alg.minRunNumber = 290000
519
520 alg.trigger = trig
521
522 # Some triggers in `250731_SummerUpdate` recommendations are not supported in 2022 period F
523 if config.campaign() is Campaign.MC23a and (trig_short == "HLT_mu8noL1_FSNOSEED" or trig_short == "HLT_mu22_L1MU14FCH"):
524 alg.minRunNumber = 435816 # Start of 2022 period H
525
526 if self.saveSF:
527 alg.scaleFactorDecoration = f"muon_{self.prefixSF}_{trig_short}_%SYS%"
528 if self.saveEff:
529 alg.mcEfficiencyDecoration = f"muon_{self.prefixEff}_{trig_short}_%SYS%"
530 if self.saveEffData:
531 alg.dataEfficiencyDecoration = f"muon_{self.prefixEffData}_{trig_short}_%SYS%"
532 alg.outOfValidity = 2 #silent
533 alg.outOfValidityDeco = f"bad_eff_muontrig_{trig_short}"
534 alg.muons = config.readName (self.containerName)
535 alg.preselection = config.getPreselection (self.containerName, '')
536 if self.saveSF:
537 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, f"{self.prefixSF}_{trig_short}")
538 if self.saveEff:
539 config.addOutputVar (self.containerName, alg.mcEfficiencyDecoration, f"{self.prefixEff}_{trig_short}")
540 if self.saveEffData:
541 config.addOutputVar (self.containerName, alg.dataEfficiencyDecoration, f"{self.prefixEffData}_{trig_short}")
542
543
544class MuonLRTMergedConfig (ConfigBlock) :
545 def __init__ (self) :
546 super (MuonLRTMergedConfig, self).__init__ ()
547 self.addOption (
548 'inputMuons', 'Muons', type=str,
549 noneAction='error',
550 info="the name of the input muon container."
551 )
552 self.addOption (
553 'inputLRTMuons', 'MuonsLRT', type=str,
554 noneAction='error',
555 info="the name of the input LRT muon container."
556 )
557 self.addOption (
558 'containerName', 'Muons_LRTMerged', type=str,
559 noneAction='error',
560 info="the name of the output container after LRT merging."
561 )
562
563 def instanceName (self) :
564 return self.containerName
565
566 def makeAlgs (self, config) :
567
568 if config.isPhyslite() :
569 raise(RuntimeError("Muon LRT merging is not available in Physlite mode"))
570
571 alg = config.createAlgorithm( "CP::MuonLRTMergingAlg", "MuonLRTMergingAlg" )
572 alg.PromptMuonLocation = self.inputMuons
573 alg.LRTMuonLocation = self.inputLRTMuons
574 alg.OutputMuonLocation = self.containerName
575 alg.UseRun3WP = config.geometry() >= LHCPeriod.Run3
576 alg.CreateViewCollection = False
577
578class MuonContainerMergingConfig (ConfigBlock) :
579 def __init__ (self) :
580 super (MuonContainerMergingConfig, self).__init__ ()
581 self.addOption (
582 'inputMuonContainers', [], type=list,
583 noneAction='error',
584 info="List of container names to be merged of type xAOD::MuonContainer."
585 )
586 self.addOption (
587 'outputMuonLocation', 'MuonsMerged', type=str,
588 noneAction='error',
589 info="The name of the output container."
590 )
591 self.addOption (
592 'createViewCollection', True, type=bool,
593 info="Decided if output container is a view (default) or a deep copy."
594 )
595
596 def instanceName (self) :
597 return self.outputMuonLocation
598
599 def makeAlgs (self, config) :
600 alg = config.createAlgorithm( "CP::MuonContainerMergingAlg", "MuonContainerMergingAlg" )
601 alg.InputMuonContainers = self.inputMuonContainers
602 alg.OutputMuonLocation = self.outputMuonLocation
603 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