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)')
38 self.addOption (
'decorateTruth',
False, type=bool,
39 info=
"decorate truth particle information on the reconstructed one")
47 elif self.
calibMode ==
'correctData_IDMS':
49 elif self.
calibMode ==
'notCorrectData_IDMS':
51 elif self.
calibMode ==
'notCorrectData_CB':
54 raise ValueError (
"invalid calibMode: \"" + self.
calibMode +
"\". Allowed values are correctData_CB, correctData_IDMS, notCorrectData_IDMS, notCorrectData_CB")
56 config.setSourceName (self.containerName,
57 "AnalysisMuons" if config.isPhyslite()
else "Muons",
61 if config.wantCopy (self.containerName) :
62 alg = config.createAlgorithm(
'CP::AsgShallowCopyAlg',
'MuonShallowCopyAlg' + self.postfix )
63 alg.input = config.readName (self.containerName)
64 alg.output = config.copyName (self.containerName)
67 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
68 'MuonEtaCutAlg' + self.postfix )
69 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
70 alg.selectionTool.maxEta = self.maxEta
71 alg.selectionDecoration =
'selectEta' + self.postfix +
',as_bits'
72 alg.particles = config.readName (self.containerName)
73 alg.preselection = config.getPreselection (self.containerName,
'')
74 config.addSelection (self.containerName,
'', alg.selectionDecoration)
77 alg = config.createAlgorithm(
'CP::MuonCalibrationAndSmearingAlg',
78 'MuonCalibrationAndSmearingAlg' + self.postfix )
79 config.addPrivateTool(
'calibrationAndSmearingTool',
82 alg.calibrationAndSmearingTool.IsRun3Geo = config.geometry() >= LHCPeriod.Run3
83 alg.calibrationAndSmearingTool.calibMode = calibMode
84 if config.geometry()
is LHCPeriod.Run4:
85 logging.warning(
"MuonCalibrationConfig: disabling NSW hits for Run4 geometry")
86 alg.calibrationAndSmearingTool.ExcludeNSWFromPrecisionLayers =
True
88 alg.calibrationAndSmearingTool.ExcludeNSWFromPrecisionLayers = self.excludeNSWFromPrecisionLayers
and (config.geometry() >= LHCPeriod.Run3)
89 alg.muons = config.readName (self.containerName)
90 alg.muonsOut = config.copyName (self.containerName)
91 alg.preselection = config.getPreselection (self.containerName,
'')
92 if config.isPhyslite()
and not self.recalibratePhyslite :
93 alg.skipNominal =
True
97 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'MuonPtCutAlg' + self.postfix )
98 alg.selectionDecoration =
'selectPt' + self.postfix +
',as_bits'
99 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
100 alg.particles = config.readName (self.containerName)
101 alg.selectionTool.minPt = self.minPt
102 alg.preselection = config.getPreselection (self.containerName,
'')
103 config.addSelection (self.containerName,
'', alg.selectionDecoration,
107 alg = config.createAlgorithm(
'CP::AsgEnergyDecoratorAlg',
'EnergyDecorator' + self.containerName + self.postfix )
108 alg.particles = config.readName (self.containerName)
110 config.addOutputVar (self.containerName,
'pt',
'pt')
111 config.addOutputVar (self.containerName,
'eta',
'eta', noSys=
True)
112 config.addOutputVar (self.containerName,
'phi',
'phi', noSys=
True)
113 config.addOutputVar (self.containerName,
'e_%SYS%',
'e')
114 config.addOutputVar (self.containerName,
'charge',
'charge', noSys=
True)
117 if self.decorateTruth
and config.dataType()
is not DataType.Data:
118 config.addOutputVar (self.containerName,
"truthType",
"truth_type", noSys=
True)
119 config.addOutputVar (self.containerName,
"truthOrigin",
"truth_origin", noSys=
True)
122 """the ConfigBlock for the muon working point
124 This may at some point be split into multiple blocks (10 Mar 22)."""
126 def __init__ (self, containerName='', selectionName='') :
127 super (MuonWorkingPointConfig, self).__init__ ()
128 self.setBlockName(
'MuonsWorkingPoint')
129 self.addOption (
'containerName', containerName, type=str,
131 info=
"the name of the input container.")
132 self.addOption (
'selectionName', selectionName, type=str,
134 info=
"the name of the muon selection to define (e.g. tight or loose).")
135 self.addOption (
'postfix', selectionName, type=str,
136 info=
"a postfix to apply to decorations and algorithm names. "
137 "Typically not needed here as selectionName is used internally.")
138 self.addOption (
'trackSelection',
True, type=bool,
139 info=
"whether or not to set up an instance of "
140 "CP::AsgLeptonTrackSelectionAlg, with the recommended d_0 and "
141 "z_0 sin(theta) cuts. The default is True.")
142 self.addOption (
'maxD0Significance', 3, type=float,
143 info=
"maximum d0 significance used for the trackSelection"
145 self.addOption (
'maxDeltaZ0SinTheta', 0.5, type=float,
146 info=
"maximum Delta z0sinTheta in mm used for the trackSelection"
147 "The default is 0.5 mm")
148 self.addOption (
'writeTrackD0Z0',
False, type = bool,
149 info=
"save the d0 significance and z0sinTheta variables so they can be written out")
150 self.addOption (
'quality',
None, type=str,
151 info=
"the ID WP (string) to use. Supported ID WPs: Tight, Medium, "
152 "Loose, LowPt, HighPt.")
153 self.addOption (
'isolation',
None, type=str,
154 info=
"the isolation WP (string) to use. Supported isolation WPs: "
155 "PflowLoose_VarRad, PflowTight_VarRad, Loose_VarRad, "
156 "Tight_VarRad, NonIso.")
157 self.addOption (
'addSelectionToPreselection',
True, type=bool,
158 info=
"whether to retain only muons satisfying the working point "
159 "requirements. The default is True.")
160 self.addOption (
'closeByCorrection',
False, type=bool,
161 info=
"whether to use close-by-corrected isolation working points.")
162 self.addOption (
'systematicBreakdown',
False, type=bool,
163 info=
"enables the full breakdown of efficiency SF systematics "
164 "(1 NP per uncertainty source, instead of 1 NP in total). "
165 "The default is False.")
166 self.addOption (
'onlyRecoEffSF',
False, type=bool,
167 info=
"same as noEffSF, but retains the ID scale factor. "
168 "Experimental! only useful for CI tests. The default is False.")
169 self.addOption (
'noEffSF',
False, type=bool,
170 info=
"disables the calculation of efficiencies and scale factors. "
171 "Experimental! only useful to test a new WP for which scale "
172 "factors are not available. The default is False.")
173 self.addOption (
'excludeNSWFromPrecisionLayers',
False, type=bool,
174 info=
"only for testing purposes, turn on to ignore NSW hits and "
175 "fix a crash with older derivations (p-tag <p5834)")
178 from xAODMuon.xAODMuonEnums
import xAODMuonEnums
180 quality = xAODMuonEnums.Quality.Tight
181 elif self.
quality ==
'Medium' :
182 quality = xAODMuonEnums.Quality.Medium
184 quality = xAODMuonEnums.Quality.Loose
185 elif self.
quality ==
'VeryLoose' :
186 quality = xAODMuonEnums.Quality.VeryLoose
187 elif self.
quality ==
'HighPt' :
189 elif self.
quality ==
'LowPtEfficiency' :
192 raise ValueError (
"invalid muon quality: \"" + self.
quality +
193 "\", allowed values are Tight, Medium, Loose, " +
194 "VeryLoose, HighPt, LowPtEfficiency")
197 if config.geometry()
is LHCPeriod.Run1:
198 raise ValueError (
"Can't set up the MuonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
200 postfix = self.postfix
201 if postfix !=
'' and postfix[0] !=
'_' :
202 postfix =
'_' + postfix
205 if self.writeTrackD0Z0
or self.trackSelection:
206 alg = config.createAlgorithm(
'CP::AsgLeptonTrackSelectionAlg',
207 'MuonTrackSelectionAlg' + postfix )
208 alg.selectionDecoration =
'trackSelection' + postfix +
',as_bits'
209 alg.decorateTTVAVars = self.writeTrackD0Z0
210 alg.maxD0Significance = self.maxD0Significance
211 alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta
212 alg.particles = config.readName (self.containerName)
213 alg.preselection = config.getPreselection (self.containerName,
'')
214 if self.trackSelection :
215 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration, preselection=self.addSelectionToPreselection)
216 if self.writeTrackD0Z0 :
217 alg.d0sigDecoration =
'd0sig' + postfix
218 alg.z0sinthetaDecoration =
'z0sintheta' + postfix
219 config.addOutputVar (self.containerName, alg.d0sigDecoration, alg.d0sigDecoration,noSys=
True)
220 config.addOutputVar (self.containerName, alg.z0sinthetaDecoration, alg.z0sinthetaDecoration,noSys=
True)
223 alg = config.createAlgorithm(
'CP::MuonSelectionAlgV2',
224 'MuonSelectionAlg' + postfix )
225 config.addPrivateTool(
'selectionTool',
'CP::MuonSelectionTool' )
226 alg.selectionTool.MuQuality = quality
227 alg.selectionTool.IsRun3Geo = config.geometry() >= LHCPeriod.Run3
228 if config.geometry()
is LHCPeriod.Run4:
229 logging.warning(
"MuonCalibrationConfig: disabling NSW hits for Run4 geometry")
230 alg.selectionTool.ExcludeNSWFromPrecisionLayers =
True
232 alg.selectionTool.ExcludeNSWFromPrecisionLayers = self.excludeNSWFromPrecisionLayers
and (config.geometry() >= LHCPeriod.Run3)
233 alg.selectionDecoration =
'good_muon' + postfix +
',as_char'
234 alg.badMuonVetoDecoration =
'is_bad' + postfix +
',as_char'
235 alg.muons = config.readName (self.containerName)
236 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
237 config.addSelection (self.containerName, self.selectionName,
238 alg.selectionDecoration,
239 preselection=self.addSelectionToPreselection)
242 if self.isolation !=
'NonIso' :
243 alg = config.createAlgorithm(
'CP::MuonIsolationAlg',
244 'MuonIsolationAlg' + postfix )
245 config.addPrivateTool(
'isolationTool',
'CP::IsolationSelectionTool' )
246 alg.isolationTool.MuonWP = self.isolation
247 if self.closeByCorrection:
248 alg.isolationTool.IsoDecSuffix =
"CloseByCorr"
249 alg.isolationDecoration =
'isolated_muon' + postfix +
',as_char'
250 alg.muons = config.readName (self.containerName)
251 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
252 config.addSelection (self.containerName, self.selectionName,
253 alg.isolationDecoration,
254 preselection=self.addSelectionToPreselection)
257 if config.dataType()
is not DataType.Data
and (
not self.noEffSF
or self.onlyRecoEffSF):
258 alg = config.createAlgorithm(
'CP::MuonEfficiencyScaleFactorAlg',
259 'MuonEfficiencyScaleFactorAlgReco' + postfix )
260 config.addPrivateTool(
'efficiencyScaleFactorTool',
261 'CP::MuonEfficiencyScaleFactors' )
262 alg.scaleFactorDecoration =
'muon_reco_effSF' + postfix +
"_%SYS%"
263 alg.outOfValidity = 2
264 alg.outOfValidityDeco =
'muon_reco_bad_eff' + postfix
265 alg.efficiencyScaleFactorTool.WorkingPoint = self.
quality
266 if config.geometry() >= LHCPeriod.Run3:
267 alg.efficiencyScaleFactorTool.CalibrationRelease =
'240711_Preliminary_r24run3'
268 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
269 alg.muons = config.readName (self.containerName)
270 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
271 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'reco_effSF' + postfix)
274 if config.dataType()
is not DataType.Data
and self.
quality ==
'HighPt' and not self.onlyRecoEffSF
and not self.noEffSF:
275 alg = config.createAlgorithm(
'CP::MuonEfficiencyScaleFactorAlg',
276 'MuonEfficiencyScaleFactorAlgBMVHighPt' + postfix )
277 config.addPrivateTool(
'efficiencyScaleFactorTool',
278 'CP::MuonEfficiencyScaleFactors' )
279 alg.scaleFactorDecoration =
'muon_BadMuonVeto_effSF' + postfix +
"_%SYS%"
280 alg.outOfValidity = 2
281 alg.outOfValidityDeco =
'muon_BadMuonVeto_bad_eff' + postfix
282 alg.efficiencyScaleFactorTool.WorkingPoint =
'BadMuonVeto_HighPt'
283 if config.geometry() >= LHCPeriod.Run3:
284 alg.efficiencyScaleFactorTool.CalibrationRelease =
'220817_Preliminary_r22run3'
285 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
286 alg.muons = config.readName (self.containerName)
287 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
288 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'BadMuonVeto_effSF' + postfix)
291 if config.dataType()
is not DataType.Data
and self.isolation !=
'NonIso' and not self.onlyRecoEffSF
and not self.noEffSF:
292 alg = config.createAlgorithm(
'CP::MuonEfficiencyScaleFactorAlg',
293 'MuonEfficiencyScaleFactorAlgIsol' + postfix )
294 config.addPrivateTool(
'efficiencyScaleFactorTool',
295 'CP::MuonEfficiencyScaleFactors' )
296 alg.scaleFactorDecoration =
'muon_isol_effSF' + postfix +
"_%SYS%"
297 alg.outOfValidity = 2
298 alg.outOfValidityDeco =
'muon_isol_bad_eff' + postfix
299 alg.efficiencyScaleFactorTool.WorkingPoint = self.isolation +
'Iso'
300 if config.geometry() >= LHCPeriod.Run3:
301 alg.efficiencyScaleFactorTool.CalibrationRelease =
'240711_Preliminary_r24run3'
302 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
303 alg.muons = config.readName (self.containerName)
304 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
305 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'isol_effSF' + postfix)
308 if config.dataType()
is not DataType.Data
and not self.onlyRecoEffSF
and not self.noEffSF:
309 alg = config.createAlgorithm(
'CP::MuonEfficiencyScaleFactorAlg',
310 'MuonEfficiencyScaleFactorAlgTTVA' + postfix )
311 config.addPrivateTool(
'efficiencyScaleFactorTool',
312 'CP::MuonEfficiencyScaleFactors' )
313 alg.scaleFactorDecoration =
'muon_TTVA_effSF' + postfix +
"_%SYS%"
314 alg.outOfValidity = 2
315 alg.outOfValidityDeco =
'muon_TTVA_bad_eff' + postfix
316 alg.efficiencyScaleFactorTool.WorkingPoint =
'TTVA'
317 if config.geometry() >= LHCPeriod.Run3:
318 alg.efficiencyScaleFactorTool.CalibrationRelease =
'240711_Preliminary_r24run3'
319 alg.efficiencyScaleFactorTool.BreakDownSystematics = self.systematicBreakdown
320 alg.muons = config.readName (self.containerName)
321 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
322 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'TTVA_effSF' + postfix)
327 """Create muon calibration analysis algorithms
329 This makes all the algorithms that need to be run first befor
330 all working point specific algorithms and that can be shared
331 between the working points.
334 containerName -- name of the output container
335 postfix -- a postfix to apply to decorations and algorithm
336 names. this is mostly used/needed when using this
337 sequence with multiple working points to ensure all
341 config = MuonCalibrationConfig (containerName)
342 config.setOptionValue (
'postfix', postfix)
350 addSelectionToPreselection = None,
351 systematicBreakdown = None,
353 onlyRecoEffSF = None ):
354 """Create muon analysis algorithms for a single working point
357 workingPoint -- The working point to use
358 selectionName -- a postfix to apply to decorations and algorithm
359 names. this is mostly used/needed when using this
360 sequence with multiple working points to ensure all
362 addSelectionToPreselection -- Whether or not to apply muon quality selection
363 when creating output containers.
364 systematicBreakdown -- enables the full breakdown of eff SF systematics
365 noEffSF -- Disables the calculation of efficiencies and scale factors
366 onlyRecoEffSF -- Only enables the reconstruction scale factor
370 config = MuonWorkingPointConfig (containerName, selectionName)
371 if workingPoint
is not None :
372 splitWP = workingPoint.split (
'.')
373 if len (splitWP) != 2 :
374 raise ValueError (
'working point should be of format "quality.isolation", not ' + workingPoint)
375 config.setOptionValue (
'quality', splitWP[0])
376 config.setOptionValue (
'isolation', splitWP[1])
377 config.setOptionValue (
'addSelectionToPreselection', addSelectionToPreselection)
378 config.setOptionValue (
'systematicBreakdown', systematicBreakdown)
379 config.setOptionValue (
'noEffSF', noEffSF)
380 config.setOptionValue (
'onlyRecoEffSF', onlyRecoEffSF)
387 super (MuonTriggerAnalysisSFBlock, self).__init__ ()
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 (
'saveEff',
False, type=bool,
395 info=
"define whether we decorate also the trigger scale efficiency "
396 "The default is false.")
397 self.addOption (
'containerName',
'', type=str,
398 info=
"the input muon container, with a possible selection, in "
399 "the format container or container.selection.")
403 if config.dataType()
is not DataType.Data:
410 if config.campaign()
is Campaign.MC20a:
412 elif config.campaign()
is Campaign.MC20d:
414 elif config.campaign()
is Campaign.MC20e:
416 elif config.campaign()
in [Campaign.MC21a, Campaign.MC23a]:
418 elif config.campaign()
in [Campaign.MC23c, Campaign.MC23d]:
422 triggerConfigYears = {}
424 triggerChains = self.triggerChainsPerYear.
get(
int(year), self.triggerChainsPerYear.
get(
str(year), []))
425 for chain
in triggerChains:
426 chain = chain.replace(
" || ",
"_OR_")
427 chain_noHLT = chain.replace(
"HLT_",
"")
428 legs = triggerDict[chain_noHLT]
430 if chain_noHLT.startswith(
'mu')
and chain_noHLT[2].isdigit:
432 triggerConfigs[chain_noHLT] = chain
433 if chain_noHLT
in triggerConfigYears.keys():
434 triggerConfigYears[chain_noHLT].
append(year)
436 triggerConfigYears[chain_noHLT] = [year]
439 if leg.startswith(
'mu')
and leg[2].isdigit:
441 triggerConfigs[leg] =
'HLT_' + leg
442 if chain_noHLT
in triggerConfigYears.keys():
443 triggerConfigYears[leg].
append(year)
445 triggerConfigYears[leg] = [year]
447 for trig_short, trig
in triggerConfigs.items():
448 alg = config.createAlgorithm(
'CP::MuonTriggerEfficiencyScaleFactorAlg',
449 'MuonTrigEfficiencyCorrectionsAlg_' + trig_short)
450 config.addPrivateTool(
'efficiencyScaleFactorTool',
451 'CP::MuonTriggerScaleFactors' )
454 alg.efficiencyScaleFactorTool.MuonQuality = self.muonID
455 alg.efficiencyScaleFactorTool.AllowZeroSF =
True
458 if config.campaign()
is Campaign.MC20a:
459 if triggerConfigYears[trig_short] == [2015]:
460 alg.maxRunNumber = 290000
461 elif triggerConfigYears[trig_short] == [2016]:
462 alg.minRunNumber = 290000
465 alg.scaleFactorDecoration =
'muon_trigEffSF_' + trig_short +
'_%SYS%'
467 alg.mcEfficiencyDecoration =
'muon_trigEff_' + trig_short +
'_%SYS%'
468 alg.outOfValidity = 2
469 alg.outOfValidityDeco =
'bad_eff_muontrig_' + trig_short
470 alg.muons = config.readName (self.containerName)
471 alg.preselection = config.getPreselection (self.containerName,
'')
472 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'trigEffSF_' + trig_short)
474 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'trigEff_' + trig_short)