ATLAS Offline Software
Loading...
Searching...
No Matches
TauAnalysisConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3# AnaAlgorithm import(s):
4from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType
5from AnalysisAlgorithmsConfig.ConfigSequence import groupBlocks
6from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
7from AthenaCommon.Logging import logging
8from AthenaConfiguration.Enums import LHCPeriod
9from Campaigns.Utils import Campaign
10
11from TriggerAnalysisAlgorithms.TriggerAnalysisSFConfig import trigger_set
12
13
14class TauCalibrationConfig (ConfigBlock):
15 """the ConfigBlock for the tau four-momentum correction"""
16
17 def __init__ (self) :
18 super (TauCalibrationConfig, self).__init__ ()
19 self.setBlockName('Taus')
20 self.addOption ('inputContainer', '', type=str,
21 info="the name of the input tau-jet container. If left empty, automatically defaults "
22 "to `'AnalysisTauJets'` for PHYSLITE and `'TauJets'` otherwise.")
23 self.addOption ('containerName', '', type=str,
24 noneAction='error',
25 info="the name of the output container after calibration.")
26 self.addOption ('postfix', '', type=str,
27 info="a postfix to apply to decorations and algorithm names. "
28 "Typically not needed here since the calibration is common to "
29 "all tau-jets.")
30 self.addOption ('rerunTruthMatching', True, type=bool,
31 info="whether to rerun truth matching (sets up an instance of "
32 "`CP::TauTruthMatchingAlg`).")
33 self.addOption ('decorateTruth', False, type=bool,
34 info="decorate the truth particle information on the reconstructed one.")
35 self.addOption ('decorateExtraVariables', True, type=bool,
36 info="decorate extra variables for the reconstructed tau-jet.")
37 self.addOption ('addGlobalFELinksDep', False, type=bool,
38 info="whether to add dependencies for the global FE links (needed for PHYSLITE production)",
39 expertMode=True)
40 self.addOption ('useGNTau', False, type=bool,
41 info="use GNTau-based ID instead of RNNTau ID. "
42 "Recommendations: experimental feature and might become default soon.",
43 expertMode=True)
44
45 def instanceName (self) :
46 """Return the instance name for this block"""
47 return self.containerName + self.postfix
48
49 def makeAlgs (self, config) :
50
51 # protection for EleRM taus, which are available only from 2024 onward
52 if 'EleRM' in self.inputContainer:
53 if config.dataType() is DataType.Data and config.dataYear() <= 2023:
54 raise RuntimeError("EleRM taus are only available from 2024 dataset onward")
55 elif config.dataType() is not DataType.Data and config.campaign() <= Campaign.MC23d:
56 raise RuntimeError("EleRM taus are only available from 2024 dataset onward")
57
58 postfix = self.postfix
59 if postfix != '' and postfix[0] != '_' :
60 postfix = '_' + postfix
61
62 inputContainer = "AnalysisTauJets" if config.isPhyslite() else "TauJets"
63 if self.inputContainer:
64 inputContainer = self.inputContainer
65 config.setSourceName (self.containerName, inputContainer)
66
67 # Set up a shallow copy to decorate
68 if config.wantCopy (self.containerName) :
69 alg = config.createAlgorithm( 'CP::AsgShallowCopyAlg', 'TauShallowCopyAlg' )
70 alg.input = config.readName (self.containerName)
71 alg.output = config.copyName (self.containerName)
72 alg.outputType = 'xAOD::TauJetContainer'
73 decorations = []
75 decorations += ['neutralGlobalFELinks', 'chargedGlobalFELinks']
76 if config.dataType() is not DataType.Data:
77 decorations += ['IsTruthMatched', 'truthJetLink', 'truthParticleLink']
78 if decorations:
79 alg.declareDecorations = decorations
80
81 # Set up the tau truth matching algorithm:
82 if self.rerunTruthMatching and config.dataType() is not DataType.Data:
83 alg = config.createAlgorithm( 'CP::TauTruthMatchingAlg',
84 'TauTruthMatchingAlg' )
85 config.addPrivateTool( 'matchingTool',
86 'TauAnalysisTools::TauTruthMatchingTool' )
87 alg.matchingTool.TruthJetContainerName = 'AntiKt4TruthDressedWZJets'
88 alg.taus = config.readName (self.containerName)
89 alg.preselection = config.getPreselection (self.containerName, '')
90
91 # decorate truth tau information on the reconstructed object:
92 if self.decorateTruth and config.dataType() is not DataType.Data:
93 alg = config.createAlgorithm( 'CP::TauTruthDecorationsAlg',
94 'TauTruthDecorationsAlg',
95 reentrant=True )
96 alg.taus = config.readName (self.containerName, nominal=True)
97 alg.doubleDecorations = ['pt_vis', 'pt_invis', 'eta_vis', 'eta_invis', 'phi_vis', 'phi_invis', 'm_vis', 'm_invis']
98 alg.floatDecorations = []
99 alg.intDecorations = ['pdgId']
100 alg.unsignedIntDecorations = ['classifierParticleOrigin', 'classifierParticleType']
101 alg.charDecorations = ['IsHadronicTau']
102 alg.prefix = 'truth_'
103
104 # these are "_ListHelper" objects, and not "list", need to copy to lists to allow concatenate
105 for var in ['DecayMode', 'ParticleType', 'PartonTruthLabelID'] + alg.doubleDecorations[:] + alg.floatDecorations[:] + alg.intDecorations[:] + alg.unsignedIntDecorations[:] + alg.charDecorations[:]:
106 branchName = alg.prefix + var
107 if 'classifierParticle' in var:
108 branchOutput = alg.prefix + var.replace('classifierParticle', '').lower()
109 else:
110 branchOutput = branchName
111 config.addOutputVar (self.containerName, branchName, branchOutput, noSys=True)
112
113 # Decorate extra variables
115 alg = config.createAlgorithm( 'CP::TauExtraVariablesAlg',
116 'TauExtraVariablesAlg',
117 reentrant=True )
118 alg.taus = config.readName (self.containerName, nominal=True)
119
120 # Set up the tau 4-momentum smearing algorithm:
121 alg = config.createAlgorithm( 'CP::TauSmearingAlg', 'TauSmearingAlg' )
122 config.addPrivateTool( 'smearingTool', 'TauAnalysisTools::TauSmearingTool' )
123 alg.smearingTool.useFastSim = config.dataType() is DataType.FastSim
124 alg.smearingTool.Campaign = "mc23" if config.geometry() is LHCPeriod.Run3 else "mc20"
125 if config.geometry() is LHCPeriod.Run2 and self.useGNTau:
126 raise RuntimeError("Tau Smearing recommendations with GNTau are not yet available for Run2")
127 alg.smearingTool.useGNTau = self.useGNTau
128 alg.taus = config.readName (self.containerName)
129 alg.tausOut = config.copyName (self.containerName)
130 config.setExtraOutputs ({('xAOD::IParticleContainer' , 'StoreGateSvc+' + config.readName(self.containerName, nominal=True) + '.RNNEleScoreSigTrans_v1')})
131 alg.preselection = config.getPreselection (self.containerName, '')
132
133 # Additional decorations
134 alg = config.createAlgorithm( 'CP::AsgEnergyDecoratorAlg', 'EnergyDecorator' )
135 alg.particles = config.readName (self.containerName)
136
137 config.addOutputVar (self.containerName, 'pt', 'pt')
138 config.addOutputVar (self.containerName, 'eta', 'eta', noSys=True)
139 config.addOutputVar (self.containerName, 'phi', 'phi', noSys=True)
140 config.addOutputVar (self.containerName, 'e_%SYS%', 'e')
141 config.addOutputVar (self.containerName, 'charge', 'charge', noSys=True)
142 config.addOutputVar (self.containerName, 'NNDecayMode', 'NNDecayMode', noSys=True, auxType='int')
143 config.addOutputVar (self.containerName, 'passTATTauMuonOLR', 'passTATTauMuonOLR', noSys=True, auxType='char')
144 config.addOutputVar (self.containerName, 'TESCompatibility', 'TESCompatibility')
146 config.addOutputVar (self.containerName, 'nTracksCharged', 'nTracksCharged', noSys=True)
147
148
150 """the ConfigBlock for the tau working point selection"""
151
152 def __init__ (self) :
153 super (TauWorkingPointSelectionConfig, self).__init__ ()
154 self.setBlockName('TauWorkingPointSelection')
155 self.addOption ('containerName', '', type=str,
156 noneAction='error',
157 info="the name of the input container.")
158 self.addOption ('selectionName', '', type=str,
159 noneAction='error',
160 info="the name of the tau-jet selection to define (e.g. `tight` or "
161 "`loose`).")
162 self.addOption ('postfix', None, type=str,
163 info="a postfix to apply to decorations and algorithm names. "
164 "Typically not needed here as selectionName is used internally.")
165 self.addOption ('quality', None, type=str,
166 info="the ID WP to use. Supported ID WPs: `Tight`, `Medium`, "
167 "`Loose`, `VeryLoose`, `Baseline`, `BaselineForFakes`.")
168 self.addOption ('use_eVeto', False, type=bool,
169 info="use selection with or without eVeto combined with TauID. "
170 "Recommendations: set it to `True` if electrons mis-reconstructed as tau-jets are a large background for your analysis.")
171 self.addOption ('use_muonOLR', False, type=bool,
172 info="use selection with or without muonOLR with TauID. "
173 "Recommendations: set it to `True` if muons mis-reconstructed as tau-jets are a large background for your analysis.")
174 self.addOption ('useGNTau', False, type=bool,
175 info="use GNTau-based ID instead of RNNTau ID. "
176 "Recommendations: experimental feature and might become default soon.",
177 expertMode=True)
178 self.addOption ('dropPtCut', False, type=bool,
179 info=r"select tau-jets without an explicit minimum $p_\mathrm{T}$ cut. For PHYS/PHYSLITE, this would mean selecting tau-jets starting from 13 GeV. "
180 "Recommendations: experimental feature and not supported for all combinations of ID/eVeto WPs.",
181 expertMode=True)
182 self.addOption ('useLowPt', False, type=bool,
183 info="select taus starting from 15 GeV instead of the default 20 GeV cut "
184 "recommendations: experimental feature and not supported for all combinations of ID/eVeto WPs.",
185 expertMode=True)
186 self.addOption ('useSelectionConfigFile', True, type=bool,
187 info="use pre-defined configuration files for selecting tau-jets. "
188 "Recommendations: set this to `False` only if you want to test/optimise the tau-jet selection for selections not already provided through config files.")
189 self.addOption ('manual_sel_minpt', 20.0, type=float,
190 info=r"minimum $p_\mathrm{T}$ cut (in GeV) used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
191 self.addOption ('manual_sel_absetaregion', [0, 1.37, 1.52, 2.5], type=list,
192 info=r"$\vert\eta\vert$ regions cut used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
193 self.addOption ('manual_sel_abscharges', [1,], type=list,
194 info="charge of the tau-jet cut used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
195 self.addOption ('manual_sel_ntracks', [1,3], type=list,
196 info="number of tau-jet tracks used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
197 self.addOption ('manual_sel_minrnnscore', -1, type=float,
198 info="minimum RNN score cut used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
199 self.addOption ('manual_sel_mingntauscore', -1, type=float,
200 info="minimum GNTau score selection when `useSelectionConfigFile` is set to `False`.")
201 self.addOption ('manual_sel_rnnwp', None, type=str,
202 info="RNN working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
203 self.addOption ('manual_sel_gntauwp', None, type=str,
204 info="GNTau working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
205 self.addOption ('manual_sel_evetowp', None, type=str,
206 info="eveto working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
207 self.addOption ('manual_sel_muonolr', False, type=bool,
208 info="use `muonolr` for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
209 self.addOption ('addSelectionToPreselection', True, type=bool,
210 info="whether to retain only tau-jets satisfying the working point "
211 "requirements.")
212
213 def instanceName (self) :
214 """Return the instance name for this block"""
215 if self.postfix is not None:
216 return self.containerName + '_' + self.selectionName + self.postfix
217 else:
218 return self.containerName + '_' + self.selectionName
219
220 def makeAlgs (self, config) :
221
222 selectionPostfix = self.selectionName
223 if selectionPostfix != '' and selectionPostfix[0] != '_' :
224 selectionPostfix = '_' + selectionPostfix
225
226 postfix = self.postfix
227 if postfix is None :
228 postfix = self.selectionName
229 if postfix != '' and postfix[0] != '_' :
230 postfix = '_' + postfix
231
232 # do tau seletion through external txt config file
234 nameFormat = 'TauAnalysisAlgorithms/tau_selection_'
235 if self.dropPtCut:
236 nameFormat = nameFormat + 'nopt_'
237 if self.useLowPt:
238 nameFormat = nameFormat + 'lowpt_'
239 if self.useGNTau:
240 nameFormat = nameFormat + 'gntau_'
241 nameFormat = nameFormat + '{}_'
242 if self.use_eVeto:
243 nameFormat = nameFormat + 'eleid'
244 else:
245 nameFormat = nameFormat + 'noeleid'
246 if self.use_muonOLR:
247 nameFormat = nameFormat + '_muonolr'
248 nameFormat = nameFormat + '.conf'
249
250 if self.quality not in ['Tight', 'Medium', 'Loose', 'VeryLoose', 'Baseline', 'BaselineForFakes'] :
251 raise ValueError ("invalid tau quality: \"" + self.quality +
252 "\", allowed values are Tight, Medium, Loose, " +
253 "VeryLoose, Baseline, BaselineForFakes")
254
255 # Set up the algorithm selecting taus:
256 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'TauSelectionAlg' )
257 config.addPrivateTool( 'selectionTool', 'TauAnalysisTools::TauSelectionTool' )
258 alg.selectionTool.TauContainerName = config.readName (self.containerName, nominal=True)
260 inputfile = nameFormat.format(self.quality.lower())
261 alg.selectionTool.ConfigPath = inputfile
262 else:
263 #build selection from user handmade selection
264 from ROOT import TauAnalysisTools
265 selectioncuts = TauAnalysisTools.SelectionCuts
266 alg.selectionTool.ConfigPath = ""
267 alg.selectionTool.SelectionCuts = int(selectioncuts.CutPt |
268 selectioncuts.CutAbsEta |
269 selectioncuts.CutAbsCharge |
270 selectioncuts.CutNTrack |
271 selectioncuts.CutJetRNNScoreSigTrans |
272 selectioncuts.CutGNTauScoreSigTrans |
273 selectioncuts.CutJetIDWP |
274 selectioncuts.CutEleIDWP |
275 selectioncuts.CutMuonOLR)
276
277 alg.selectionTool.PtMin = self.manual_sel_minpt
278 alg.selectionTool.AbsEtaRegion = self.manual_sel_absetaregion
279 alg.selectionTool.AbsCharges = self.manual_sel_abscharges
280 alg.selectionTool.NTracks = self.manual_sel_ntracks
281 alg.selectionTool.JetRNNSigTransMin = self.manual_sel_minrnnscore
282 alg.selectionTool.GNTauSigTransMin = self.manual_sel_mingntauscore
283 #cross-check that min rnn score and min gntau score are not both set at the same time
284 if self.manual_sel_minrnnscore != -1 and self.manual_sel_mingntauscore != -1:
285 raise RuntimeError("manual_sel_minrnnscore and manual_sel_mingntauscore have been both set; please choose only one type of ID: RNN or GNTau, not both")
286 # working point following the Enums from https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h
287 if self.manual_sel_rnnwp is None:
288 alg.selectionTool.JetIDWP = 1
289 elif self.manual_sel_rnnwp == "veryloose":
290 alg.selectionTool.JetIDWP = 6
291 elif self.manual_sel_rnnwp == "loose":
292 alg.selectionTool.JetIDWP = 7
293 elif self.manual_sel_rnnwp == "medium":
294 alg.selectionTool.JetIDWP = 8
295 elif self.manual_sel_rnnwp == "tight":
296 alg.selectionTool.JetIDWP = 9
297 else:
298 raise ValueError ("invalid RNN TauID WP: \"" + self.manual_sel_rnnwp + "\". Allowed values are None, veryloose, loose, medium, tight")
299
300 # cross-check that min rnn score and RNN WPs are not set at the same time
301 if self.manual_sel_minrnnscore != -1 and self.manual_sel_rnnwp is not None:
302 raise RuntimeError("manual_sel_minrnnscore and manual_sel_rnnwp have been both set; please set only one of them")
303
304 # working point following the Enums from https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h
305 if self.manual_sel_gntauwp is None:
306 alg.selectionTool.JetIDWP = 1
307 elif self.manual_sel_gntauwp == "veryloose":
308 alg.selectionTool.JetIDWP = 10
309 elif self.manual_sel_gntauwp == "loose":
310 alg.selectionTool.JetIDWP = 11
311 elif self.manual_sel_gntauwp == "medium":
312 alg.selectionTool.JetIDWP = 12
313 elif self.manual_sel_gntauwp == "tight":
314 alg.selectionTool.JetIDWP = 13
315 else:
316 raise ValueError ("invalid GNN Tau ID WP: \"" + self.manual_sel_gntauwp + "\". Allowed values are None, veryloose, loose, medium, tight")
317
318 # cross-check that min gntau score and GNTau WPs are not set at the same time
319 if self.manual_sel_mingntauscore != -1 and self.manual_sel_gntauwp is not None:
320 raise RuntimeError("manual_sel_mingntauscore and manual_sel_gntauwp have been both set; please set only one of them")
321
322 # working point following the Enums from https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h
323 if self.manual_sel_evetowp is None:
324 alg.selectionTool.EleIDWP = 1
325 elif self.manual_sel_evetowp == "loose":
326 alg.selectionTool.EleIDWP = 2
327 elif self.manual_sel_evetowp == "medium":
328 alg.selectionTool.EleIDWP = 3
329 elif self.manual_sel_evetowp == "tight":
330 alg.selectionTool.EleIDWP = 4
331 else:
332 raise ValueError ("invalid eVeto WP: \"" + self.manual_sel_evetowp + "\". Allowed values are None, loose, medium, tight")
333
334 # set MuonOLR option:
335 alg.selectionTool.MuonOLR = self.manual_sel_muonolr
336
337 alg.selectionDecoration = 'selected_tau' + selectionPostfix + ',as_char'
338 alg.particles = config.readName (self.containerName)
339 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
340 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
341 preselection=self.addSelectionToPreselection)
342
343
345 """the ConfigBlock for the tau working point efficiency computation"""
346
347 def __init__ (self) :
348 super (TauWorkingPointEfficiencyConfig, self).__init__ ()
349 self.setBlockName('TauWorkingPointEfficiency')
350 self.addDependency('TauWorkingPointSelection', required=True)
351 self.addDependency('EventSelection', required=False)
352 self.addDependency('EventSelectionMerger', required=False)
353 self.addOption ('containerName', '', type=str,
354 noneAction='error',
355 info="the name of the input container.")
356 self.addOption ('selectionName', '', type=str,
357 noneAction='error',
358 info="the name of the tau-jet selection to define (e.g. `tight` or "
359 "`loose`).")
360 self.addOption ('postfix', None, type=str,
361 info="a postfix to apply to decorations and algorithm names. "
362 "Typically not needed here as selectionName is used internally.")
363 self.addOption ('quality', None, type=str,
364 info="the ID WP to use. Supported ID WPs: `Tight`, `Medium`, "
365 "`Loose`, `VeryLoose`, `Baseline`, `BaselineForFakes`.")
366 self.addOption ('use_eVeto', False, type=bool,
367 info="use selection with or without eVeto combined with TauID. "
368 "Recommendations: set it to `True` if electrons mis-reconstructed as tau-jets are a large background for your analysis.")
369 self.addOption ('useGNTau', False, type=bool,
370 info="use GNTau-based ID instead of RNNTau ID. "
371 "Recommendations: experimental feature and might become default soon.",
372 expertMode=True)
373 self.addOption ('manual_sel_rnnwp', None, type=str,
374 info="RNN working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
375 self.addOption ('manual_sel_evetowp', None, type=str,
376 info="eveto working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
377 self.addOption ('noEffSF', False, type=bool,
378 info="disables the calculation of efficiencies and scale factors. "
379 "Experimental! only useful to test a new WP for which scale "
380 "factors are not available.",
381 expertMode=True)
382 self.addOption ('saveDetailedSF', True, type=bool,
383 info="save all the independent detailed object scale factors.")
384 self.addOption ('saveCombinedSF', False, type=bool,
385 info="save the combined object scale factor.")
386
387 def instanceName (self) :
388 """Return the instance name for this block"""
389 if self.postfix is not None:
390 return self.containerName + '_' + self.selectionName + self.postfix
391 else:
392 return self.containerName + '_' + self.selectionName
393
394 def makeAlgs (self, config) :
395
396 selectionPostfix = self.selectionName
397 if selectionPostfix != '' and selectionPostfix[0] != '_' :
398 selectionPostfix = '_' + selectionPostfix
399
400 postfix = self.postfix
401 if postfix is None :
402 postfix = self.selectionName
403 if postfix != '' and postfix[0] != '_' :
404 postfix = '_' + postfix
405
406 if self.quality is not None and self.quality not in ['Tight', 'Medium', 'Loose', 'VeryLoose', 'Baseline', 'BaselineForFakes'] :
407 raise ValueError ("invalid tau quality: \"" + self.quality +
408 "\", allowed values are Tight, Medium, Loose, " +
409 "VeryLoose, Baseline, BaselineForFakes")
410
411 sfList = []
412 # Set up the algorithm calculating the efficiency scale factors for the
413 # taus:
414 if config.dataType() is not DataType.Data and not self.noEffSF:
415 log = logging.getLogger('TauJetSFConfig')
416 # need multiple instances of the TauEfficiencyCorrectionTool
417 # 1) Reco 2) TauID, 3) eVeto for fake tau 4) eVeto for true tau
418 # 3) and 4) are optional if eVeto is used in TauSelectionTool
419
420 # TauEfficiencyCorrectionTool for Reco, this should be always enabled
421 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
422 'TauEfficiencyCorrectionsAlgReco' )
423 config.addPrivateTool( 'efficiencyCorrectionsTool',
424 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
425 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [0]
426 alg.efficiencyCorrectionsTool.Campaign = "mc23" if config.geometry() is LHCPeriod.Run3 else "mc20"
427 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
428 alg.scaleFactorDecoration = 'tau_Reco_effSF' + selectionPostfix + '_%SYS%'
429 alg.outOfValidity = 2 #silent
430 alg.outOfValidityDeco = 'bad_Reco_eff' + selectionPostfix
431 alg.taus = config.readName (self.containerName)
432 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
434 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
435 'Reco_effSF' + postfix)
436 sfList += [alg.scaleFactorDecoration]
437
438
439 campaign = "mc23" if config.geometry() is LHCPeriod.Run3 else "mc20"
440
441 # TauEfficiencyCorrectionTool for Identification, use only in case TauID is requested in TauSelectionTool
442 if self.quality not in ('VeryLoose','Baseline','BaselineForFakes'):
443 # current recommendations are for RNN ID Run2/Run3 or GNTAU for Run3,
444 if (not self.useGNTau or (self.useGNTau and campaign == "mc23")):
445
446 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
447 'TauEfficiencyCorrectionsAlgID' )
448 config.addPrivateTool( 'efficiencyCorrectionsTool',
449 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
450 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [4]
451
452 jetIDLevels = (
453 {"Loose": 11, "Medium": 12, "Tight": 13}
454 if self.useGNTau
455 else {"Loose": 7, "Medium": 8, "Tight": 9}
456 )
457 wp = self.quality
458 if not self.useGNTau and self.manual_sel_rnnwp is not None:
459 wp = self.manual_sel_rnnwp.capitalize()
460 if wp not in jetIDLevels:
461 raise ValueError(
462 'Invalid tauID: "'
463 + str(wp)
464 + '". Allowed values are Loose, Medium, Tight'
465 )
466
467 alg.efficiencyCorrectionsTool.JetIDLevel = jetIDLevels[wp]
468 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
469 alg.efficiencyCorrectionsTool.Campaign = campaign
470 alg.efficiencyCorrectionsTool.useGNTau = self.useGNTau
471 alg.scaleFactorDecoration = 'tau_ID_effSF' + selectionPostfix + '_%SYS%'
472 alg.outOfValidity = 2 #silent
473 alg.outOfValidityDeco = 'bad_ID_eff' + selectionPostfix
474 alg.taus = config.readName (self.containerName)
475 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
476 if self.saveDetailedSF:
477 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
478 'ID_effSF' + postfix)
479 sfList += [alg.scaleFactorDecoration]
480
481 # TauEfficiencyCorrectionTool for eVeto both on true tau and fake tau, use only in case eVeto is requested in TauSelectionTool
482 if self.use_eVeto:
483 # eVeto correction for fake tau are for RNN ID Run2/Run3, or for GNTau for Run3
484 if (not self.useGNTau or (self.useGNTau and campaign == "mc23")):
485 # correction for fake tau
486 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
487 'TauEfficiencyCorrectionsAlgEvetoFakeTau' )
488 config.addPrivateTool( 'efficiencyCorrectionsTool',
489 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
490 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [10]
491 # since all TauSelectionTool config files have loose eRNN, code only this option for now
492 alg.efficiencyCorrectionsTool.EleIDLevel = 2
493 #overwrite decision in case user selects a WP manually
494 if self.manual_sel_evetowp == "loose":
495 alg.efficiencyCorrectionsTool.EleIDLevel = 2
496 elif self.manual_sel_evetowp == "medium":
497 alg.efficiencyCorrectionsTool.EleIDLevel = 3
498
499 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
500 alg.efficiencyCorrectionsTool.Campaign = campaign
501 alg.efficiencyCorrectionsTool.useGNTau = self.useGNTau
502 alg.scaleFactorDecoration = 'tau_EvetoFakeTau_effSF' + selectionPostfix + '_%SYS%'
503
504 # for 2025-prerec, eVeto recommendations are given separately for Loose and Medium RNN
505 jetIDLevels = (
506 {"Loose": 11, "Medium": 12, "Tight": 13}
507 if self.useGNTau
508 else {"Loose": 7, "Medium": 8, "Tight": 9}
509 )
510 wp = self.quality
511 if not self.useGNTau and self.manual_sel_rnnwp is not None:
512 wp = self.manual_sel_rnnwp.capitalize()
513 if wp not in jetIDLevels:
514 raise ValueError(
515 'Invalid tauID: "'
516 + str(wp)
517 + '". Allowed values are Loose, Medium, Tight'
518 )
519 if wp == "Tight":
520 log.warning(
521 "eVeto SFs are not available for Tight WP -> fallback to Medium WP"
522 )
523 wp = "Medium"
524
525 alg.efficiencyCorrectionsTool.JetIDLevel = jetIDLevels[wp]
526 alg.outOfValidity = 2 #silent
527 alg.outOfValidityDeco = 'bad_EvetoFakeTau_eff' + selectionPostfix
528 alg.taus = config.readName (self.containerName)
529 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
530 if self.saveDetailedSF:
531 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
532 'EvetoFakeTau_effSF' + postfix)
533 sfList += [alg.scaleFactorDecoration]
534
535 # correction for true tau
536 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
537 'TauEfficiencyCorrectionsAlgEvetoTrueTau' )
538 config.addPrivateTool( 'efficiencyCorrectionsTool',
539 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
540 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [8]
541 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
542 alg.efficiencyCorrectionsTool.Campaign = "mc23" if config.geometry() is LHCPeriod.Run3 else "mc20"
543 alg.scaleFactorDecoration = 'tau_EvetoTrueTau_effSF' + selectionPostfix + '_%SYS%'
544 # since all TauSelectionTool config files have loose eRNN, code only this option for now
545 alg.efficiencyCorrectionsTool.EleIDLevel = 2
546 #overwrite decision in case user selects a WP manually
547 if self.manual_sel_evetowp == "loose":
548 alg.efficiencyCorrectionsTool.EleIDLevel = 2
549 elif self.manual_sel_evetowp == "medium":
550 alg.efficiencyCorrectionsTool.EleIDLevel = 3
551 alg.outOfValidity = 2 #silent
552 alg.outOfValidityDeco = 'bad_EvetoTrueTau_eff' + selectionPostfix
553 alg.taus = config.readName (self.containerName)
554 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
555 if self.saveDetailedSF:
556 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
557 'EvetoTrueTau_effSF' + postfix)
558 sfList += [alg.scaleFactorDecoration]
559
561 alg = config.createAlgorithm( 'CP::AsgObjectScaleFactorAlg',
562 'TauCombinedEfficiencyScaleFactorAlg' )
563 alg.particles = config.readName (self.containerName)
564 alg.inScaleFactors = sfList
565 alg.outScaleFactor = 'effSF' + postfix + '_%SYS%'
566 config.addOutputVar (self.containerName, alg.outScaleFactor,
567 'effSF' + postfix)
568
569
571 def __init__ (self) :
572 super (EXPERIMENTAL_TauCombineMuonRemovalConfig, self).__init__ ()
573 self.addOption (
574 'inputTaus', 'TauJets', type=str,
575 noneAction='error',
576 info="the name of the input tau container."
577 )
578 self.addOption (
579 'inputTausMuRM', 'TauJets_MuonRM', type=str,
580 noneAction='error',
581 info="the name of the input tau container with muon removal applied."
582 )
583 self.addOption (
584 'outputTaus', 'TauJets_MuonRmCombined', type=str,
585 noneAction='error',
586 info="the name of the output tau container."
587 )
588
589 def instanceName (self) :
590 """Return the instance name for this block"""
591 return self.outputTaus
592
593 def makeAlgs (self, config) :
594
595 if config.isPhyslite() :
596 raise(RuntimeError("Muon removal taus is not available in Physlite mode"))
597
598 alg = config.createAlgorithm( 'CP::TauCombineMuonRMTausAlg', 'TauCombineMuonRMTausAlg' )
599 alg.taus = self.inputTaus
600 alg.muonrm_taus = self.inputTausMuRM
601 alg.combined_taus = self.outputTaus
602
603class TauTriggerAnalysisSFBlock (ConfigBlock):
604
605 def __init__ (self) :
606 super (TauTriggerAnalysisSFBlock, self).__init__ ()
607 self.addDependency('EventSelection', required=False)
608 self.addDependency('EventSelectionMerger', required=False)
609 self.addOption ('triggerChainsPerYear', {}, type=dict,
610 info="a dictionary with key (string) the year and value (list of "
611 "strings) the trigger chains.")
612 self.addOption ('tauID', '', type=str,
613 info="the tau-jet quality WP to use.")
614 self.addOption ('prefixSF', 'trigEffSF', type=str,
615 info="the decoration prefix for trigger scale factors.")
616 self.addOption ('includeAllYearsPerRun', False, type=bool,
617 info="all configured years in the LHC run will "
618 "be included in all jobs.")
619 self.addOption ('removeHLTPrefix', True, type=bool,
620 info="remove the HLT prefix from trigger chain names.")
621 self.addOption ('containerName', '', type=str,
622 info="the input tau-jet container, with a possible selection, in "
623 "the format `container` or `container.selection`.")
624
625 def instanceName (self) :
626 """Return the instance name for this block"""
627 return self.containerName + '_' + self.prefixSF + '_' + self.tauID
628
629 def makeAlgs (self, config) :
630
631 if config.dataType() is not DataType.Data:
632 log = logging.getLogger('TauTriggerAnalysisSF')
633
634 # Temporary skip for MC23e until SFs are available
635 if config.campaign() is Campaign.MC23e:
636 log.warning("Tau trigger scale factors are not available yet for MC23e")
637 return
638 # Temporary skip for MC23g until SFs are available
639 if config.campaign() is Campaign.MC23g:
640 log.warning("Tau trigger scale factors are not available yet for MC23g")
641 return
642
643 triggers = trigger_set(config, self.triggerChainsPerYear,
644 self.includeAllYearsPerRun)
645 for chain in triggers:
646 chain_noHLT = chain.replace("HLT_", "")
647 chain_out = chain_noHLT if self.removeHLTPrefix else chain
648 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
649 'TauTrigEfficiencyCorrectionsAlg_' + chain )
650 config.addPrivateTool( 'efficiencyCorrectionsTool',
651 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
652 # SFTriggerHadTau correction type from
653 # https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h#L79
654 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [12]
655 if config.geometry() is LHCPeriod.Run2:
656 alg.efficiencyCorrectionsTool.Campaign = "mc20"
657 else:
658 alg.efficiencyCorrectionsTool.Campaign = config.campaign().value
659 alg.efficiencyCorrectionsTool.TriggerName = chain
660
661 # JetIDLevel from
662 # https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h#L79
663 if self.tauID=="Loose":
664 JetIDLevel = 7
665 elif self.tauID=="Medium":
666 JetIDLevel = 8
667 elif self.tauID=="Tight":
668 JetIDLevel = 9
669 else:
670 raise ValueError ("invalid tauID: \"" + self.tauID + "\". Allowed values are loose, medium, tight")
671 alg.efficiencyCorrectionsTool.JetIDLevel = JetIDLevel
672 alg.efficiencyCorrectionsTool.TriggerSFMeasurement = "combined"
673 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
674
675 alg.scaleFactorDecoration = f"tau_{self.prefixSF}_{chain_out}_%SYS%"
676 alg.outOfValidity = 2 #silent
677 alg.outOfValidityDeco = f"bad_eff_tautrig_{chain_out}"
678 alg.taus = config.readName (self.containerName)
679 alg.preselection = config.getPreselection (self.containerName, self.tauID)
680 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, f"{self.prefixSF}_{chain_out}")
681
682@groupBlocks