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