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' , f'StoreGateSvc+{inputContainer}.EleRNNLoose_v1' ),
89 ( 'SG::AuxVectorBase' , f'StoreGateSvc+{inputContainer}.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 alg.smearingTool.useGNTau = self.useGNTau
148 alg.taus = config.readName (self.containerName)
149 alg.tausOut = config.copyName (self.containerName)
150 config.setExtraOutputs ({('xAOD::IParticleContainer' , 'StoreGateSvc+' + config.readName(self.containerName, nominal=True) + '.RNNEleScoreSigTrans_v1')})
151 alg.preselection = config.getPreselection (self.containerName, '')
152
153 # Additional decorations
154 alg = config.createAlgorithm( 'CP::AsgEnergyDecoratorAlg', 'EnergyDecorator' )
155 alg.particles = config.readName (self.containerName)
156
157 config.addOutputVar (self.containerName, 'pt', 'pt', auxType='float')
158 config.addOutputVar (self.containerName, 'eta', 'eta', noSys=True, auxType='float')
159 config.addOutputVar (self.containerName, 'phi', 'phi', noSys=True, auxType='float')
160 config.addOutputVar (self.containerName, 'e_%SYS%', 'e', auxType='float')
161 config.addOutputVar (self.containerName, 'charge', 'charge', noSys=True, auxType='float')
162 config.addOutputVar (self.containerName, 'NNDecayMode', 'NNDecayMode', noSys=True, auxType='int')
163 config.addOutputVar (self.containerName, 'passTATTauMuonOLR', 'passTATTauMuonOLR', noSys=True, auxType='char')
164 config.addOutputVar (self.containerName, 'TESCompatibility', 'TESCompatibility', auxType='char')
166 config.addOutputVar (self.containerName, 'nTracksCharged', 'nTracksCharged', noSys=True, auxType='int')
167
168
170 """the ConfigBlock for the tau working point selection"""
171
172 def __init__ (self) :
173 super().__init__()
174 self.setBlockName('TauWorkingPointSelection')
175 self.addOption ('containerName', '', type=str,
176 noneAction='error',
177 info="the name of the input container.",
178 meta={'role':'containerRef'})
179 self.addOption ('selectionName', '', type=str,
180 noneAction='error',
181 info="the name of the tau-jet selection to define (e.g. `tight` or "
182 "`loose`).",
183 meta={'role':'selection'})
184 self.addOption ('postfix', None, type=str,
185 info="a postfix to apply to decorations and algorithm names. "
186 "Typically not needed here as selectionName is used internally.")
187 self.addOption ('quality', None, type=str,
188 info="the ID WP to use. Supported ID WPs: `Tight`, `Medium`, "
189 "`Loose`, `VeryLoose`, `Baseline`, `BaselineForFakes`.",
190 meta={'choices':(['Tight','Medium','Loose','VeryLoose','Baseline','BaselineForFakes'],1)})
191 self.addOption ('use_eVeto', False, type=bool,
192 info="use selection with or without eVeto combined with TauID. "
193 "Recommendations: set it to `True` if electrons mis-reconstructed as tau-jets are a large background for your analysis.")
194 self.addOption ('use_muonOLR', False, type=bool,
195 info="use selection with or without muonOLR with TauID. "
196 "Recommendations: set it to `True` if muons mis-reconstructed as tau-jets are a large background for your analysis.")
197 self.addOption ('useGNTau', False, type=bool,
198 info="use GNTau-based ID instead of RNNTau ID. "
199 "Recommendations: experimental feature and might become default soon.",
200 expertMode=True)
201 self.addOption ('dropPtCut', False, type=bool,
202 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. "
203 "Recommendations: experimental feature and not supported for all combinations of ID/eVeto WPs.",
204 expertMode=True)
205 self.addOption ('useLowPt', False, type=bool,
206 info="select taus starting from 15 GeV instead of the default 20 GeV cut "
207 "recommendations: experimental feature and not supported for all combinations of ID/eVeto WPs.",
208 expertMode=True)
209 self.addOption ('useSelectionConfigFile', True, type=bool,
210 info="use pre-defined configuration files for selecting tau-jets. "
211 "Recommendations: set this to `False` only if you want to test/optimise the tau-jet selection for selections not already provided through config files.")
212 self.addOption ('manual_sel_minpt', 20.0, type=float,
213 info=r"minimum $p_\mathrm{T}$ cut (in GeV) used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
214 self.addOption ('manual_sel_absetaregion', [0, 1.37, 1.52, 2.5], type=list,
215 info=r"$\vert\eta\vert$ regions cut used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
216 self.addOption ('manual_sel_abscharges', [1,], type=list,
217 info="charge of the tau-jet cut used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
218 self.addOption ('manual_sel_ntracks', [1,3], type=list,
219 info="number of tau-jet tracks used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
220 self.addOption ('manual_sel_minrnnscore', -1, type=float,
221 info="minimum RNN score cut used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
222 self.addOption ('manual_sel_mingntauscore', -1, type=float,
223 info="minimum GNTau score selection when `useSelectionConfigFile` is set to `False`.")
224 self.addOption ('manual_sel_rnnwp', None, type=str,
225 info="RNN working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
226 self.addOption ('manual_sel_gntauwp', None, type=str,
227 info="GNTau working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
228 self.addOption ('manual_sel_evetowp', None, type=str,
229 info="eveto working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
230 self.addOption ('manual_sel_muonolr', False, type=bool,
231 info="use `muonolr` for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
232 self.addOption ('addSelectionToPreselection', True, type=bool,
233 info="whether to retain only tau-jets satisfying the working point "
234 "requirements.")
235
236 def instanceName (self) :
237 """Return the instance name for this block"""
238 if self.postfix is not None:
239 return self.containerName + '_' + self.selectionName + self.postfix
240 else:
241 return self.containerName + '_' + self.selectionName
242
243 def makeAlgs (self, config) :
244
245 selectionPostfix = self.selectionName
246 if selectionPostfix != '' and selectionPostfix[0] != '_' :
247 selectionPostfix = '_' + selectionPostfix
248
249 postfix = self.postfix
250 if postfix is None :
251 postfix = self.selectionName
252 if postfix != '' and postfix[0] != '_' :
253 postfix = '_' + postfix
254
255 # do tau seletion through external txt config file
257 if self.dropPtCut and self.useLowPt:
258 raise ValueError ("dropPtCut and useLowPt cannot both be set")
259 if self.dropPtCut and not self.useGNTau:
260 raise ValueError ("dropPtCut is only supported together with useGNTau")
261 nameFormat = 'TauAnalysisAlgorithms/tau_selection_'
262 if self.dropPtCut:
263 nameFormat = nameFormat + 'nopt_'
264 if self.useLowPt:
265 nameFormat = nameFormat + 'lowpt_'
266 if self.useGNTau:
267 nameFormat = nameFormat + 'gntau_'
268 nameFormat = nameFormat + '{}_'
269 if self.use_eVeto:
270 nameFormat = nameFormat + 'eleid'
271 else:
272 nameFormat = nameFormat + 'noeleid'
273 if self.use_muonOLR:
274 nameFormat = nameFormat + '_muonolr'
275 nameFormat = nameFormat + '.conf'
276
277 if self.quality not in ['Tight', 'Medium', 'Loose', 'VeryLoose', 'Baseline', 'BaselineForFakes'] :
278 raise ValueError (f"invalid tau quality: \"{self.quality}\", allowed values are Tight, Medium, Loose, "
279 "VeryLoose, Baseline, BaselineForFakes")
280
281 # Set up the algorithm selecting taus:
282 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'TauSelectionAlg' )
283 config.addPrivateTool( 'selectionTool', 'TauAnalysisTools::TauSelectionTool' )
284 alg.selectionTool.TauContainerName = config.readName (self.containerName, nominal=True)
286 inputfile = nameFormat.format(self.quality.lower())
287 alg.selectionTool.ConfigPath = inputfile
288 else:
289 #build selection from user handmade selection
290 from ROOT import TauAnalysisTools
291 selectioncuts = TauAnalysisTools.SelectionCuts
292 alg.selectionTool.ConfigPath = ""
293 alg.selectionTool.SelectionCuts = int(selectioncuts.CutPt |
294 selectioncuts.CutAbsEta |
295 selectioncuts.CutAbsCharge |
296 selectioncuts.CutNTrack |
297 selectioncuts.CutJetRNNScoreSigTrans |
298 selectioncuts.CutGNTauScoreSigTrans |
299 selectioncuts.CutJetIDWP |
300 selectioncuts.CutEleIDWP |
301 selectioncuts.CutMuonOLR)
302
303 alg.selectionTool.PtMin = self.manual_sel_minpt
304 alg.selectionTool.AbsEtaRegion = self.manual_sel_absetaregion
305 alg.selectionTool.AbsCharges = self.manual_sel_abscharges
306 alg.selectionTool.NTracks = self.manual_sel_ntracks
307 alg.selectionTool.JetRNNSigTransMin = self.manual_sel_minrnnscore
308 alg.selectionTool.GNTauSigTransMin = self.manual_sel_mingntauscore
309 #cross-check that min rnn score and min gntau score are not both set at the same time
310 if self.manual_sel_minrnnscore != -1 and self.manual_sel_mingntauscore != -1:
311 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")
312 # cross-check that RNN and GNTau WPs are not set at the same time
313 if self.manual_sel_rnnwp is not None and self.manual_sel_gntauwp is not None:
314 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")
315 # working point following the Enums from https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h
316 alg.selectionTool.JetIDWP = 1
317 if self.manual_sel_rnnwp is not None:
318 if self.manual_sel_rnnwp not in _MANUAL_RNN_JETIDWP:
319 raise ValueError (f"invalid RNN TauID WP: \"{self.manual_sel_rnnwp}\". Allowed values are None, veryloose, loose, medium, tight")
320 alg.selectionTool.JetIDWP = _MANUAL_RNN_JETIDWP[self.manual_sel_rnnwp]
321
322 # cross-check that min rnn score and RNN WPs are not set at the same time
323 if self.manual_sel_minrnnscore != -1 and self.manual_sel_rnnwp is not None:
324 raise RuntimeError("manual_sel_minrnnscore and manual_sel_rnnwp have been both set; please set only one of them")
325
326 if self.manual_sel_gntauwp is not None:
327 if self.manual_sel_gntauwp not in _MANUAL_GNTAU_JETIDWP:
328 raise ValueError (f"invalid GNN Tau ID WP: \"{self.manual_sel_gntauwp}\". Allowed values are None, veryloose, loose, medium, tight")
329 alg.selectionTool.JetIDWP = _MANUAL_GNTAU_JETIDWP[self.manual_sel_gntauwp]
330
331 # cross-check that min gntau score and GNTau WPs are not set at the same time
332 if self.manual_sel_mingntauscore != -1 and self.manual_sel_gntauwp is not None:
333 raise RuntimeError("manual_sel_mingntauscore and manual_sel_gntauwp have been both set; please set only one of them")
334
335 # working point following the Enums from https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h
336 if self.manual_sel_evetowp is None:
337 alg.selectionTool.EleIDWP = 1
338 elif self.manual_sel_evetowp in _MANUAL_ELEIDWP:
339 alg.selectionTool.EleIDWP = _MANUAL_ELEIDWP[self.manual_sel_evetowp]
340 else:
341 raise ValueError (f"invalid eVeto WP: \"{self.manual_sel_evetowp}\". Allowed values are None, loose, medium, tight")
342
343 # set MuonOLR option:
344 alg.selectionTool.MuonOLR = self.manual_sel_muonolr
345
346 alg.selectionDecoration = 'selected_tau' + selectionPostfix + ',as_char'
347 alg.particles = config.readName (self.containerName)
348 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
349 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
350 preselection=self.addSelectionToPreselection)
351
352
354 """the ConfigBlock for the tau working point efficiency computation"""
355
356 def __init__ (self) :
357 super().__init__()
358 self.setBlockName('TauWorkingPointEfficiency')
359 self.addDependency('TauWorkingPointSelection', required=True)
360 self.addDependency('EventSelection', required=False)
361 self.addDependency('EventSelectionMerger', required=False)
362 self.addOption ('containerName', '', type=str,
363 noneAction='error',
364 info="the name of the input container.",
365 meta={'role':'containerRef'})
366 self.addOption ('selectionName', '', type=str,
367 noneAction='error',
368 info="the name of the tau-jet selection to define (e.g. `tight` or "
369 "`loose`).",
370 meta={'role':'selection'})
371 self.addOption ('postfix', None, type=str,
372 info="a postfix to apply to decorations and algorithm names. "
373 "Typically not needed here as selectionName is used internally.")
374 self.addOption ('quality', None, type=str,
375 info="the ID WP to use. Supported ID WPs: `Tight`, `Medium`, "
376 "`Loose`, `VeryLoose`, `Baseline`, `BaselineForFakes`.",
377 meta={'choices':(['Tight','Medium','Loose','VeryLoose','Baseline','BaselineForFakes'],1)})
378 self.addOption ('use_eVeto', False, type=bool,
379 info="use selection with or without eVeto combined with TauID. "
380 "Recommendations: set it to `True` if electrons mis-reconstructed as tau-jets are a large background for your analysis.")
381 self.addOption ('useGNTau', False, type=bool,
382 info="use GNTau-based ID instead of RNNTau ID. "
383 "Recommendations: experimental feature and might become default soon.",
384 expertMode=True)
385 self.addOption ('manual_sel_rnnwp', None, type=str,
386 info="RNN working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
387 self.addOption ('manual_sel_evetowp', None, type=str,
388 info="eveto working point used for tau-jet selection when `useSelectionConfigFile` is set to `False`.")
389 self.addOption ('noEffSF', False, type=bool,
390 info="disables the calculation of efficiencies and scale factors. "
391 "Experimental! only useful to test a new WP for which scale "
392 "factors are not available.",
393 expertMode=True)
394 self.addOption ('saveDetailedSF', True, type=bool,
395 info="save all the independent detailed object scale factors.")
396 self.addOption ('saveCombinedSF', False, type=bool,
397 info="save the combined object scale factor.")
398
399 def instanceName (self) :
400 """Return the instance name for this block"""
401 if self.postfix is not None:
402 return self.containerName + '_' + self.selectionName + self.postfix
403 else:
404 return self.containerName + '_' + self.selectionName
405
406 def _eVetoEleIDLevel (self) :
407 """the EleIDLevel for the eVeto scale factors"""
408 # since all TauSelectionTool config files have loose eRNN, code only this option for now
409 #overwrite decision in case user selects a WP different than "loose" manually
410 if self.manual_sel_evetowp == "medium":
411 return 3
412 return 2
413
414 def makeAlgs (self, config) :
415
416 selectionPostfix = self.selectionName
417 if selectionPostfix != '' and selectionPostfix[0] != '_' :
418 selectionPostfix = '_' + selectionPostfix
419
420 postfix = self.postfix
421 if postfix is None :
422 postfix = self.selectionName
423 if postfix != '' and postfix[0] != '_' :
424 postfix = '_' + postfix
425
426 if self.quality is not None and self.quality not in ['Tight', 'Medium', 'Loose', 'VeryLoose', 'Baseline', 'BaselineForFakes'] :
427 raise ValueError (f"invalid tau quality: \"{self.quality}\", allowed values are Tight, Medium, Loose, "
428 "VeryLoose, Baseline, BaselineForFakes")
429
430 sfList = []
431 # Set up the algorithm calculating the efficiency scale factors for the
432 # taus:
433 if config.dataType() is not DataType.Data and not self.noEffSF:
434 log = logging.getLogger('TauJetSFConfig')
435 # need multiple instances of the TauEfficiencyCorrectionTool
436 # 1) Reco 2) TauID, 3) eVeto for fake tau 4) eVeto for true tau
437 # 3) and 4) are optional if eVeto is used in TauSelectionTool
438
439 # TauEfficiencyCorrectionTool for Reco, this should be always enabled
440 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
441 'TauEfficiencyCorrectionsAlgReco' )
442 config.addPrivateTool( 'efficiencyCorrectionsTool',
443 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
444 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [0]
445 alg.efficiencyCorrectionsTool.Campaign = _campaign(config)
446 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
447 alg.scaleFactorDecoration = 'tau_Reco_effSF' + selectionPostfix + '_%SYS%'
448 alg.outOfValidity = 2 #silent
449 alg.outOfValidityDeco = 'bad_Reco_eff' + selectionPostfix
450 alg.taus = config.readName (self.containerName)
451 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
453 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
454 'Reco_effSF' + postfix, auxType='float')
455 sfList += [alg.scaleFactorDecoration]
456
457
458 campaign = _campaign(config)
459
460 # TauEfficiencyCorrectionTool for Identification, use only in case TauID is requested in TauSelectionTool
461 if self.quality not in ('VeryLoose','Baseline','BaselineForFakes'):
462 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
463 'TauEfficiencyCorrectionsAlgID' )
464 config.addPrivateTool( 'efficiencyCorrectionsTool',
465 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
466 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [4]
467
468 jetIDLevels = _GNTAU_JETID_LEVELS if self.useGNTau else _RNN_JETID_LEVELS
469 wp = self.quality
470 if not self.useGNTau and self.manual_sel_rnnwp is not None:
471 wp = self.manual_sel_rnnwp.capitalize()
472 if wp not in jetIDLevels:
473 raise ValueError(f'Invalid tauID: "{wp}". Allowed values are Loose, Medium, Tight')
474
475 alg.efficiencyCorrectionsTool.JetIDLevel = jetIDLevels[wp]
476 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
477 alg.efficiencyCorrectionsTool.Campaign = campaign
478 alg.efficiencyCorrectionsTool.useGNTau = self.useGNTau
479 alg.scaleFactorDecoration = 'tau_ID_effSF' + selectionPostfix + '_%SYS%'
480 alg.outOfValidity = 2 #silent
481 alg.outOfValidityDeco = 'bad_ID_eff' + selectionPostfix
482 alg.taus = config.readName (self.containerName)
483 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
484 if self.saveDetailedSF:
485 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
486 'ID_effSF' + postfix, auxType='float')
487 sfList += [alg.scaleFactorDecoration]
488
489 # TauEfficiencyCorrectionTool for eVeto both on true tau and fake tau, use only in case eVeto is requested in TauSelectionTool
490 if self.use_eVeto:
491 #use only in case TauID is requested in TauSelectionTool
492 if self.quality not in ('VeryLoose','Baseline','BaselineForFakes'):
493 # correction for fake tau
494 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
495 'TauEfficiencyCorrectionsAlgEvetoFakeTau' )
496 config.addPrivateTool( 'efficiencyCorrectionsTool',
497 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
498 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [10]
499 alg.efficiencyCorrectionsTool.EleIDLevel = self._eVetoEleIDLevel()
500
501 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
502 alg.efficiencyCorrectionsTool.Campaign = campaign
503 alg.efficiencyCorrectionsTool.useGNTau = self.useGNTau
504 alg.scaleFactorDecoration = 'tau_EvetoFakeTau_effSF' + selectionPostfix + '_%SYS%'
505
506 # for 2025-prerec, eVeto recommendations are given separately for Loose and Medium RNN
507 jetIDLevels = _GNTAU_JETID_LEVELS if self.useGNTau else _RNN_JETID_LEVELS
508 wp = self.quality
509 if not self.useGNTau and self.manual_sel_rnnwp is not None:
510 wp = self.manual_sel_rnnwp.capitalize()
511 if wp not in jetIDLevels:
512 raise ValueError(f'Invalid tauID: "{wp}". Allowed values are Loose, Medium, Tight')
513 if wp == "Tight":
514 log.warning(
515 "eVeto SFs are not available for Tight WP -> fallback to Medium WP"
516 )
517 wp = "Medium"
518
519 alg.efficiencyCorrectionsTool.JetIDLevel = jetIDLevels[wp]
520 alg.outOfValidity = 2 #silent
521 alg.outOfValidityDeco = 'bad_EvetoFakeTau_eff' + selectionPostfix
522 alg.taus = config.readName (self.containerName)
523 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
524 if self.saveDetailedSF:
525 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
526 'EvetoFakeTau_effSF' + postfix, auxType='float')
527 sfList += [alg.scaleFactorDecoration]
528
529 # correction for true tau
530 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
531 'TauEfficiencyCorrectionsAlgEvetoTrueTau' )
532 config.addPrivateTool( 'efficiencyCorrectionsTool',
533 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
534 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [8]
535 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
536 alg.efficiencyCorrectionsTool.Campaign = _campaign(config)
537 alg.scaleFactorDecoration = 'tau_EvetoTrueTau_effSF' + selectionPostfix + '_%SYS%'
538 alg.efficiencyCorrectionsTool.EleIDLevel = self._eVetoEleIDLevel()
539 alg.outOfValidity = 2 #silent
540 alg.outOfValidityDeco = 'bad_EvetoTrueTau_eff' + selectionPostfix
541 alg.taus = config.readName (self.containerName)
542 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
543 if self.saveDetailedSF:
544 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
545 'EvetoTrueTau_effSF' + postfix, auxType='float')
546 sfList += [alg.scaleFactorDecoration]
547
549 alg = config.createAlgorithm( 'CP::AsgObjectScaleFactorAlg',
550 'TauCombinedEfficiencyScaleFactorAlg' )
551 alg.particles = config.readName (self.containerName)
552 alg.inScaleFactors = sfList
553 alg.outScaleFactor = 'effSF' + postfix + '_%SYS%'
554 config.addOutputVar (self.containerName, alg.outScaleFactor,
555 'effSF' + postfix, auxType='float')
556
557
559 def __init__ (self) :
560 super().__init__()
561 self.addOption (
562 'inputTaus', 'TauJets', type=str,
563 noneAction='error',
564 info="the name of the input tau container."
565 )
566 self.addOption (
567 'inputTausMuRM', 'TauJets_MuonRM', type=str,
568 noneAction='error',
569 info="the name of the input tau container with muon removal applied."
570 )
571 self.addOption (
572 'outputTaus', 'TauJets_MuonRmCombined', type=str,
573 noneAction='error',
574 info="the name of the output tau container."
575 )
576
577 def instanceName (self) :
578 """Return the instance name for this block"""
579 return self.outputTaus
580
581 def makeAlgs (self, config) :
582
583 if config.isPhyslite() :
584 raise(RuntimeError("Muon removal taus is not available in Physlite mode"))
585
586 alg = config.createAlgorithm( 'CP::TauCombineMuonRMTausAlg', 'TauCombineMuonRMTausAlg' )
587 alg.taus = self.inputTaus
588 alg.muonrm_taus = self.inputTausMuRM
589 alg.combined_taus = self.outputTaus
590
591class TauTriggerAnalysisSFBlock (ConfigBlock):
592
593 def __init__ (self) :
594 super().__init__()
595 self.addDependency('EventSelection', required=False)
596 self.addDependency('EventSelectionMerger', required=False)
597 self.addOption ('triggerChainsPerYear', {}, type=dict,
598 info="a dictionary with key (string) the year and value (list of "
599 "strings) the trigger chains.")
600 self.addOption ('useGNTau', False, type=bool,
601 info="use GNTau-based ID instead of RNNTau ID. "
602 "Recommendations: experimental feature and might become default soon.")
603 self.addOption ('tauID', '', type=str,
604 info="the tau-jet quality WP to use.")
605 self.addOption ('prefixSF', 'trigEffSF', type=str,
606 info="the decoration prefix for trigger scale factors.")
607 self.addOption ('includeAllYearsPerRun', False, type=bool,
608 info="all configured years in the LHC run will "
609 "be included in all jobs.")
610 self.addOption ('removeHLTPrefix', True, type=bool,
611 info="remove the HLT prefix from trigger chain names.")
612 self.addOption ('containerName', '', type=str,
613 info="the input tau-jet container, with a possible selection, in "
614 "the format `container` or `container.selection`.",
615 meta={'role':'containerRef'})
616
617 def instanceName (self) :
618 """Return the instance name for this block"""
619 return self.containerName + '_' + self.prefixSF + '_' + self.tauID
620
621 def makeAlgs (self, config) :
622
623 if config.dataType() is not DataType.Data:
624 log = logging.getLogger('TauTriggerAnalysisSF')
625
626 # Temporary skip all MC campaigns apart from MC23a when using GNTau until SFs are available
627 if( self.useGNTau and config.campaign() is not Campaign.MC23a ):
628 log.warning("Tau trigger scale factors are only available for MC23a when using GNTau offline ID")
629 return
630
631 # Temporary skip for MC23e until SFs are available
632 if config.campaign() is Campaign.MC23e:
633 log.warning("Tau trigger scale factors are not available yet for MC23e")
634 return
635 # Temporary skip for MC23g until SFs are available
636 if config.campaign() is Campaign.MC23g:
637 log.warning("Tau trigger scale factors are not available yet for MC23g")
638 return
639
640 triggers = trigger_set(config, self.triggerChainsPerYear,
641 self.includeAllYearsPerRun)
642 for chain in triggers:
643 chain_noHLT = chain.replace("HLT_", "")
644 chain_out = chain_noHLT if self.removeHLTPrefix else chain
645 alg = config.createAlgorithm( 'CP::TauEfficiencyCorrectionsAlg',
646 'TauTrigEfficiencyCorrectionsAlg_' + chain )
647 config.addPrivateTool( 'efficiencyCorrectionsTool',
648 'TauAnalysisTools::TauEfficiencyCorrectionsTool' )
649 # SFTriggerHadTau correction type from
650 # https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h#L79
651 alg.efficiencyCorrectionsTool.EfficiencyCorrectionTypes = [12]
652 if config.geometry() is LHCPeriod.Run2:
653 alg.efficiencyCorrectionsTool.Campaign = "mc20"
654 else:
655 alg.efficiencyCorrectionsTool.Campaign = config.campaign().value
656 alg.efficiencyCorrectionsTool.TriggerName = chain
657 alg.efficiencyCorrectionsTool.useGNTau = self.useGNTau
658
659 # JetIDLevel from
660 # https://gitlab.cern.ch/atlas/athena/-/blob/main/PhysicsAnalysis/TauID/TauAnalysisTools/TauAnalysisTools/Enums.h#L79
661 if self.tauID not in _RNN_JETID_LEVELS:
662 raise ValueError (f"invalid tauID: \"{self.tauID}\". Allowed values are Loose, Medium, Tight")
663 alg.efficiencyCorrectionsTool.JetIDLevel = _RNN_JETID_LEVELS[self.tauID]
664 alg.efficiencyCorrectionsTool.TriggerSFMeasurement = "combined"
665 alg.efficiencyCorrectionsTool.useFastSim = config.dataType() is DataType.FastSim
666
667 alg.scaleFactorDecoration = f"tau_{self.prefixSF}_{chain_out}_%SYS%"
668 alg.outOfValidity = 2 #silent
669 alg.outOfValidityDeco = f"bad_eff_tautrig_{chain_out}"
670 alg.taus = config.readName (self.containerName)
671 alg.preselection = config.getPreselection (self.containerName, self.tauID)
672 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, f"{self.prefixSF}_{chain_out}", auxType='float')
673
674@groupBlocks
if(pathvar)