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
9
10#include <algorithm>
11#include <cstdint>
12#include <exception>
13#include <regex>
14#include <set>
15#include <sstream>
16#include <string>
17#include <tuple>
18#include <vector>
19
21#include "TrigConfData/L1Menu.h"
24
25namespace
26{
27
28// Returns the L1 RoI ET for Run-2 and Run-3 EDMs respectively.
29inline float
30getL1JetEt(const xAOD::JetRoI* roi)
31{
32 return roi->et8x8();
33}
34
35inline float
36getL1JetEt(const xAOD::jFexSRJetRoI* roi)
37{
38 return static_cast<float>(roi->et());
39}
40
41// Returns the list of fired threshold names for a Run-2 L1 RoI.
42inline std::vector<std::string>
43getL1JetThresholds(const xAOD::JetRoI* roi)
44{
45 return roi->thrNames();
46}
47
48inline std::vector<std::string>
49getL1JetThresholds(const xAOD::jFexSRJetRoI* roi,
50 const std::vector<std::string>& bitToName)
51{
52 std::vector<std::string> passed;
53 static const SG::AuxElement::ConstAccessor<uint64_t>
54 thrPatternsAcc("thresholdPatterns");
55 if (!thrPatternsAcc.isAvailable(*roi)) return passed;
56 const uint64_t pat = thrPatternsAcc(*roi);
57 passed.reserve(bitToName.size());
58 for (size_t b = 0; b < bitToName.size(); ++b) {
59 if (((pat >> b) & 1ULL) && !bitToName[b].empty()) {
60 passed.push_back(bitToName[b]);
61 }
62 }
63 return passed;
64}
65
66// Parsed L1 leg token: the threshold-name (without leg multiplicity, e.g.
67// "jJ50") and its integer threshold. The chain L1 lower-chain name is
68// event-constant, so it is parsed once per event (see parseL1LegTokens)
69// rather than re-parsed for every candidate RoI.
70struct L1LegToken {
71 std::string name; // legName_noMultiplicity, e.g. "jJ50"
72 int threshold = 0;
73};
74
75// Parse the L1 lower-chain name into (name, threshold) leg tokens. Splits
76// on '_' (after mapping '-' -> '_') and applies the fixed leg-name regex.
77// Constructed once per event; the regex itself is a function-local static
78// const so it is compiled exactly once for the whole job.
79inline std::vector<L1LegToken> parseL1LegTokens(const std::string& l1Name) {
80 static const std::regex l1NameParser(
81 "(\\d*)(j?J)(\\d*)((p|\\.)(\\d*)ETA(\\d*))?");
82 static const std::regex dashToUnderscore("-");
83
84 std::vector<L1LegToken> tokens;
85 std::stringstream ss(std::regex_replace(l1Name, dashToUnderscore, "_"));
86 std::string legName;
87 std::smatch match;
88 while (getline(ss, legName, '_')) {
89 if (std::regex_match(legName, match, l1NameParser)) {
90 std::string legName_noMultiplicity =
91 match[2].str() + match[3].str() + match[4].str();
92 int threshold = match[3].str().empty() ? 1 : std::stoi(match[3].str());
93 tokens.push_back({legName_noMultiplicity, threshold});
94 }
95 }
96 return tokens;
97}
98
99template <typename RoI, typename Container, typename ThrAccessor>
100std::tuple<float, float, float, float, std::vector<int>> matchL1Container(
101 const xAOD::Jet* jet, const Container& container, ThrAccessor thrAccessor,
102 const std::vector<L1LegToken>& l1LegTokens, float drMax) {
103 const RoI* bestL1 = nullptr;
104 float minDRL1 = drMax;
105 std::set<int> L1Thresholds;
106
107 for (const RoI* l1_jet : container) {
108 TLorentzVector l1_jet_p4;
109 l1_jet_p4.SetPtEtaPhiM(
110 getL1JetEt(l1_jet), l1_jet->eta(), l1_jet->phi(), 0.);
111 const float dR = static_cast<float>(jet->p4().DeltaR(l1_jet_p4));
112 if (dR < minDRL1) {
113 minDRL1 = dR;
114 bestL1 = l1_jet;
115 // Reset so only the finally-selected RoI's thresholds are reported.
116 L1Thresholds.clear();
117 const std::vector<std::string> thrNames = thrAccessor(l1_jet);
118 for (const L1LegToken& tok : l1LegTokens) {
119 for (const auto& thr : thrNames) {
120 if (thr == tok.name) {
121 L1Thresholds.insert(tok.threshold);
122 }
123 }
124 }
125 }
126 }
127
128 return {
129 bestL1 ? getL1JetEt(bestL1) : -99.f,
130 bestL1 ? bestL1->eta() : -99.f,
131 bestL1 ? bestL1->phi() : -99.f,
132 minDRL1,
133 bestL1 ? std::vector<int>(L1Thresholds.begin(), L1Thresholds.end())
134 : std::vector<int>()
135 };
136}
137
138} // anonymous namespace
139
140
141namespace CP
142{
144 ISvcLocator* svcLoc)
145 : EL::AnaAlgorithm(name, svcLoc) {}
146
181
183 const EventContext& ctx) {
184 const TrigConf::L1Menu* l1menu = nullptr;
185 try {
186 l1menu = &m_trigConfigTool->l1Menu(ctx);
187 } catch (const std::exception& e) {
188 ANA_MSG_ERROR("Could not read the L1 menu in execute(): "
189 << e.what()
190 << ". The Phase-I jFEX threshold table "
191 "cannot be built. Ensure TrigConf::xAODConfigSvc has "
192 "loaded the L1 menu for this input file.");
193 return StatusCode::FAILURE;
194 }
195
196 if (m_thresholdNamesLoaded && l1menu->name() == m_cachedL1MenuName) {
197 return StatusCode::SUCCESS;
198 }
200 ANA_MSG_INFO("L1 menu changed from '"
201 << m_cachedL1MenuName << "' to '" << l1menu->name()
202 << "' — rebuilding jFEX threshold table");
203 }
204 const auto& thresholds = l1menu->thresholds(m_l1ThresholdType.value());
205 if (thresholds.empty()) {
206 ANA_MSG_ERROR("L1 menu '" << l1menu->name()
207 << "' has no thresholds of type '"
208 << m_l1ThresholdType.value() << "'");
209 return StatusCode::FAILURE;
210 }
211 m_jfexThresholdNames.clear();
212 for (const auto& thr : thresholds) {
213 const unsigned int bit = thr->mapping();
214 if (bit >= m_jfexThresholdNames.size())
215 m_jfexThresholdNames.resize(bit + 1);
216 m_jfexThresholdNames[bit] = thr->name();
217 }
218 ANA_MSG_INFO("Loaded " << m_jfexThresholdNames.size()
219 << " jFEX threshold names from L1 menu '"
220 << l1menu->name() << "'");
221 m_cachedL1MenuName = l1menu->name();
223 return StatusCode::SUCCESS;
224 }
225
226 StatusCode JetTriggerDecoratorAlg::execute(const EventContext& ctx) {
229 if (m_doL1Matching) {
230 if (m_usePhaseIL1) {
232 l1JetsPhaseI = SG::makeHandle(m_L1JetsPhaseIInKey, ctx);
233 ANA_CHECK(l1JetsPhaseI.isValid());
234 } else {
235 l1Jets = SG::makeHandle(m_L1JetsInKey, ctx);
236 ANA_CHECK(l1Jets.isValid());
237 }
238 }
240 if (m_doHLTMatching) {
242 ANA_MSG_DEBUG(m_trigger << " isPassed "
243 << m_emulationTool->isPassed(m_trigger));
244 else {
245 hltJetsFromCont = SG::makeHandle(m_HLTJetsInKey, ctx);
246 ANA_CHECK(hltJetsFromCont.isValid());
247 }
248 }
249
252 // prepare Run2 emulation results
253 std::unordered_map<std::string,
254 std::vector<std::pair<const xAOD::Jet*, bool>>>
255 emulatedJets = {};
257 emulatedJets = m_emulationTool->getEmulatedJets(m_trigger);
258 bool isTrigPassed = m_trigDecisionTool->isPassed(m_trigger);
259 const TrigConf::HLTChain* hltChain =
260 m_trigDecisionTool->ExperimentalAndExpertMethods()
261 .getChainConfigurationDetails(m_trigger);
262 const std::string& l1Name = hltChain->lower_chain_name();
263 const std::vector<L1LegToken> l1LegTokens = parseL1LegTokens(l1Name);
264
265 for (const auto& sys : m_systematicsList.systematicsVector()) {
266 const xAOD::JetContainer* jets = nullptr;
267 ANA_CHECK(m_jetsHandle.retrieve(jets, sys, ctx));
268
269 for (const xAOD::Jet* jet : *jets) {
273
274 if (m_doL1Matching) {
275 float l1Et = -99.f;
276 float l1Eta = -99.f;
277 float l1Phi = -99.f;
278 float minDRL1 = m_l1dR.value();
279 std::vector<int> l1ThresholdsVec;
280
281 if (isTrigPassed) {
282 if (m_usePhaseIL1) {
283 const auto& jfexNames = m_jfexThresholdNames;
284 std::tie(l1Et, l1Eta, l1Phi, minDRL1, l1ThresholdsVec) =
285 matchL1Container<xAOD::jFexSRJetRoI>(
286 jet, *l1JetsPhaseI,
287 [&jfexNames](const xAOD::jFexSRJetRoI* r) {
288 return getL1JetThresholds(r, jfexNames);
289 },
290 l1LegTokens, m_l1dR.value());
291 } else {
292 // Legacy L1Calo path: use JetRoI::thrNames().
293 std::tie(l1Et, l1Eta, l1Phi, minDRL1, l1ThresholdsVec) =
294 matchL1Container<xAOD::JetRoI>(
295 jet, *l1Jets,
296 [](const xAOD::JetRoI* r) {
297 return getL1JetThresholds(r);
298 },
299 l1LegTokens, m_l1dR.value());
300 }
301 }
302
303 m_L1Et_decor.set(*jet, l1Et, sys);
304 m_L1Eta_decor.set(*jet, l1Eta, sys);
305 m_L1Phi_decor.set(*jet, l1Phi, sys);
306 m_L1DR_decor.set(*jet, minDRL1, sys);
307 m_L1Threshold_decor.set(*jet, l1ThresholdsVec, sys);
308 } // end L1 matching
309
313
314 if (m_doHLTMatching) {
315 const xAOD::IParticle* bestHLT = nullptr;
316 float minDRHLT = m_hltDR.value();
317 std::set<int> HLTThresholds = {};
318
319 if (isTrigPassed) {
320 int ileg = 0;
321 for (const ChainNameParser::LegInfo& legInfo :
323 if (legInfo.signature == "j") {
324 ANA_MSG_VERBOSE(" Leg" << ileg << ": "
325 << " " << legInfo.legName() << " "
326 << legInfo.type() << " "
327 << legInfo.signature << " "
328 << legInfo.threshold);
329
330 int legThreshold = legInfo.threshold;
331
332 if (legInfo.legName().find("gsc") != std::string::npos) {
333 for (const std::string& part : legInfo.legParts) {
334 if (part.find("gsc") != std::string::npos) {
335 legThreshold = std::stoi(part.substr(3));
336 ATH_MSG_DEBUG("GSC leg found. Using threshold "
337 << legThreshold);
338 break;
339 }
340 }
341 }
342
346
347 if (m_useEmulationTool) {
348 auto hlt_emulated_jets =
349 emulatedJets[legInfo.legName()]; // use pre-fetched
350 // emulation results
351 ANA_MSG_DEBUG(" Emulated jets for "
352 << legInfo.legName() << ": "
353 << hlt_emulated_jets.size());
354
355 for (const auto& [hlt_jet, passBtag] : hlt_emulated_jets) {
356 float dR = jet->p4().DeltaR(hlt_jet->p4());
357 ANA_MSG_VERBOSE(" pt: " << hlt_jet->pt()
358 << " eta: " << hlt_jet->eta()
359 << " phi: " << hlt_jet->phi()
360 << " dR: " << dR);
361
362 if (bestHLT && isSameJet(bestHLT, hlt_jet))
363 HLTThresholds.insert(legThreshold);
364 else if (dR < minDRHLT) {
365 minDRHLT = dR;
366 bestHLT = hlt_jet;
367 HLTThresholds.clear();
368 HLTThresholds.insert(legThreshold);
369 }
370 }
371 }
372
376
377 else {
378 frd.setRestrictRequestToLeg(ileg);
379 auto hlt_jetsFromtrigDec =
381 frd);
382 std::vector<const xAOD::IParticle*> allHLTJets;
383
384 for (const auto& hlt_jet_link : hlt_jetsFromtrigDec) {
385 const xAOD::IParticle* hlt_jetFromtrigDec =
386 *hlt_jet_link.link;
387 if (!hlt_jetFromtrigDec)
388 continue;
389 allHLTJets.push_back(hlt_jetFromtrigDec);
390 }
391
392 // Start adding missing HLT jets -- only for buggy triggers
393 if (std::find(m_triggerNavBug.begin(), m_triggerNavBug.end(),
394 m_trigger.value()) != m_triggerNavBug.end()) {
395 for (const xAOD::Jet* jetFromCont : *hltJetsFromCont) {
396 bool alreadyIn = false;
397 for (const xAOD::IParticle* seenJet : allHLTJets) {
398 if (isSameJet(seenJet, jetFromCont)) {
399 alreadyIn = true;
400 break;
401 }
402 }
403 if (alreadyIn)
404 continue;
405
406 allHLTJets.push_back(jetFromCont);
407 ANA_MSG_DEBUG("Added missing HLT jet from container: pt="
408 << jetFromCont->pt()
409 << " eta=" << jetFromCont->eta()
410 << " phi=" << jetFromCont->phi());
411 }
412 }
413
414 for (const xAOD::IParticle* hlt_jet : allHLTJets) {
415 float dR = jet->p4().DeltaR(hlt_jet->p4());
416 bool fromtrigDec = false;
417 for (const auto& hlt_jet_link : hlt_jetsFromtrigDec) {
418 if (*hlt_jet_link.link == hlt_jet) {
419 fromtrigDec = true;
420 break;
421 }
422 }
423
425 " pt: " << hlt_jet->pt() << " eta: " << hlt_jet->eta()
426 << " phi: " << hlt_jet->phi() << " dR: " << dR
427 << " (fromContainer=" << !fromtrigDec << ")");
428
429 if (bestHLT && isSameJet(bestHLT, hlt_jet))
430 HLTThresholds.insert(legThreshold);
431 else if (dR < minDRHLT) {
432 minDRHLT = dR;
433 bestHLT = hlt_jet;
434 HLTThresholds.clear();
435 HLTThresholds.insert(legThreshold);
436 }
437 } // Loop over allHLTJets
438 } // end Run 3 access
439 } // end HLT matching
440
441 ileg++;
442 }
443 }
444
445 m_HLTPt_decor.set(*jet, bestHLT ? bestHLT->pt() : -99., sys);
446 m_HLTEta_decor.set(*jet, bestHLT ? bestHLT->eta() : -99., sys);
447 m_HLTPhi_decor.set(*jet, bestHLT ? bestHLT->phi() : -99., sys);
448 m_HLTDR_decor.set(*jet, minDRHLT, sys);
449
450 std::vector<int> hltThresh;
451 if (bestHLT)
452 hltThresh = std::vector<int>(HLTThresholds.begin(),
453 HLTThresholds.end());
454 m_HLTThreshold_decor.set(*jet, hltThresh, sys);
455
456 ANA_MSG_VERBOSE("Summary "
457 << " Trigger: " << m_trigger << " bestHLT pT: "
458 << (bestHLT ? bestHLT->pt() : -99.));
459 }
460 }
461 };
462 return StatusCode::SUCCESS;
463 }
464
466 {
467 // Need this function because jet1 == jet2 would return false when comparing b-jet to untagged jet
468 return (jet1->p4().DeltaR(jet2->p4()) < 0.01) && (std::abs(jet1->pt() - jet2->pt()) < 100);
469 }
470
471}
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_MSG_DEBUG(x)
Helper class to provide constant type-safe access to aux data.
#define ANA_MSG_INFO(xmsg)
Macro printing info messages.
#define ANA_MSG_ERROR(xmsg)
Macro printing error messages.
#define ANA_MSG_VERBOSE(xmsg)
Macro printing verbose messages.
#define ANA_MSG_DEBUG(xmsg)
Macro printing debug messages.
#define ANA_CHECK(EXP)
check whether the given expression was successful
bool passed(DecisionID id, const DecisionIDContainer &)
checks if required decision ID is in the set of IDs in the container
static Double_t ss
static const Attributes_t empty
SysListHandle m_systematicsList
the systematics list we run
ToolHandle< TrigConf::ITrigConfigTool > m_trigConfigTool
Gaudi::Property< bool > m_doHLTMatching
Gaudi::Property< float > m_l1dR
JetTriggerDecoratorAlg(const std::string &name, ISvcLocator *svcLoc=nullptr)
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
Gaudi::Property< float > m_hltDR
CP::SysWriteDecorHandle< float > m_L1DR_decor
StatusCode rebuildJfexThresholdTable(const EventContext &ctx)
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
std::vector< std::string > m_jfexThresholdNames
CP::SysWriteDecorHandle< float > m_HLTDR_decor
CP::SysWriteDecorHandle< float > m_L1Et_decor
SG::ReadHandleKey< xAOD::JetRoIContainer > m_L1JetsInKey
Gaudi::Property< std::string > m_l1ThresholdType
Gaudi::Property< bool > m_doL1Matching
CP::SysWriteDecorHandle< float > m_L1Phi_decor
Helper class that provides access to information about individual legs.
AnaAlgorithm(const std::string &name, ISvcLocator *pSvcLocator)
constructor with parameters
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
L1 menu configuration.
Definition L1Menu.h:29
FeatureRequestDescriptor & setChainGroup(const std::string &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.
This module defines the arguments passed from the BATCH driver to the BATCH worker.
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.
Struct containing information on each leg of a chain.