4 from AnalysisAlgorithmsConfig.ConfigBlock
import ConfigBlock
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
14 """the ConfigBlock for the muon four-momentum correction"""
17 super (MuonCalibrationConfig, self).__init__ ()
18 self.setBlockName(
'Muons')
19 self.addOption (
'containerName', containerName, type=str,
21 info=
"the name of the output container after calibration.")
22 self.addOption (
'postfix',
"", type=str,
23 info=
"a postfix to apply to decorations and algorithm names. "
24 "Typically not needed here since the calibration is common to "
26 self.addOption (
'minPt', 3.0*GeV, type=float,
27 info=
"pT cut to apply to calibrated muons, in MeV. "
28 "The default is 3.0 GeV.")
29 self.addOption (
'recalibratePhyslite',
True, type=bool,
30 info=
"whether to run the CP::EgammaCalibrationAndSmearingAlg on "
31 "PHYSLITE derivations. The default is True.")
32 self.addOption (
'maxEta', 2.7, type=float,
33 info=
"maximum muon |eta| (float). The default is 2.7.")
34 self.addOption (
'excludeNSWFromPrecisionLayers',
False, type=bool,
35 info=
"only for testing purposes, turn on to ignore NSW hits and "
36 "fix a crash with older derivations (p-tag <p5834)")
37 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)')
45 elif self.
calibMode ==
'correctData_IDMS':
47 elif self.
calibMode ==
'notCorrectData_IDMS':
49 elif self.
calibMode ==
'notCorrectData_CB':
52 raise ValueError (
"invalid calibMode: \"" + self.
calibMode +
"\". Allowed values are correctData_CB, correctData_IDMS, notCorrectData_IDMS, notCorrectData_CB")
54 if config.isPhyslite() :
55 config.setSourceName (self.containerName,
"AnalysisMuons")
57 config.setSourceName (self.containerName,
"Muons")
60 if config.wantCopy (self.containerName) :
61 alg = config.createAlgorithm(
'CP::AsgShallowCopyAlg',
'MuonShallowCopyAlg' + self.postfix )
62 alg.input = config.readName (self.containerName)
63 alg.output = config.copyName (self.containerName)
66 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
67 'MuonEtaCutAlg' + self.postfix )
68 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
69 alg.selectionTool.maxEta = self.maxEta
70 alg.selectionDecoration =
'selectEta' + self.postfix +
',as_bits'
71 alg.particles = config.readName (self.containerName)
72 alg.preselection = config.getPreselection (self.containerName,
'')
73 config.addSelection (self.containerName,
'', alg.selectionDecoration)
76 alg = config.createAlgorithm(
'CP::MuonCalibrationAndSmearingAlg',
77 'MuonCalibrationAndSmearingAlg' + self.postfix )
78 config.addPrivateTool(
'calibrationAndSmearingTool',
81 alg.calibrationAndSmearingTool.IsRun3Geo = config.geometry() >= LHCPeriod.Run3
82 alg.calibrationAndSmearingTool.calibMode = calibMode
84 config._muonCalibMode = alg.calibrationAndSmearingTool.calibMode
85 if config.geometry()
is LHCPeriod.Run4:
86 logging.warning(
"MuonCalibrationConfig: disabling NSW hits for Run4 geometry")
87 alg.calibrationAndSmearingTool.ExcludeNSWFromPrecisionLayers =
True
89 alg.calibrationAndSmearingTool.ExcludeNSWFromPrecisionLayers = self.excludeNSWFromPrecisionLayers
and (config.geometry() >= LHCPeriod.Run3)
90 alg.muons = config.readName (self.containerName)
91 alg.muonsOut = config.copyName (self.containerName)
92 alg.preselection = config.getPreselection (self.containerName,
'')
93 if config.isPhyslite()
and not self.recalibratePhyslite :
94 alg.skipNominal =
True
98 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'MuonPtCutAlg' + self.postfix )
99 alg.selectionDecoration =
'selectPt' + self.postfix +
',as_bits'
100 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
101 alg.particles = config.readName (self.containerName)
102 alg.selectionTool.minPt = self.minPt
103 alg.preselection = config.getPreselection (self.containerName,
'')
104 config.addSelection (self.containerName,
'', alg.selectionDecoration,
108 alg = config.createAlgorithm(
'CP::AsgEnergyDecoratorAlg',
'EnergyDecorator' + self.containerName + self.postfix )
109 alg.particles = config.readName (self.containerName)
111 config.addOutputVar (self.containerName,
'pt',
'pt')
112 config.addOutputVar (self.containerName,
'eta',
'eta', noSys=
True)
113 config.addOutputVar (self.containerName,
'phi',
'phi', noSys=
True)
114 config.addOutputVar (self.containerName,
'e_%SYS%',
'e')
115 config.addOutputVar (self.containerName,
'charge',
'charge', noSys=
True)
119 """the ConfigBlock for the muon working point
121 This may at some point be split into multiple blocks (10 Mar 22)."""
123 def __init__ (self, containerName='', selectionName='') :
124 super (MuonWorkingPointConfig, self).__init__ ()
125 self.addOption (
'containerName', containerName, type=str,
127 info=
"the name of the input container.")
128 self.addOption (
'selectionName', selectionName, type=str,
130 info=
"the name of the muon selection to define (e.g. tight or loose).")
131 self.addOption (
'postfix', selectionName, type=str,
132 info=
"a postfix to apply to decorations and algorithm names. "
133 "Typically not needed here as selectionName is used internally.")
134 self.addOption (
'trackSelection',
True, type=bool,
135 info=
"whether or not to set up an instance of "
136 "CP::AsgLeptonTrackSelectionAlg, with the recommended d_0 and "
137 "z_0 sin(theta) cuts. The default is True.")
138 self.addOption (
'maxD0Significance', 3, type=float,
139 info=
"maximum d0 significance used for the trackSelection"
141 self.addOption (
'maxDeltaZ0SinTheta', 0.5, type=float,
142 info=
"maximum Delta z0sinTheta in mm used for the trackSelection"
143 "The default is 0.5 mm")
144 self.addOption (
'writeTrackD0Z0',
False, type = bool,
145 info=
"save the d0 significance and z0sinTheta variables so they can be written out")
146 self.addOption (
'quality',
None, type=str,
147 info=
"the ID WP (string) to use. Supported ID WPs: Tight, Medium, "
148 "Loose, LowPt, HighPt.")
149 self.addOption (
'isolation',
None, type=str,
150 info=
"the isolation WP (string) to use. Supported isolation WPs: "
151 "PflowLoose_VarRad, PflowTight_VarRad, Loose_VarRad, "
152 "Tight_VarRad, NonIso.")
153 self.addOption (
'qualitySelectionOutput',
True, type=bool,
154 info=
"whether to retain only muons satisfying the quality "
155 "requirements (bad muon veto). The default is True.")
156 self.addOption (
'closeByCorrection',
False, type=bool,
157 info=
"whether to use close-by-corrected isolation working points.")
158 self.addOption (
'systematicBreakdown',
False, type=bool,
159 info=
"enables the full breakdown of efficiency SF systematics "
160 "(1 NP per uncertainty source, instead of 1 NP in total). "
161 "The default is False.")
162 self.addOption (
'onlyRecoEffSF',
False, type=bool,
163 info=
"same as noEffSF, but retains the ID scale factor. "
164 "Experimental! only useful for CI tests. The default is False.")
165 self.addOption (
'noEffSF',
False, type=bool,
166 info=
"disables the calculation of efficiencies and scale factors. "
167 "Experimental! only useful to test a new WP for which scale "
168 "factors are not available. The default is False.")
169 self.addOption (
'excludeNSWFromPrecisionLayers',
False, type=bool,
170 info=
"only for testing purposes, turn on to ignore NSW hits and "
171 "fix a crash with older derivations (p-tag <p5834)")
174 from xAODMuon.xAODMuonEnums
import xAODMuonEnums
176 quality = xAODMuonEnums.Quality.Tight
177 elif self.
quality ==
'Medium' :
178 quality = xAODMuonEnums.Quality.Medium
180 quality = xAODMuonEnums.Quality.Loose
181 elif self.
quality ==
'VeryLoose' :
182 quality = xAODMuonEnums.Quality.VeryLoose
183 elif self.
quality ==
'HighPt' :
185 elif self.
quality ==
'LowPtEfficiency' :
188 raise ValueError (
"invalid muon quality: \"" + self.
quality +
189 "\", allowed values are Tight, Medium, Loose, " +
190 "VeryLoose, HighPt, LowPtEfficiency")
193 if config.geometry()
is LHCPeriod.Run1:
194 raise ValueError (
"Can't set up the MuonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
196 postfix = self.postfix
197 if postfix !=
'' and postfix[0] !=
'_' :
198 postfix =
'_' + postfix
201 if self.writeTrackD0Z0
or self.trackSelection:
202 alg = config.createAlgorithm(
'CP::AsgLeptonTrackSelectionAlg',
203 'MuonTrackSelectionAlg' + postfix )
204 alg.selectionDecoration =
'trackSelection' + postfix +
',as_bits'
205 alg.decorateTTVAVars = self.writeTrackD0Z0
206 alg.maxD0Significance = self.maxD0Significance
207 alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta
208 alg.particles = config.readName (self.containerName)
209 alg.preselection = config.getPreselection (self.containerName,
'')
210 if self.trackSelection :
211 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration, preselection=self.qualitySelectionOutput)
212 if self.writeTrackD0Z0 :
213 alg.d0sigDecoration =
'd0sig' + postfix
214 alg.z0sinthetaDecoration =
'z0sintheta' + postfix
215 config.addOutputVar (self.containerName, alg.d0sigDecoration, alg.d0sigDecoration,noSys=
True)
216 config.addOutputVar (self.containerName, alg.z0sinthetaDecoration, alg.z0sinthetaDecoration,noSys=
True)
219 alg = config.createAlgorithm(
'CP::MuonSelectionAlgV2',
220 'MuonSelectionAlg' + postfix )
221 config.addPrivateTool(
'selectionTool',
'CP::MuonSelectionTool' )
222 alg.selectionTool.MuQuality = quality
223 alg.selectionTool.IsRun3Geo = config.geometry() >= LHCPeriod.Run3
224 if config.geometry()
is LHCPeriod.Run4:
225 logging.warning(
"MuonCalibrationConfig: disabling NSW hits for Run4 geometry")
226 alg.selectionTool.ExcludeNSWFromPrecisionLayers =
True
228 alg.selectionTool.ExcludeNSWFromPrecisionLayers = self.excludeNSWFromPrecisionLayers
and (config.geometry() >= LHCPeriod.Run3)
229 alg.selectionDecoration =
'good_muon' + postfix +
',as_bits'
230 alg.badMuonVetoDecoration =
'is_bad' + postfix +
',as_char'
231 alg.muons = config.readName (self.containerName)
232 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
233 config.addSelection (self.containerName, self.selectionName,
234 alg.selectionDecoration,
235 preselection=self.qualitySelectionOutput)
238 if self.isolation !=
'NonIso' :
239 alg = config.createAlgorithm(
'CP::MuonIsolationAlg',
240 'MuonIsolationAlg' + postfix )
241 config.addPrivateTool(
'isolationTool',
'CP::IsolationSelectionTool' )
242 alg.isolationTool.MuonWP = self.isolation
243 if self.closeByCorrection:
244 alg.isolationTool.IsoDecSuffix =
"CloseByCorr"
245 alg.isolationDecoration =
'isolated_muon' + postfix +
',as_bits'
246 alg.muons = config.readName (self.containerName)
247 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
248 config.addSelection (self.containerName, self.selectionName,
249 alg.isolationDecoration,
250 preselection=self.qualitySelectionOutput)
253 if config.dataType()
is not DataType.Data
and (
not self.noEffSF
or self.onlyRecoEffSF):
254 alg = config.createAlgorithm(
'CP::MuonEfficiencyScaleFactorAlg',
255 'MuonEfficiencyScaleFactorAlgReco' + postfix )
256 config.addPrivateTool(
'efficiencyScaleFactorTool',
257 'CP::MuonEfficiencyScaleFactors' )
258 alg.scaleFactorDecoration =
'muon_reco_effSF' + postfix +
"_%SYS%"
259 alg.outOfValidity = 2
260 alg.outOfValidityDeco =
'muon_reco_bad_eff' + postfix
261 alg.efficiencyScaleFactorTool.WorkingPoint = self.
quality
262 if config.geometry() >= LHCPeriod.Run3:
263 alg.efficiencyScaleFactorTool.CalibrationRelease =
'240711_Preliminary_r24run3'
264 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
265 alg.muons = config.readName (self.containerName)
266 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
267 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'reco_effSF' + postfix)
270 if config.dataType()
is not DataType.Data
and self.
quality ==
'HighPt' and not self.onlyRecoEffSF
and not self.noEffSF:
271 alg = config.createAlgorithm(
'CP::MuonEfficiencyScaleFactorAlg',
272 'MuonEfficiencyScaleFactorAlgBMVHighPt' + postfix )
273 config.addPrivateTool(
'efficiencyScaleFactorTool',
274 'CP::MuonEfficiencyScaleFactors' )
275 alg.scaleFactorDecoration =
'muon_BadMuonVeto_effSF' + postfix +
"_%SYS%"
276 alg.outOfValidity = 2
277 alg.outOfValidityDeco =
'muon_BadMuonVeto_bad_eff' + postfix
278 alg.efficiencyScaleFactorTool.WorkingPoint =
'BadMuonVeto_HighPt'
279 if config.geometry() >= LHCPeriod.Run3:
280 alg.efficiencyScaleFactorTool.CalibrationRelease =
'220817_Preliminary_r22run3'
281 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
282 alg.muons = config.readName (self.containerName)
283 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
284 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'BadMuonVeto_effSF' + postfix)
287 if config.dataType()
is not DataType.Data
and self.isolation !=
'NonIso' and not self.onlyRecoEffSF
and not self.noEffSF:
288 alg = config.createAlgorithm(
'CP::MuonEfficiencyScaleFactorAlg',
289 'MuonEfficiencyScaleFactorAlgIsol' + postfix )
290 config.addPrivateTool(
'efficiencyScaleFactorTool',
291 'CP::MuonEfficiencyScaleFactors' )
292 alg.scaleFactorDecoration =
'muon_isol_effSF' + postfix +
"_%SYS%"
293 alg.outOfValidity = 2
294 alg.outOfValidityDeco =
'muon_isol_bad_eff' + postfix
295 alg.efficiencyScaleFactorTool.WorkingPoint = self.isolation +
'Iso'
296 if config.geometry() >= LHCPeriod.Run3:
297 alg.efficiencyScaleFactorTool.CalibrationRelease =
'240711_Preliminary_r24run3'
298 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
299 alg.muons = config.readName (self.containerName)
300 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
301 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'isol_effSF' + postfix)
304 if config.dataType()
is not DataType.Data
and not self.onlyRecoEffSF
and not self.noEffSF:
305 alg = config.createAlgorithm(
'CP::MuonEfficiencyScaleFactorAlg',
306 'MuonEfficiencyScaleFactorAlgTTVA' + postfix )
307 config.addPrivateTool(
'efficiencyScaleFactorTool',
308 'CP::MuonEfficiencyScaleFactors' )
309 alg.scaleFactorDecoration =
'muon_TTVA_effSF' + postfix +
"_%SYS%"
310 alg.outOfValidity = 2
311 alg.outOfValidityDeco =
'muon_TTVA_bad_eff' + postfix
312 alg.efficiencyScaleFactorTool.WorkingPoint =
'TTVA'
313 if config.geometry() >= LHCPeriod.Run3:
314 alg.efficiencyScaleFactorTool.CalibrationRelease =
'240711_Preliminary_r24run3'
315 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
316 alg.muons = config.readName (self.containerName)
317 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
318 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'TTVA_effSF' + postfix)
323 """Create muon calibration analysis algorithms
325 This makes all the algorithms that need to be run first befor
326 all working point specific algorithms and that can be shared
327 between the working points.
330 containerName -- name of the output container
331 postfix -- a postfix to apply to decorations and algorithm
332 names. this is mostly used/needed when using this
333 sequence with multiple working points to ensure all
337 config = MuonCalibrationConfig (containerName)
338 config.setOptionValue (
'postfix', postfix)
346 qualitySelectionOutput = None,
347 systematicBreakdown = None,
349 onlyRecoEffSF = None ):
350 """Create muon analysis algorithms for a single working point
353 workingPoint -- The working point to use
354 selectionName -- a postfix to apply to decorations and algorithm
355 names. this is mostly used/needed when using this
356 sequence with multiple working points to ensure all
358 qualitySelectionOutput -- Whether or not to apply muon quality selection
359 when creating output containers.
360 systematicBreakdown -- enables the full breakdown of eff SF systematics
361 noEffSF -- Disables the calculation of efficiencies and scale factors
362 onlyRecoEffSF -- Only enables the reconstruction scale factor
366 config = MuonWorkingPointConfig (containerName, selectionName)
367 if workingPoint
is not None :
368 splitWP = workingPoint.split (
'.')
369 if len (splitWP) != 2 :
370 raise ValueError (
'working point should be of format "quality.isolation", not ' + workingPoint)
371 config.setOptionValue (
'quality', splitWP[0])
372 config.setOptionValue (
'isolation', splitWP[1])
373 config.setOptionValue (
'qualitySelectionOutput', qualitySelectionOutput)
374 config.setOptionValue (
'systematicBreakdown', systematicBreakdown)
375 config.setOptionValue (
'noEffSF', noEffSF)
376 config.setOptionValue (
'onlyRecoEffSF', onlyRecoEffSF)
383 super (MuonTriggerAnalysisSFBlock, self).__init__ ()
385 self.addOption (
'triggerChainsPerYear', {}, type=
None,
386 info=
"a dictionary with key (string) the year and value (list of "
387 "strings) the trigger chains. The default is {} (empty dictionary).")
388 self.addOption (
'muonID',
'', type=str,
389 info=
"the muon quality WP (string) to use.")
390 self.addOption (
'saveEff',
False, type=bool,
391 info=
"define whether we decorate also the trigger scale efficiency "
392 "The default is false.")
393 self.addOption (
'containerName',
'', type=str,
394 info=
"the input muon container, with a possible selection, in "
395 "the format container or container.selection.")
399 if config.dataType()
is not DataType.Data:
406 if config.campaign()
is Campaign.MC20a:
407 years = [
'2015',
'2016']
408 elif config.campaign()
is Campaign.MC20d:
410 elif config.campaign()
is Campaign.MC20e:
412 elif config.campaign()
in [Campaign.MC21a, Campaign.MC23a]:
414 elif config.campaign()
in [Campaign.MC23c, Campaign.MC23d]:
418 triggerConfigYears = {}
420 triggerChains = self.triggerChainsPerYear.
get(year,[])
421 for chain
in triggerChains:
422 chain = chain.replace(
" || ",
"_OR_")
423 chain_noHLT = chain.replace(
"HLT_",
"")
424 legs = triggerDict[chain_noHLT]
426 if chain_noHLT.startswith(
'mu')
and chain_noHLT[2].isdigit:
428 triggerConfigs[chain_noHLT] = chain
429 if chain_noHLT
in triggerConfigYears.keys():
430 triggerConfigYears[chain_noHLT].
append(year)
432 triggerConfigYears[chain_noHLT] = [year]
435 if leg.startswith(
'mu')
and leg[2].isdigit:
437 triggerConfigs[leg] =
'HLT_' + leg
438 if chain_noHLT
in triggerConfigYears.keys():
439 triggerConfigYears[leg].
append(year)
441 triggerConfigYears[leg] = [year]
443 for trig_short, trig
in triggerConfigs.items():
444 alg = config.createAlgorithm(
'CP::MuonTriggerEfficiencyScaleFactorAlg',
445 'MuonTrigEfficiencyCorrectionsAlg_' + trig_short)
446 config.addPrivateTool(
'efficiencyScaleFactorTool',
447 'CP::MuonTriggerScaleFactors' )
450 alg.efficiencyScaleFactorTool.MuonQuality = self.muonID
451 alg.efficiencyScaleFactorTool.AllowZeroSF =
True
454 if config.campaign()
is Campaign.MC20a:
455 if triggerConfigYears[trig_short] == [
'2015']:
456 alg.maxRunNumber = 290000
457 elif triggerConfigYears[trig_short] == [
'2016']:
458 alg.minRunNumber = 290000
461 alg.scaleFactorDecoration =
'muon_trigEffSF_' + trig_short +
'_%SYS%'
463 alg.mcEfficiencyDecoration =
'muon_trigEff_' + trig_short +
'_%SYS%'
464 alg.outOfValidity = 2
465 alg.outOfValidityDeco =
'bad_eff_muontrig_' + trig_short
466 alg.muons = config.readName (self.containerName)
467 alg.preselection = config.getPreselection (self.containerName,
'')
468 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'trigEffSF_' + trig_short)
470 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'trigEff_' + trig_short)