ATLAS Offline Software
Loading...
Searching...
No Matches
TrigTauInfo.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6#include <regex>
7#include <ranges> //std::views::split
8#include <cstdint>
9
10TrigTauInfo::TrigTauInfo(const std::string& trigger)
11 : m_trigger{trigger}
12{
14}
15
16TrigTauInfo::TrigTauInfo(const std::string& trigger, const std::map<std::string, float>& L1Phase1_thresholds)
17 : m_trigger{trigger}
18{
19 parseTriggerString(L1Phase1_thresholds);
20}
21
22TrigTauInfo::TrigTauInfo(const std::string& trigger, const std::map<std::string, float>& L1Phase1_thresholds, const std::map<std::string, uint64_t>& L1Phase1_threshold_patterns)
23 : m_trigger{trigger}
24{
25 parseTriggerString(L1Phase1_thresholds, L1Phase1_threshold_patterns);
26}
27
28TrigTauInfo::TrigTauInfo(const std::string& trigger, const std::map<int, int>& L1Phase1ThrMap_eTAU, const std::map<int, int>& L1Phase1ThrMap_jTAU)
29 : m_trigger{trigger}
30{
31 parseTriggerString(L1Phase1ThrMap_eTAU, L1Phase1ThrMap_jTAU);
32}
33
34void TrigTauInfo::parseTriggerString(bool remove_L1_phase1_thresholds)
35{
36 std::string clean_trigger = m_trigger;
37
38 // Change the "L1_" prefix to "L1" internally, in case the trigger being parsed is a pure L1 trigger with the usual L1 standalone naming scheme
39 if(clean_trigger.size() > 3 && clean_trigger.rfind("L1_", 0) == 0) {
40 clean_trigger = "L1" + clean_trigger.substr(3);
41 }
42
43 std::vector<std::string> sections;
44 for (auto&& subrange : std::views::split(m_trigger, '_')) sections.emplace_back(subrange.begin(), subrange.end());
45
46 std::regex tau_rgx("^(\\d*)tau(\\d+)$");
47 std::regex elec_rgx("^(\\d*)e(\\d+)$");
48 std::regex muon_rgx("^(\\d*)mu(\\d+)$");
49 std::regex gamma_rgx("^(\\d*)g(\\d+)$");
50 std::regex jet_rgx("^(\\d*)j(\\d+)$");
51 std::regex met_rgx("^xe(\\d+)$");
52 std::regex noalg_rgx("^noalg$");
53 std::regex l1_rgx("^L1.*$");
54 std::regex l1_tau_rgx("(\\d*)(e|j|c|)TAU(\\d+)(L|M|T|HL|HM|HT|H|IM|I|)");
55 std::regex l1_toposeparate_rgx("^(\\d{0,2})(DETA|DPHI)(\\d{0,2})$");
56 std::regex topo_rgx("^.*(invm|dR|deta|dphi)AB.*$");
57 std::regex ditauomni_rgx("^ditauOmni(\\d)+Trk(\\d)+$");
58 std::vector<std::regex*> all_regexes = {&tau_rgx, &elec_rgx, &muon_rgx, &gamma_rgx, &jet_rgx, &met_rgx, &l1_rgx, &ditauomni_rgx};
59
60 std::regex tau_type_rgx("^(ptonly|tracktwoMVA|tracktwoLLP|trackLRT)$");
61 std::regex tau_ID_rgx("^(idperf|noperf|perfcore|perfiso|perf|veryloose.*|loose.*|medium.*|tight.*)$");
62 std::regex tau_HitZ_rgx("^(([\\dp]*mmX[\\dp]*mm)?HitZ.*)$");
63 std::regex tau_CHPreselID_rgx("^(idperfCHP|veryloose.*CHP.*|loose.*CHP.*|medium.*CHP.*|tight.*CHP.*)$");
64
65 std::smatch match;
66 std::regex_token_iterator<std::string::iterator> rend;
67
68 std::vector<bool> is_tau_probe_leg;
69
70 // Check each leg
71 int hlt_leg_idx = -1;
72 std::vector<std::string> leg;
73 for(size_t i = 0; i < sections.size(); i++) {
74 leg.push_back(sections[i]); // Attach to the current leg
75 //Match the beginning of a new leg, or the end of the chain
76 if(i == sections.size() - 1 || (std::any_of(all_regexes.begin(), all_regexes.end(), [&sections, i](const std::regex* rgx) { return std::regex_match(sections[i+1], *rgx); }))) {
77 // Process the previous leg, which starts with the item, multiplicity, and threshold
78 if(std::regex_match(leg[0], match, tau_rgx)) {
79 hlt_leg_idx++;
80
81 size_t multiplicity = match[1].str() == "" ? 1 : std::stoi(match[1].str());
82 unsigned int threshold = std::stoi(match[2].str());
83
84 // HLT Tau sequence
85 auto itr = find_if(leg.begin(), leg.end(), [tau_type_rgx](const std::string& s) { return std::regex_match(s, tau_type_rgx); });
86 std::string type = itr != leg.end() ? *itr : "tracktwoMVA"; // Default to the tracktwoMVA sequence
87
88 // HLT Tau ID
89 itr = find_if(leg.begin(), leg.end(), [tau_ID_rgx](const std::string& s) { return std::regex_match(s, tau_ID_rgx); });
90 std::string tau_id = itr != leg.end() ? *itr : "";
91 if(tau_id.starts_with("veryloose")) tau_id = tau_id.substr(9);
92 else if(tau_id.starts_with("loose")) tau_id = tau_id.substr(5);
93 else if(tau_id.starts_with("medium")) tau_id = tau_id.substr(6);
94 else if(tau_id.starts_with("tight")) tau_id = tau_id.substr(5);
95
96 // The WP is a variation (e.g. "mediumvar2GNTauDev1")
97 if(tau_id.starts_with("var")) {
98 std::size_t i = 3; // Take out the "var" prefix
99
100 // Now find the variation number
101 while(i < tau_id.size() && std::isdigit(static_cast<unsigned char>(tau_id[i]))) i++;
102
103 tau_id = tau_id.substr(i);
104 }
105
106 // Get the perf-selection suffix
107 if(tau_id.starts_with("perfcore")) tau_id = tau_id.substr(8);
108 else if(tau_id.starts_with("perfiso")) tau_id = tau_id.substr(7);
109 else if(tau_id.starts_with("noperf")) tau_id = tau_id.substr(6);
110
111 // Override for the old trigger names
112 if(tau_id == "RNN") {
113 if(type == "tracktwoMVA") tau_id = "DeepSet";
114 if(type == "tracktwoLLP" || type == "trackLRT") tau_id = "RNNLLP";
115 }
116
117 // Replacements (this is temporary, the entire TrigTauInfo class will be removed soon, and all this will be handled centrally in Python using the already available infrastructure)
118 if(tau_id == "DS") tau_id = "DeepSet";
119 else if(tau_id == "GNT") tau_id = "GNTau";
120
121 // HitZ algorithm
122 itr = find_if(leg.begin(), leg.end(), [tau_HitZ_rgx](const std::string& s) { return std::regex_match(s, tau_HitZ_rgx); });
123 std::string tau_hitz = itr != leg.end() ? *itr : "";
124 std::string tau_hitz_alg = tau_hitz;
125 if(!tau_hitz_alg.empty()) {
126 // Check if tau_hitz contains "mm", and if it does, extract the part after the last "mm"
127 size_t pos = tau_hitz_alg.rfind("mm");
128 if(pos != std::string::npos) {
129 tau_hitz_alg = tau_hitz_alg.substr(pos + 2);
130 }
131 }
132
133 // HLT Calo+Hits Presel Tau ID
134 itr = find_if(leg.begin(), leg.end(), [tau_CHPreselID_rgx](const std::string& s) { return std::regex_match(s, tau_CHPreselID_rgx); });
135 std::string tau_chpresel_id = itr != leg.end() ? *itr : "";
136 if(tau_chpresel_id.starts_with("veryloose")) tau_chpresel_id = tau_chpresel_id.substr(9);
137 else if(tau_chpresel_id.starts_with("loose")) tau_chpresel_id = tau_chpresel_id.substr(5);
138 else if(tau_chpresel_id.starts_with("medium")) tau_chpresel_id = tau_chpresel_id.substr(6);
139 else if(tau_chpresel_id.starts_with("tight")) tau_chpresel_id = tau_chpresel_id.substr(5);
140
141 // The WP is a variation (e.g. "mediumvar2GNTauDev1")
142 if(tau_chpresel_id.starts_with("var")) {
143 std::size_t i = 3; // Take out the "var" prefix
144
145 // Now find the variation number
146 while(i < tau_chpresel_id.size() && std::isdigit(static_cast<unsigned char>(tau_chpresel_id[i]))) i++;
147
148 tau_chpresel_id = tau_chpresel_id.substr(i);
149 }
150
151 // TauJet container name suffix (base: HLT_TrigTauRecMerged_)
152 std::string tau_jet_container_sfx = type;
153 // Remove the tracktwo prefixes if they are present
154 if(type == "ptonly") tau_jet_container_sfx = "CaloMVAOnly";
155 else if(tau_jet_container_sfx.starts_with("tracktwo")) tau_jet_container_sfx = tau_jet_container_sfx.substr(8);
156 else if(tau_jet_container_sfx.starts_with("track")) tau_jet_container_sfx = tau_jet_container_sfx.substr(5);
157
158 // Check if the leg is a probe leg (to support bootstrapped tau triggers)
159 const bool is_probe_leg = std::find(leg.begin(), leg.end(), "probe") != leg.end();
160
161 for(size_t j = 0; j < multiplicity; j++) {
162 m_HLTThr.push_back(threshold);
163 m_HLTTauTypes.push_back(type);
164 m_HLTTauIDs.push_back(tau_id);
165 m_HLTTauHitZSelections.push_back(tau_hitz);
166 m_HLTTauHitZAlgs.push_back(tau_hitz_alg);
167 m_HLTTauCHPreselIDs.push_back(tau_chpresel_id);
168
169 m_HLTTauLegIndices.push_back(hlt_leg_idx);
170 m_HLTTauLegContainerSfxs.push_back(tau_jet_container_sfx);
171
172 is_tau_probe_leg.push_back(is_probe_leg);
173 }
174 } else if(std::regex_match(leg[0], match, elec_rgx)) {
175 hlt_leg_idx++;
176 size_t multiplicity = match[1].str() == "" ? 1 : std::stoi(match[1].str());
177 unsigned int threshold = std::stoi(match[2].str());
178 for(size_t j = 0; j < multiplicity; j++) m_HLTElecThr.push_back(threshold);
179 } else if(std::regex_match(leg[0], match, muon_rgx)) {
180 hlt_leg_idx++;
181 size_t multiplicity = match[1].str() == "" ? 1 : std::stoi(match[1].str());
182 unsigned int threshold = std::stoi(match[2].str());
183 for(size_t j = 0; j < multiplicity; j++) m_HLTMuonThr.push_back(threshold);
184 } else if(std::regex_match(leg[0], match, gamma_rgx)) {
185 hlt_leg_idx++;
186 size_t multiplicity = match[1].str() == "" ? 1 : std::stoi(match[1].str());
187 unsigned int threshold = std::stoi(match[2].str());
188 for(size_t j = 0; j < multiplicity; j++) m_HLTGammaThr.push_back(threshold);
189 } else if(std::regex_match(leg[0], match, jet_rgx)) {
190 hlt_leg_idx++;
191 size_t multiplicity = match[1].str() == "" ? 1 : std::stoi(match[1].str());
192 unsigned int threshold = std::stoi(match[2].str());
193 for(size_t j = 0; j < multiplicity; j++) m_HLTJetThr.push_back(threshold);
194 } else if(std::regex_match(leg[0], match, met_rgx)) {
195 hlt_leg_idx++;
196 unsigned int threshold = std::stoi(match[2].str());
197 m_HLTMETThr.push_back(threshold);
198 } else if(std::regex_match(leg[0], match, noalg_rgx)) {
199 hlt_leg_idx++;
200 m_isStreamer = true;
201 } else if (std::regex_match(leg[0], match, ditauomni_rgx)) {
202 hlt_leg_idx++;
203 m_HLTBoostedDitauName.push_back(leg[0]);
204 } else if(std::regex_match(leg[0], l1_rgx)){ // Treat the L1 items as a leg
205 for(size_t j = 0; j < leg.size(); j++) {
206 if(std::regex_match(leg[j], topo_rgx)) continue; // Remove HLT topo sections, not part of the L1 item
207
208 // L1Topo items (they all include a "-" in the name, or have a separate "##DETA/PHI##_" prefix):
209 if(leg[j].find('-') != std::string::npos || std::regex_match(leg[j], l1_toposeparate_rgx)) {
210 // We only keep information from the legacy L1Topo item, from which we will not always use all thresholds
211 // Since we won't be adding any more Legacy thresholds, let's hard-code it...
212 if(leg[0] == "L1TAU60" && leg[j] == "DR-TAU12ITAU12I") leg[j] = "TAU12IM"; // L1_TAU60_DR-TAU20ITAU12I, uses "TAU12IM" threshold from the L1Topo item
213 else if(leg.size() == 1 && (leg[0] == "L1DR-TAU20ITAU12I" || leg[0] == "L1DR-TAU20ITAU12I-J25")) {
214 // Uses both TAU items, in the M isolation threshold
215 leg[0] = "L1TAU20IM";
216 leg.push_back("TAU12IM");
217 // Even on combined chains using jets, we don't use the jets threshold
218 }
219 else continue; // Remove the Phase 1 L1Topo items, since we always use a multiplicity threshold
220 }
221
222 m_L1Items.push_back(j == 0 ? leg[j].substr(2, leg[j].size()) : leg[j]); // Remove the "L1" prefix on the first L1 item
223 }
224 }
225
226 // Start a new leg
227 leg = {};
228 }
229 }
230
231 // Support for HitZ bootstrapped tau triggers
232 // We have to check if we have both tag (non-probe) and probe di-tau legs with the same HLT threshold
233 std::map<float, std::pair<std::vector<size_t>, std::vector<size_t>>> tau_n_tag_probes;
234 for(size_t i = 0; i < m_HLTThr.size(); i++) {
235 if(tau_n_tag_probes.find(m_HLTThr.at(i)) == tau_n_tag_probes.end()) tau_n_tag_probes[m_HLTThr.at(i)] = {{}, {}};
236
237 if(is_tau_probe_leg.at(i)) tau_n_tag_probes[m_HLTThr.at(i)].second.push_back(i);
238 else tau_n_tag_probes[m_HLTThr.at(i)].first.push_back(i);
239 }
240 // Keep only the entries with equal number of tag and probe legs
241 for(auto it = tau_n_tag_probes.begin(); it != tau_n_tag_probes.end(); ) {
242 if(it->second.first.size() != it->second.second.size()) it = tau_n_tag_probes.erase(it);
243 else it++;
244 }
245 // If we have tag-probe pairs remaining, this is a bootstrapped trigger.
246 // Get the list of tag legs to remove:
247 std::vector<size_t> legs_to_remove;
248 for(const auto& [thr, n_tag_probe] : tau_n_tag_probes) {
249 legs_to_remove.insert(legs_to_remove.end(), n_tag_probe.first.begin(), n_tag_probe.first.end());
250 }
251 m_isBootstrappedTauTrigger = !legs_to_remove.empty();
252 // Sort them from last to first, so we can remove them without affecting the indices of the remaining legs to remove
253 std::sort(legs_to_remove.begin(), legs_to_remove.end(), std::greater<size_t>());
254 // Remove the tag legs
255 for(size_t i : legs_to_remove) {
256 m_HLTThr.erase(m_HLTThr.begin() + i);
257 m_HLTTauTypes.erase(m_HLTTauTypes.begin() + i);
258 m_HLTTauIDs.erase(m_HLTTauIDs.begin() + i);
260 m_HLTTauHitZAlgs.erase(m_HLTTauHitZAlgs.begin() + i);
261 m_HLTTauCHPreselIDs.erase(m_HLTTauCHPreselIDs.begin() + i);
262
263 m_HLTTauLegIndices.erase(m_HLTTauLegIndices.begin() + i);
265
266 is_tau_probe_leg.erase(is_tau_probe_leg.begin() + i);
267 }
268
269
270 if(!m_L1Items.empty()) {
271 // Build the full L1 string
272 m_L1Item = m_L1Items[0];
273 for(size_t j = 1; j < m_L1Items.size(); j++) m_L1Item += "_" + m_L1Items[j];
274
275 // Get all individual L1 TAU items
276 std::regex_token_iterator<std::string::iterator> rgx_iter(m_L1Item.begin(), m_L1Item.end(), l1_tau_rgx);
277 while(rgx_iter != rend) {
278 const std::string & s = *rgx_iter;
279 if (std::regex_match(s, match, l1_tau_rgx)){
280 size_t multiplicity = match[1].str() == "" ? 1 : std::stoi(match[1].str());
281 std::string item_type = match[2].str(); // e, j, c, or ""
282 int threshold = std::stoi(match[3].str());
283 std::string item_isolation = match[4].str(); // "", L, M, T, HL, HM, HT, IM, H
284
285 // Set the Phase 1 thresholds to -1
286 if(remove_L1_phase1_thresholds && (item_type == "e" || item_type == "j" || item_type == "c")) threshold = -1;
287
288 for(size_t j = 0; j < multiplicity; j++) {
289 m_tauL1Items.push_back(s.substr(match[1].str().size()));
290 m_tauL1Thr.push_back(threshold);
291 m_tauL1Type.push_back(item_type + "TAU");
292 m_tauL1Iso.push_back(item_isolation);
293 m_tauL1ThresholdPattern.push_back(-1);
294 }
295 }
296 rgx_iter++;
297 }
298
299 m_L1Item = "L1" + m_L1Items[0];
300 }
301}
302
303void TrigTauInfo::parseTriggerString(const std::map<std::string, float>& L1Phase1_thresholds)
304{
306
307 for(size_t i = 0; i < m_tauL1Items.size(); i++) {
308 if(m_tauL1Type.at(i) == "TAU") continue; // Skip the legacy items
309
310 const std::string& item = m_tauL1Items.at(i);
311
312 m_tauL1Thr[i] = L1Phase1_thresholds.at(item);
313 }
314}
315
316void TrigTauInfo::parseTriggerString(const std::map<std::string, float>& L1Phase1_thresholds, const std::map<std::string, uint64_t>& L1Phase1_threshold_patterns)
317{
319
320 for(size_t i = 0; i < m_tauL1Items.size(); i++) {
321 if(m_tauL1Type.at(i) == "TAU") continue; // Skip the legacy items
322
323 const std::string& item = m_tauL1Items.at(i);
324
325 m_tauL1Thr[i] = L1Phase1_thresholds.at(item);
326 m_tauL1ThresholdPattern[i] = L1Phase1_threshold_patterns.at(item);
327 }
328}
329
330void TrigTauInfo::parseTriggerString(const std::map<int, int>& L1Phase1ThrMap_eTAU, const std::map<int, int>& L1Phase1ThrMap_jTAU)
331{
332 parseTriggerString(false);
333
334 // Correct the Phase 1 thresholds:
335 for(size_t i = 0; i < m_tauL1Items.size(); i++) {
336 const std::string& item_type = m_tauL1Type.at(i);
337 if(item_type == "eTAU" || item_type == "cTAU") {
338 m_tauL1Thr[i] = L1Phase1ThrMap_eTAU.at(m_tauL1Thr.at(i));
339 } else if(item_type == "jTAU") {
340 m_tauL1Thr[i] = L1Phase1ThrMap_jTAU.at(m_tauL1Thr.at(i));
341 }
342 }
343}
size_t size() const
Number of registered mappings.
std::vector< std::string > m_tauL1Iso
bool m_isBootstrappedTauTrigger
Definition TrigTauInfo.h:98
std::vector< std::string > m_HLTTauLegContainerSfxs
Definition TrigTauInfo.h:97
std::vector< float > m_HLTMuonThr
bool m_isStreamer
Definition TrigTauInfo.h:89
std::vector< float > m_HLTGammaThr
std::string m_L1Item
std::vector< std::string > m_L1Items
std::vector< std::string > m_HLTBoostedDitauName
std::string m_trigger
Definition TrigTauInfo.h:88
std::vector< float > m_HLTElecThr
std::vector< std::string > m_tauL1Type
std::vector< std::string > m_HLTTauHitZAlgs
Definition TrigTauInfo.h:94
std::vector< std::string > m_tauL1Items
std::vector< std::string > m_HLTTauTypes
Definition TrigTauInfo.h:91
std::vector< std::string > m_HLTTauHitZSelections
Definition TrigTauInfo.h:93
void parseTriggerString(bool remove_L1_phase1_thresholds=true)
std::vector< std::string > m_HLTTauCHPreselIDs
Definition TrigTauInfo.h:95
std::vector< std::string > m_HLTTauIDs
Definition TrigTauInfo.h:92
std::vector< int64_t > m_tauL1ThresholdPattern
std::vector< float > m_tauL1Thr
std::vector< float > m_HLTJetThr
std::vector< float > m_HLTMETThr
std::vector< float > m_HLTThr
Definition TrigTauInfo.h:90
std::vector< int > m_HLTTauLegIndices
Definition TrigTauInfo.h:96
std::string find(const std::string &s)
return a remapped string
Definition hcg.cxx:140
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition hcg.cxx:359
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.