ATLAS Offline Software
Loading...
Searching...
No Matches
JetTriggerDecoratorAlg.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
7#include <algorithm>
8#include <regex>
9#include <set>
10#include <sstream>
11#include <string>
12#include <tuple>
13#include <vector>
14
17
18namespace
19{
20
21// Returns the L1 RoI ET for Run-2 and Run-3 EDMs respectively.
22inline float
23getL1JetEt(const xAOD::JetRoI* roi)
24{
25 return roi->et8x8();
26}
27
28inline float
29getL1JetEt(const xAOD::jFexSRJetRoI* roi)
30{
31 return static_cast<float>(roi->et());
32}
33
34// Returns the list of fired threshold names for a Run-2 L1 RoI.
35inline const std::vector<std::string>&
36getL1JetThresholds(const xAOD::JetRoI* roi)
37{
38 return roi->thrNames();
39}
40
41// Parsed L1 leg token: the threshold-name (without leg multiplicity, e.g.
42// "jJ50") and its integer threshold. The chain L1 lower-chain name is
43// event-constant, so it is parsed once per event (see parseL1LegTokens)
44// rather than re-parsed for every candidate RoI.
45struct L1LegToken {
46 std::string name; // legName_noMultiplicity, e.g. "jJ50"
47 int threshold = 0;
48};
49
50// Parse the L1 lower-chain name into (name, threshold) leg tokens. Splits
51// on '_' (after mapping '-' -> '_') and applies the fixed leg-name regex.
52// Constructed once per event; the regex itself is a function-local static
53// const so it is compiled exactly once for the whole job.
54inline std::vector<L1LegToken> parseL1LegTokens(const std::string& l1Name) {
55 static const std::regex l1NameParser(
56 "(\\d*)(j?J)(\\d*)((p|\\.)(\\d*)ETA(\\d*))?");
57 static const std::regex dashToUnderscore("-");
58
59 std::vector<L1LegToken> tokens;
60 std::stringstream ss(std::regex_replace(l1Name, dashToUnderscore, "_"));
61 std::string legName;
62 std::smatch match;
63 while (getline(ss, legName, '_')) {
64 if (std::regex_match(legName, match, l1NameParser)) {
65 std::string legName_noMultiplicity =
66 match[2].str() + match[3].str() + match[4].str();
67 int threshold = match[3].str().empty() ? 1 : std::stoi(match[3].str());
68 tokens.push_back({std::move(legName_noMultiplicity), threshold});
69 }
70 }
71 return tokens;
72}
73
74template <typename RoI, typename Container, typename ThrAccessor>
75std::tuple<float, float, float, float, std::vector<int>> matchL1Container(
76 const TLorentzVector& jetP4, const Container& container,
77 ThrAccessor thrAccessor, const std::vector<L1LegToken>& l1LegTokens,
78 float drMax) {
79 const RoI* bestL1 = nullptr;
80 float minDRL1 = drMax;
81 std::set<int> L1Thresholds;
82
83 for (const RoI* l1_jet : container) {
84 TLorentzVector l1_jet_p4;
85 l1_jet_p4.SetPtEtaPhiM(
86 getL1JetEt(l1_jet), l1_jet->eta(), l1_jet->phi(), 0.);
87 const float dR = static_cast<float>(jetP4.DeltaR(l1_jet_p4));
88 if (dR < minDRL1) {
89 minDRL1 = dR;
90 bestL1 = l1_jet;
91 }
92 }
93
94 // Only the finally-selected RoI's thresholds are reported.
95 if (bestL1) {
96 const auto& thrNames = thrAccessor(bestL1);
97 for (const L1LegToken& tok : l1LegTokens) {
98 for (const auto& thr : thrNames) {
99 if (thr == tok.name) {
100 L1Thresholds.insert(tok.threshold);
101 }
102 }
103 }
104 }
105
106 return {
107 bestL1 ? getL1JetEt(bestL1) : -99.f,
108 bestL1 ? bestL1->eta() : -99.f,
109 bestL1 ? bestL1->phi() : -99.f,
110 minDRL1,
111 bestL1 ? std::vector<int>(L1Thresholds.begin(), L1Thresholds.end())
112 : std::vector<int>()
113 };
114}
115
116} // anonymous namespace
117
118
119namespace CP
120{
123
126 ANA_CHECK(m_HLTJetsInKey.initialize(
128
129 ANA_CHECK(m_trigDecisionTool.retrieve());
131
133 ANA_CHECK(m_trigConfigTool.retrieve());
134 }
135
136 if(m_doL1Matching){
142 }
143
144 if(m_doHLTMatching){
150
151 // The chain is job-constant: parse its "j" legs once.
152 int ileg = 0;
153 for (const ChainNameParser::LegInfo& legInfo :
155 if (legInfo.signature == "j") {
156 ANA_MSG_VERBOSE(" Leg" << ileg << ": "
157 << " " << legInfo.legName() << " "
158 << legInfo.type() << " "
159 << legInfo.signature << " "
160 << legInfo.threshold);
161
162 int legThreshold = legInfo.threshold;
163
164 if (legInfo.legName().find("gsc") != std::string::npos) {
165 for (const std::string& part : legInfo.legParts) {
166 if (part.find("gsc") != std::string::npos) {
167 legThreshold = std::stoi(part.substr(3));
168 ATH_MSG_DEBUG("GSC leg found. Using threshold "
169 << legThreshold);
170 break;
171 }
172 }
173 }
174
175 m_jetLegs.push_back({ileg, legThreshold, legInfo.legName()});
176 }
177 ileg++;
178 }
179
181 std::find(m_triggerNavBug.begin(), m_triggerNavBug.end(),
182 m_trigger.value()) != m_triggerNavBug.end();
183 }
184
185 ANA_CHECK (m_systematicsList.initialize());
186 return StatusCode::SUCCESS;
187 }
188
189 StatusCode JetTriggerDecoratorAlg::execute(const EventContext& ctx) {
192 if (m_doL1Matching) {
193 if (m_usePhaseIL1) {
195 m_l1ThresholdType.value(), ctx,
196 msg()));
197 l1JetsPhaseI = SG::makeHandle(m_L1JetsPhaseIInKey, ctx);
198 ANA_CHECK(l1JetsPhaseI.isValid());
199 } else {
200 l1Jets = SG::makeHandle(m_L1JetsInKey, ctx);
201 ANA_CHECK(l1Jets.isValid());
202 }
203 }
205 if (m_doHLTMatching) {
207 ANA_MSG_DEBUG(m_trigger << " isPassed "
208 << m_emulationTool->isPassed(m_trigger.value()));
209 else {
210 hltJetsFromCont = SG::makeHandle(m_HLTJetsInKey, ctx);
211 ANA_CHECK(hltJetsFromCont.isValid());
212 }
213 }
214
216 frd.setChainGroup(m_trigger.value());
217 // prepare Run2 emulation results
218 std::unordered_map<std::string,
219 std::vector<std::pair<const xAOD::Jet*, bool>>>
220 emulatedJets = {};
222 emulatedJets = m_emulationTool->getEmulatedJets(m_trigger);
223 bool isTrigPassed = m_trigDecisionTool->isPassed(m_trigger.value());
224 const TrigConf::HLTChain* hltChain =
225 m_trigDecisionTool->ExperimentalAndExpertMethods()
226 .getChainConfigurationDetails(m_trigger);
227 std::vector<L1LegToken> l1LegTokens;
228 if (hltChain) {
229 l1LegTokens = parseL1LegTokens(hltChain->lower_chain_name());
230 } else {
231 // Chain not in this file's menu (e.g. a chain of another year):
232 // it cannot have fired, so only the default decorations are written.
234 ANA_MSG_WARNING("Trigger chain " << m_trigger.value()
235 << " not found in the trigger menu; writing default "
236 "matching decorations (warning printed once)");
238 }
239 isTrigPassed = false;
240 }
241
242 // HLT candidates of each "j" leg (aligned with m_jetLegs): they depend
243 // only on the event, so are collected once before the jet loops. The
244 // first nNavCandidates[i] of them come from the trigger navigation (or
245 // the Run 2 emulation), the rest from the container (nav-bug triggers).
246 std::vector<std::vector<const xAOD::IParticle*>> hltCandidates(
247 m_jetLegs.size());
248 std::vector<std::size_t> nNavCandidates(m_jetLegs.size(), 0);
249 if (m_doHLTMatching && isTrigPassed) {
250 for (std::size_t i = 0; i < m_jetLegs.size(); ++i) {
251 const JetLeg& leg = m_jetLegs[i];
252 std::vector<const xAOD::IParticle*>& candidates = hltCandidates[i];
253
257
258 if (m_useEmulationTool) {
259 const auto emulated = emulatedJets.find(leg.name);
260 if (emulated != emulatedJets.end()) {
261 candidates.reserve(emulated->second.size());
262 for (const auto& jetAndBtag : emulated->second)
263 candidates.push_back(jetAndBtag.first);
264 }
265 ANA_MSG_DEBUG(" Emulated jets for " << leg.name << ": "
266 << candidates.size());
267 nNavCandidates[i] = candidates.size();
268 }
269
273
274 else {
275 frd.setRestrictRequestToLeg(leg.index);
276 const auto hlt_jetsFromtrigDec =
278
279 for (const auto& hlt_jet_link : hlt_jetsFromtrigDec) {
280 const xAOD::IParticle* hlt_jetFromtrigDec = *hlt_jet_link.link;
281 if (!hlt_jetFromtrigDec)
282 continue;
283 candidates.push_back(hlt_jetFromtrigDec);
284 }
285 nNavCandidates[i] = candidates.size();
286
287 // Start adding missing HLT jets -- only for buggy triggers
288 if (m_isNavBugTrigger) {
289 for (const xAOD::Jet* jetFromCont : *hltJetsFromCont) {
290 bool alreadyIn = false;
291 for (const xAOD::IParticle* seenJet : candidates) {
292 if (isSameJet(seenJet, jetFromCont)) {
293 alreadyIn = true;
294 break;
295 }
296 }
297 if (alreadyIn)
298 continue;
299
300 candidates.push_back(jetFromCont);
301 ANA_MSG_DEBUG("Added missing HLT jet from container: pt="
302 << jetFromCont->pt()
303 << " eta=" << jetFromCont->eta()
304 << " phi=" << jetFromCont->phi());
305 }
306 }
307 } // end Run 3 access
308 }
309 }
310
311 for (const auto& sys : m_systematicsList.systematicsVector()) {
312 const xAOD::JetContainer* jets = nullptr;
313 ANA_CHECK(m_jetsHandle.retrieve(jets, sys, ctx));
314
315 for (const xAOD::Jet* jet : *jets) {
316 const TLorentzVector jetP4 = jet->p4();
317
321
322 if (m_doL1Matching) {
323 float l1Et = -99.f;
324 float l1Eta = -99.f;
325 float l1Phi = -99.f;
326 float minDRL1 = m_l1dR.value();
327 std::vector<int> l1ThresholdsVec;
328
329 if (isTrigPassed) {
330 if (m_usePhaseIL1) {
331 const JfexThresholdTable& jfexTable = m_jfexThresholdTable;
332 std::tie(l1Et, l1Eta, l1Phi, minDRL1, l1ThresholdsVec) =
333 matchL1Container<xAOD::jFexSRJetRoI>(
334 jetP4, *l1JetsPhaseI,
335 [&jfexTable](const xAOD::jFexSRJetRoI* r) {
336 return jfexTable.decode(*r);
337 },
338 l1LegTokens, m_l1dR.value());
339 } else {
340 // Legacy L1Calo path: use JetRoI::thrNames().
341 std::tie(l1Et, l1Eta, l1Phi, minDRL1, l1ThresholdsVec) =
342 matchL1Container<xAOD::JetRoI>(
343 jetP4, *l1Jets,
344 [](const xAOD::JetRoI* r)
345 -> const std::vector<std::string>& {
346 return getL1JetThresholds(r);
347 },
348 l1LegTokens, m_l1dR.value());
349 }
350 }
351
352 m_L1Et_decor.set(*jet, l1Et, sys);
353 m_L1Eta_decor.set(*jet, l1Eta, sys);
354 m_L1Phi_decor.set(*jet, l1Phi, sys);
355 m_L1DR_decor.set(*jet, minDRL1, sys);
356 m_L1Threshold_decor.set(*jet, l1ThresholdsVec, sys);
357 } // end L1 matching
358
362
363 if (m_doHLTMatching) {
364 const xAOD::IParticle* bestHLT = nullptr;
365 float minDRHLT = m_hltDR.value();
366 std::set<int> HLTThresholds = {};
367
368 for (std::size_t i = 0; i < m_jetLegs.size(); ++i) {
369 const int legThreshold = m_jetLegs[i].threshold;
370 const std::vector<const xAOD::IParticle*>& candidates =
371 hltCandidates[i];
372
373 for (std::size_t j = 0; j < candidates.size(); ++j) {
374 const xAOD::IParticle* hlt_jet = candidates[j];
375 float dR = jetP4.DeltaR(hlt_jet->p4());
376
378 " pt: " << hlt_jet->pt() << " eta: " << hlt_jet->eta()
379 << " phi: " << hlt_jet->phi() << " dR: " << dR
380 << " (fromContainer=" << (j >= nNavCandidates[i])
381 << ")");
382
383 if (bestHLT && isSameJet(bestHLT, hlt_jet))
384 HLTThresholds.insert(legThreshold);
385 else if (dR < minDRHLT) {
386 minDRHLT = dR;
387 bestHLT = hlt_jet;
388 HLTThresholds.clear();
389 HLTThresholds.insert(legThreshold);
390 }
391 } // Loop over the leg's HLT candidates
392 }
393
394 m_HLTPt_decor.set(*jet, bestHLT ? bestHLT->pt() : -99., sys);
395 m_HLTEta_decor.set(*jet, bestHLT ? bestHLT->eta() : -99., sys);
396 m_HLTPhi_decor.set(*jet, bestHLT ? bestHLT->phi() : -99., sys);
397 m_HLTDR_decor.set(*jet, minDRHLT, sys);
398
399 std::vector<int> hltThresh;
400 if (bestHLT)
401 hltThresh = std::vector<int>(HLTThresholds.begin(),
402 HLTThresholds.end());
403 m_HLTThreshold_decor.set(*jet, hltThresh, sys);
404
405 ANA_MSG_VERBOSE("Summary "
406 << " Trigger: " << m_trigger << " bestHLT pT: "
407 << (bestHLT ? bestHLT->pt() : -99.));
408 }
409 }
410 };
411 return StatusCode::SUCCESS;
412 }
413
415 {
416 // Need this function because jet1 == jet2 would return false when comparing b-jet to untagged jet
417 return (jet1->p4().DeltaR(jet2->p4()) < 0.01) && (std::abs(jet1->pt() - jet2->pt()) < 100);
418 }
419
420}
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_MSG_DEBUG(x,...)
#define ANA_MSG_DEBUG(xmsg,...)
Macro printing debug messages.
#define ANA_MSG_VERBOSE(xmsg,...)
Macro printing verbose messages.
#define ANA_CHECK(EXP)
check whether the given expression was successful
#define ANA_MSG_WARNING(xmsg,...)
Macro printing warning messages.
static Double_t ss
SysListHandle m_systematicsList
the systematics list we run
ToolHandle< TrigConf::ITrigConfigTool > m_trigConfigTool
Gaudi::Property< bool > m_doHLTMatching
Gaudi::Property< float > m_l1dR
Gaudi::Property< bool > m_useEmulationTool
Gaudi::Property< bool > m_usePhaseIL1
CP::SysWriteDecorHandle< std::vector< int > > m_L1Threshold_decor
CP::SysWriteDecorHandle< float > m_HLTPt_decor
CP::SysWriteDecorHandle< float > m_HLTPhi_decor
CP::SysWriteDecorHandle< std::vector< int > > m_HLTThreshold_decor
ToolHandle< Trig::ITrigBtagEmulationTool > m_emulationTool
bool isSameJet(const xAOD::IParticle *jet1, const xAOD::IParticle *jet2) const
SG::ReadHandleKey< xAOD::jFexSRJetRoIContainer > m_L1JetsPhaseIInKey
Gaudi::Property< std::vector< std::string > > m_triggerNavBug
CP::SysWriteDecorHandle< float > m_L1Eta_decor
bool m_isNavBugTrigger
whether m_trigger is in m_triggerNavBug
Gaudi::Property< float > m_hltDR
CP::SysWriteDecorHandle< float > m_L1DR_decor
PublicToolHandle< Trig::TrigDecisionTool > m_trigDecisionTool
CP::SysReadHandle< xAOD::JetContainer > m_jetsHandle
SG::ReadHandleKey< xAOD::JetContainer > m_HLTJetsInKey
CP::SysWriteDecorHandle< float > m_HLTEta_decor
Gaudi::Property< std::string > m_trigger
CP::SysWriteDecorHandle< float > m_HLTDR_decor
CP::SysWriteDecorHandle< float > m_L1Et_decor
SG::ReadHandleKey< xAOD::JetRoIContainer > m_L1JetsInKey
Gaudi::Property< std::string > m_l1ThresholdType
bool m_warnedMissingChain
whether we already warned about m_trigger missing from the menu
Gaudi::Property< bool > m_doL1Matching
CP::SysWriteDecorHandle< float > m_L1Phi_decor
the Phase-I L1 jFEX threshold bit-to-name table, taken from the L1 menu, shared by the algorithms dec...
std::vector< std::string > decode(const xAOD::jFexSRJetRoI &roi) const
the names of the menu thresholds whose bits are set in the RoI's thresholdPatterns (empty if the patt...
Helper class that provides access to information about individual legs.
virtual::StatusCode execute()
execute this algorithm
storage of the time histories of all the cells
virtual bool isValid() override final
Can the handle be successfully dereferenced?
HLT chain configuration information.
const std::string & lower_chain_name() const
FeatureRequestDescriptor & setChainGroup(std::string_view chainGroupName)
Set the desired Chain or Chain Group.
FeatureRequestDescriptor & setRestrictRequestToLeg(const int restrictToLegIndex)
Set to -1 by default, indicating that all legs of multi-leg chains are searched.
Class providing the definition of the 4-vector interface.
virtual double eta() const =0
The pseudorapidity ( ) of the particle.
virtual FourMom_t p4() const =0
The full 4-momentum of the particle.
virtual double pt() const =0
The transverse momentum ( ) of the particle.
virtual double phi() const =0
The azimuthal angle ( ) of the particle.
float et8x8() const
The energy deposited in a 0.8x0.8 area around the RoI.
const std::vector< std::string > & thrNames() const
The names of the thresholds passed by jet candidate.
unsigned int et() const
Methods that require combining results or applying scales.
int r
Definition globals.cxx:22
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition hcg.cxx:359
Select isolated Photons, Electrons and Muons.
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
STL namespace.
Jet_v1 Jet
Definition of the current "jet version".
JetRoI_v2 JetRoI
Definition JetRoI.h:16
JetContainer_v1 JetContainer
Definition of the current "jet container version".
jFexSRJetRoI_v1 jFexSRJetRoI
Define the latest version of the jFexSRJetRoI class.
DataVector< IParticle > IParticleContainer
Simple convenience declaration of IParticleContainer.
a "j" leg of the chain, parsed once in initialize()
Struct containing information on each leg of a chain.
MsgStream & msg
Definition testRead.cxx:32