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