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