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