ATLAS Offline Software
Loading...
Searching...
No Matches
TrigTauHypoTool.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3from typing import Any
4
5from AthenaCommon.SystemOfUnits import GeV
6from AthenaConfiguration.AthConfigFlags import AthConfigFlags
7
8from TriggerMenuMT.HLT.Tau.TauConfigurationTools import getChainIDConfigName
9from .TrigTauHypoMonitoring import getTrigTauPrecisionIDHypoToolMonitoring, getTrigTauPrecisionDiKaonHypoToolMonitoring
10
11from AthenaCommon.Logging import logging
12log = logging.getLogger('TrigHLTTauHypoTool')
13
14
15#============================================================================================
16# Precision step hypothesis tool
17#============================================================================================
18def TrigTauPrecisionHypoToolFromDict(flags: AthConfigFlags, chainDict: dict[str, Any]):
19 chainPart = chainDict['chainParts'][0]
20
21 identification = getChainIDConfigName(flags, chainPart)
22
23 if identification == 'MesonCuts':
24 # Meson cut-based triggers (ATR-22644)
25 return TrigTauPrecisionDiKaonHypoToolFromDict(flags, chainDict)
26 else:
27 # Everything else
28 return TrigTauPrecisionIDHypoToolFromDict(flags, chainDict)
29
30
31#-----------------------------------------------------------------
32# Standard tau triggers configuration
33#-----------------------------------------------------------------
34class TauCuts:
35 def __init__(self, flags, chain_part: dict[str, Any]):
36 self._id = getChainIDConfigName(flags, chain_part)
37 self._chain_part = chain_part
38
39 self._id_wp = ''
40 self._highpt_id_wp = ''
41 self._do_perfcore = True
42 self._do_perfiso = True
43
44 self._use_rnn_selection = False
45
46 if self._id in ['idperf', 'noperf', 'perfcore', 'perfiso', 'perf']:
47 self._do_perfcore = self._id in ['perfcore', 'perf']
48 self._do_perfiso = self._id in ['perfiso', 'perf']
49
50 else:
51 if self._id in ['DeepSet', 'RNNLLP'] and self._chain_part['selection'].endswith('RNN'):
52 # Support for the legacy triggers
53 self._use_rnn_selection = True
54 self._id_wp = self._chain_part['selection'][:-3] # Remove the "RNN" suffix
55 if self._id_wp in ['medium', 'tight']: self._highpt_id_wp = 'loose'
56
57 else:
58 id_wp = self._chain_part['selection'].removesuffix(self._id).lower()
59
60 # Check for a perf selection specifier
61 sfx = id_wp[-2:]
62 if sfx in ['np', 'pc', 'pi']: id_wp = id_wp[:-2]
63 if sfx in ['np', 'pi']: self._do_perfcore = False
64 if sfx in ['np', 'pc']: self._do_perfiso = False
65
66 # Find the matching WP with the correct casing
67 def find_wp(wp: str, fail: bool = True) -> str:
68 for twp in getattr(flags.Trigger.Offline.Tau, self._id).TargetWPs.keys():
69 if twp.lower() == wp: return twp
70 else:
71 if fail: ValueError(f'Cannot find the "{self._id}" WP "{wp}"')
72 else: return ''
73
74 # Standard ID WP
75 self._id_wp = find_wp(id_wp)
76
77 # High-pT ID WP
78 if id_wp.startswith('medium'): self._highpt_id_wp = find_wp(f'loose{id_wp[6:]}', True)
79 elif id_wp.startswith('tight'): self._highpt_id_wp = find_wp(f'loose{id_wp[5:]}', True)
80
81 @property
82 def n_track_max(self) -> int:
83 return 3 if self._do_perfcore else 999
84
85 @property
86 def n_iso_track_max(self) -> int:
87 return 1 if self._do_perfiso else 999
88
89 @property
90 def pt_min(self) -> float: return float(self._chain_part['threshold']) * GeV
91
92 @property
93 def id_wp_decor(self) -> str:
94 if not self._id_wp: return ''
95 if self._use_rnn_selection: return self._id_wp
96 return f'{self._id}_{self._id_wp}'
97
98 @property
99 def highpt_id_wp_decor(self) -> int:
100 if not self._highpt_id_wp: return ''
101 if self._use_rnn_selection: return self._highpt_id_wp
102 return f'{self._id}_{self._highpt_id_wp}'
103
104
105def TrigTauPrecisionIDHypoToolFromDict(flags: AthConfigFlags, chainDict: dict[str, Any]):
106 '''TrigTauPrecisionIDHypoTool configuration for the standard Tau triggers'''
107 name = chainDict['chainName']
108 chainPart = chainDict['chainParts'][0]
109 cuts = TauCuts(flags, chainPart)
110
111 # Setup the Hypothesis tool
112 from AthenaConfiguration.ComponentFactory import CompFactory
113 currentHypo = CompFactory.TrigTauPrecisionIDHypoTool(
114 name,
115 PtMin=cuts.pt_min,
116 NTracksMax=cuts.n_track_max,
117 NIsoTracksMax=cuts.n_iso_track_max,
118 IDWP=cuts.id_wp_decor,
119 HighPtIDWP=cuts.highpt_id_wp_decor,
120 )
121
122 from TriggerMenuMT.HLT.Tau.TauConfigurationTools import getChainPrecisionSeqName, useBuiltInTauJetRNNScore, getPrecisionSequenceTauIDs, getTauIDScoreVariables
123
124 id_score_monitoring = {}
125
126 precision_seq_name = getChainPrecisionSeqName(chainPart)
127 identification = getChainIDConfigName(flags, chainPart)
128 if identification in ['idperf', 'noperf', 'perf', 'perfcore', 'perfiso']:
129 if identification == 'idperf':
130 # Disable everything, even the pT cut
131 currentHypo.AcceptAll = True
132
133 # Monitor all the included algorithms
134 used_builtin_rnnscore = False
135 for tau_id in getPrecisionSequenceTauIDs(flags, precision_seq_name):
136 # Skip algs without inference scores
137 if tau_id in ['MesonCuts']: continue
138
139 # We can only have at most one alg. using the built-in TauJet RNN score variables
140 if useBuiltInTauJetRNNScore(tau_id, precision_seq_name):
141 if used_builtin_rnnscore:
142 raise ValueError('Cannot have two TauID algorithms with scores stored in the built-in TauJet RNN score variables')
143 used_builtin_rnnscore = True
144
145 id_score_monitoring[tau_id] = getTauIDScoreVariables(tau_id, precision_seq_name)
146
147 else:
148 if useBuiltInTauJetRNNScore(identification, precision_seq_name):
149 # To support the legacy tracktwoMVA/LLP/LRT chains, only in those cases we store the
150 # ID score and passed WPs in the native TauJet variables
151 currentHypo.IDMethod = 1 # TauJet built-in RNN score
152 else:
153 # Decorator-based triggers
154 currentHypo.IDMethod = 2 # Use decorators
155
156 # Monitor this algorithm only
157 id_score_monitoring[identification] = getTauIDScoreVariables(identification, precision_seq_name)
158
159 # For any triggers following the tracktwoMVA reconstruction (2023+ DeepSet and GNTau)
160 if chainPart['reconstruction'] == 'tracktwoMVA':
161 currentHypo.TrackPtCut = 1.5*GeV
162 currentHypo.HighPtSelectionIDThr = 200*GeV
163 currentHypo.HighPtSelectionJetThr = 430*GeV
164
165 # Only monitor chains with the 'tauMon:online' groups
166 if 'tauMon:online' in chainDict['monGroups']:
167 currentHypo.MonTool = getTrigTauPrecisionIDHypoToolMonitoring(flags, name, id_score_monitoring.keys())
168
169 # TauID Score monitoring
170 currentHypo.MonitoredIDScores = id_score_monitoring
171
172 return currentHypo
173
174
175#-----------------------------------------------------------------
176# Meson cut-based triggers configuration (ATR-22644 + ATR-23239)
177#-----------------------------------------------------------------
178from collections import namedtuple
179
180DiKaonCuts = namedtuple('DiKaonCuts', 'massTrkSysMin massTrkSysMax massTrkSysKaonMin massTrkSysKaonMax massTrkSysKaonPiMin massTrkSysKaonPiMax targetMassTrkSysKaonPi leadTrkPtMin PtMin EMPOverTrkSysPMax')
181thresholds_dikaon = {
182 ('dikaonmass', 25): DiKaonCuts(0.0*GeV, 1000.0*GeV, 0.987*GeV, 1.060*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 15.0*GeV, 25.0*GeV, 1.5),
183 ('dikaonmass', 35): DiKaonCuts(0.0*GeV, 1000.0*GeV, 0.987*GeV, 1.060*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 25.0*GeV, 35.0*GeV, 1.5),
184
185 ('kaonpi1', 25): DiKaonCuts(0.0*GeV, 1000.0*GeV, 0.0*GeV, 1000.0*GeV, 0.79*GeV, 0.99*GeV, 0.89*GeV, 15.0*GeV, 25.0*GeV, 1.0),
186 ('kaonpi1', 35): DiKaonCuts(0.0*GeV, 1000.0*GeV, 0.0*GeV, 1000.0*GeV, 0.79*GeV, 0.99*GeV, 0.89*GeV, 25.0*GeV, 35.0*GeV, 1.0),
187
188 ('kaonpi2', 25): DiKaonCuts(0.0*GeV, 1000.0*GeV, 0.0*GeV, 1000.0*GeV, 1.8*GeV, 1.93*GeV, 1.865*GeV, 15.0*GeV, 25.0*GeV, 1.0),
189 ('kaonpi2', 35): DiKaonCuts(0.0*GeV, 1000.0*GeV, 0.0*GeV, 1000.0*GeV, 1.8*GeV, 1.93*GeV, 1.865*GeV, 25.0*GeV, 35.0*GeV, 1.0),
190
191 ('dipion1', 25): DiKaonCuts(0.475*GeV, 1.075*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 15.0*GeV, 25.0*GeV, 1.0),
192 ('dipion2', 25): DiKaonCuts(0.460*GeV, 0.538*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 15.0*GeV, 25.0*GeV, 1.0),
193 ('dipion3', 25): DiKaonCuts(0.279*GeV, 0.648*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 25.0*GeV, 25.0*GeV, 2.2),
194 ('dipion4', 25): DiKaonCuts(0.460*GeV, 1.075*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 1000.0*GeV, 0.0*GeV, 15.0*GeV, 25.0*GeV, 1.0),
195}
196
197SinglePionCuts = namedtuple('SinglePionCuts', 'leadTrkPtMin PtMin NTracksMax NIsoTracksMax dRmaxMax etOverPtLeadTrkMin etOverPtLeadTrkMax')
198thresholds_singlepion = {
199 ('singlepion', 25): SinglePionCuts(30.0*GeV, 25.0*GeV, 1, 0, 0.06, 0.4, 0.85),
200}
201
202def TrigTauPrecisionDiKaonHypoToolFromDict(flags: AthConfigFlags, chainDict: dict[str, Any]):
203 '''TrigTauPrecisionDiKaonHypoTool configuration for the meson cut-based Tau triggers (ATR-22644)'''
204 name = chainDict['chainName']
205 chainPart = chainDict['chainParts'][0]
206
207 # Setup the Hypothesis tool
208 from AthenaConfiguration.ComponentFactory import CompFactory
209 currentHypo = CompFactory.TrigTauPrecisionDiKaonHypoTool(name)
210
211 key = (chainPart['selection'], int(chainPart['threshold']))
212 if key in thresholds_dikaon:
213 thr = thresholds_dikaon[key]
214 currentHypo.PtMin = thr.PtMin
215 currentHypo.leadTrkPtMin = thr.leadTrkPtMin
216 currentHypo.massTrkSysMin = thr.massTrkSysMin
217 currentHypo.massTrkSysMax = thr.massTrkSysMax
218 currentHypo.massTrkSysKaonMin = thr.massTrkSysKaonMin
219 currentHypo.massTrkSysKaonMax = thr.massTrkSysKaonMax
220 currentHypo.massTrkSysKaonPiMin = thr.massTrkSysKaonPiMin
221 currentHypo.massTrkSysKaonPiMax = thr.massTrkSysKaonPiMax
222 currentHypo.targetMassTrkSysKaonPi = thr.targetMassTrkSysKaonPi
223 currentHypo.EMPOverTrkSysPMax = thr.EMPOverTrkSysPMax
224
225 elif key in thresholds_singlepion:
226 thr = thresholds_singlepion[key]
227 currentHypo.PtMin = thr.PtMin
228 currentHypo.NTracksMax = thr.NTracksMax
229 currentHypo.NIsoTracksMax = thr.NIsoTracksMax
230 currentHypo.leadTrkPtMin = thr.leadTrkPtMin
231 currentHypo.dRmaxMax = thr.dRmaxMax
232 currentHypo.etOverPtLeadTrkMin = thr.etOverPtLeadTrkMin
233 currentHypo.etOverPtLeadTrkMax = thr.etOverPtLeadTrkMax
234
235 currentHypo.MonTool = getTrigTauPrecisionDiKaonHypoToolMonitoring(flags, name)
236
237 return currentHypo
238
239
240
241#============================================================================================
242# Tracking step hypothesis tool (without selection)
243#============================================================================================
244def TrigTauTrackingHypoToolFromDict(flags: AthConfigFlags, chainDict: dict[str, Any]):
245 name = chainDict['chainName']
246
247 from AthenaConfiguration.ComponentFactory import CompFactory
248 currentHypo = CompFactory.TrigTauTrackingHypoTool(name)
249
250 return currentHypo
251
252
253
254#============================================================================================
255# CaloMVA step hypothesis tool
256#============================================================================================
257def TrigTauCaloMVAHypoToolFromDict(flags: AthConfigFlags, chainDict: dict[str, Any]):
258 name = chainDict['chainName']
259 threshold = float(chainDict['chainParts'][0]['threshold'])
260
261 from AthenaConfiguration.ComponentFactory import CompFactory
262 currentHypo = CompFactory.TrigTauCaloHypoTool(name)
263 currentHypo.PtMin = threshold * GeV
264
265 return currentHypo
266
__init__(self, flags, dict[str, Any] chain_part)
TrigTauTrackingHypoToolFromDict(AthConfigFlags flags, dict[str, Any] chainDict)
TrigTauPrecisionIDHypoToolFromDict(AthConfigFlags flags, dict[str, Any] chainDict)
TrigTauCaloMVAHypoToolFromDict(AthConfigFlags flags, dict[str, Any] chainDict)
TrigTauPrecisionDiKaonHypoToolFromDict(AthConfigFlags flags, dict[str, Any] chainDict)
TrigTauPrecisionHypoToolFromDict(AthConfigFlags flags, dict[str, Any] chainDict)