ATLAS Offline Software
Loading...
Searching...
No Matches
Run2ToRun3TrigNavConverterV2.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include <boost/functional/hash.hpp>
6#include <GaudiKernel/StatusCode.h>
7#include "AthLinks/ElementLinkVector.h"
16#include "SpecialCases.h"
17#include <limits>
18#include <cstdint>
19
20namespace TCU = TrigCompositeUtils;
21
22// helper class
24{
25 teIDs.push_back(te->getId());
26}
27
28// find if proxy is a child of other proxies, also follows to children of the children etc...
29bool ConvProxy::isChild(const ConvProxy* other ) const {
30 for ( auto c: children ) {
31 if (other == c)
32 return true;
33 if ( c->isChild(other) )
34 return true;
35 }
36 return false;
37}
38
39bool ConvProxy::isParent(const ConvProxy* other ) const {
40 for ( auto c: parents ) {
41 if (other == c)
42 return true;
43 if ( c->isParent(other) )
44 return true;
45 }
46 return false;
47}
48
49
50bool ConvProxy::mergeAllowed(const ConvProxy *other) const
51{
52 if (this == other)
53 return false; // no merging with self
54 // never merge children with parents
55 if ( isChild(other) )
56 return false;
57 if ( isParent(other) )
58 return false;
59 return true;
60}
61
63{
64 if (other == this)
65 {
66 return;
67 }
68 // copy over chains
69 runChains.insert(other->runChains.begin(), other->runChains.end());
70 passChains.insert(other->passChains.begin(), other->passChains.end());
71 teIDs.push_back(other->te->getId());
72 /* the intention of the code below is following.
73 Intial structure like is like this (the line is always bidirectional):
74 P1 P2 P3 <- parents
75 | | /
76 T1 T2 <- "this" and "other"
77 | |
78 C1 C2 <- children
79 1) Lets assume that the first proxies we treat are B1 & B2 ath they are mergable. The resulting structure should look like this:
80 P1 P2 P3
81 |/__/
82 T1 T2
83 |\
84 C1 C2
85
86 */
87 auto add = [](ConvProxy *toadd, std::set<ConvProxy *> &coll)
88 {
89 if (std::find(coll.begin(), coll.end(), toadd) == coll.end())
90 {
91 coll.insert(toadd);
92 }
93 };
94
95 auto remove = [](ConvProxy *torem, std::set<ConvProxy *> &coll)
96 {
97 auto place = std::find(coll.begin(), coll.end(), torem);
98 if (place != coll.end())
99 {
100 coll.erase(place);
101 }
102 };
103
104 // this is T <-> C connection
105 for (auto otherChild : other->children)
106 {
107 add(otherChild, children);
108 add(this, otherChild->parents);
109 }
110 // this is T <-> P connection rewiring
111 for (auto otherParent : other->parents)
112 {
113 add(otherParent, parents);
114 add(this, otherParent->children);
115 }
116
117 // now need to remove links back to the "other"
118 for (auto otherParent : other->parents)
119 {
120 remove(other, otherParent->children);
121 }
122
123 for (auto otherChild : other->children)
124 {
125 remove(other, otherChild->parents);
126 }
127 other->children.clear();
128 other->parents.clear();
129}
130
131std::string ConvProxy::description() const
132{
133 std::string ret;
134 ret += " N parents: " + std::to_string(parents.size());
135 ret += " N children: " + std::to_string(children.size());
136 std::ostringstream os;
137 for ( auto c: children )
138 os << c << " ";
139 ret += " ptrs: " + os.str();
140 ret += " feaHash: " + std::to_string(feaHash);
141 ret += " N run chains: " + std::to_string(runChains.size());
142 return ret;
143}
144
145// the algorithm
146Run2ToRun3TrigNavConverterV2::Run2ToRun3TrigNavConverterV2(const std::string &name, ISvcLocator *pSvcLocator) : AthReentrantAlgorithm(name, pSvcLocator)
147{
148}
149
153
155{
156 ATH_CHECK(m_trigOutputNavKey.initialize());
157 ATH_CHECK(m_tdt.empty() != m_trigNavKey.key().empty()); // either of the two has to be enabled but not both
158 if (!m_tdt.empty())
159 {
160 ATH_CHECK(m_tdt.retrieve());
161 ATH_MSG_INFO("Will use Trigger Navigation from TrigDecisionTool");
162 }
163 else
164 {
166 ATH_MSG_INFO("Will use Trigger Navigation decoded from TrigNavigation object");
167 }
168
169 if (!m_chainsToSave.empty()) {
170 ATH_MSG_DEBUG("Will only save features for these chains " << m_chainsToSave);
171 }
172
173 ATH_CHECK(m_configSvc.retrieve());
174 ATH_CHECK(m_clidSvc.retrieve());
175
176 // configured collections can be either just type name, or type#key
177 // decoding takes this into account, if only the type is configured then empty string is places in the decoded lookup map
178 // else CLID + a name is placed
179
180 for (const auto &name : m_collectionsToSave)
181 {
182 std::string typeName = name;
183 std::string collName;
184 size_t delimeterIndex = name.find('#');
185 if (delimeterIndex != std::string::npos)
186 {
187 typeName = name.substr(0, delimeterIndex);
188 collName = name.substr(delimeterIndex + 1);
189 }
190 CLID id{0};
191 ATH_CHECK(m_clidSvc->getIDOfTypeName(typeName, id));
192 ATH_MSG_DEBUG("Will be linking collection type " << typeName << " name (empty==all) " << collName);
193 if ( collName.empty() )
194 m_collectionsToSaveDecoded[id]; // creates empty set
195 else
196 m_collectionsToSaveDecoded[id].insert(std::move(collName));
197 }
198
199 for (const auto &name : m_roisToSave)
200 {
201 m_setRoiName.push_back(name);
202 }
203
204 // sanity check, i.e. if there is at least one entry w/o the coll name no other entries are needed for a given clid
205 for (const auto & [clid, keysSet] : m_collectionsToSaveDecoded)
206 {
207 if (!keysSet.empty() and keysSet.contains(""))
208 {
209 ATH_MSG_ERROR("Bad configuration for CLID " << clid << " requested saving of all (empty coll name configures) collections, yet there are also specific keys");
210 return StatusCode::FAILURE;
211 }
212
213 }
214
215 bool anyChainBad=false;
216 for ( const auto & chain: m_chainsToSave ) {
217 if ( chain.contains('*') or chain.contains('|')) {
218 ATH_MSG_ERROR("Supplied chain name: " << chain << " contains wildcard characters, this is not supported by the conversion tool");
219 anyChainBad=true;
220 }
221 }
222 if ( anyChainBad ) {
223 ATH_MSG_ERROR("Supplied chain names contain wildcard characters, this is not supported by the conversion tool");
224 return StatusCode::FAILURE;
225 }
226 if ( m_chainsToSave.empty() ) {
227 ATH_MSG_INFO("No chains list supplied, the conversion will occur for all chains");
228 }
229
230 ATH_CHECK(m_clidSvc->getIDOfTypeName("TrigRoiDescriptor", m_roIDescriptorCLID));
231 ATH_CHECK(m_clidSvc->getIDOfTypeName("TrigRoiDescriptorCollection", m_roIDescriptorCollectionCLID));
232 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::TrigRingerRings", m_TrigRingerRingsCLID));
233 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::TrigRingerRingsContainer", m_TrigRingerRingsContainerCLID));
234 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::TrigEMCluster", m_TrigEMClusterCLID));
235 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::TrigEMClusterContainer", m_TrigEMClusterContainerCLID));
236 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::CaloCluster", m_CaloClusterCLID));
237 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::CaloClusterContainer", m_CaloClusterContainerCLID));
238 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::TrackParticleContainer", m_TrackParticleContainerCLID));
239 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::TauTrackContainer", m_TauTrackContainerCLID));
240 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::ElectronContainer", m_ElectronContainerCLID));
241 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::PhotonContainer", m_PhotonContainerCLID));
242 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::MuonContainer", m_MuonContainerCLID));
243 ATH_CHECK(m_clidSvc->getIDOfTypeName("xAOD::TauJetContainer", m_TauJetContainerCLID));
244
245 return StatusCode::SUCCESS;
246}
247
249{
250 return StatusCode::SUCCESS;
251}
252
253StatusCode Run2ToRun3TrigNavConverterV2::execute(const EventContext &context) const
254{
255 {
256 // configuration reading could not be done before the event loop
257 // it needs to be done only once though
258 std::scoped_lock lock(m_configUpdateMutex);
259 if (m_allTEIdsToChains.empty())
261 }
262
263 ConvProxySet_t convProxies;
264 HLT::StandaloneNavigation standaloneNav; // needed to keep TEs around, so it is out of the scope where it is filled and referenced
265 const HLT::TrigNavStructure *run2NavigationPtr = nullptr;
266 if (!m_trigNavKey.key().empty())
267 {
268 SG::ReadHandle navReadHandle(m_trigNavKey, context);
269 ATH_CHECK(navReadHandle.isValid());
270 bool ok = standaloneNav.deserialize(navReadHandle->serialized());
271 if (not ok)[[unlikely]]{
272 ATH_MSG_ERROR("deserialize returned false");
273 return StatusCode::FAILURE;
274 }
275 run2NavigationPtr = &standaloneNav;
276 }
277 else
278 {
279 run2NavigationPtr = m_tdt->ExperimentalAndExpertMethods().getNavigation();
280 }
281
282 ATH_CHECK(mirrorTEsStructure(convProxies, *run2NavigationPtr));
283 // printProxies(convProxies, [](auto ){ return true;},
284 // {m_chainIdsPrinter});
285
287 ATH_CHECK(allProxiesConnected(convProxies));
288
290 // printProxies(convProxies, [](auto ){ return true;},
291 // {m_chainIdsPrinter});
292
294 ATH_MSG_DEBUG("Proxies to chains mapping done");
295
297
298 if (not m_chainsToSave.empty())
299 {
301 printProxies(convProxies, [](auto ){ return true;},
303 ATH_MSG_DEBUG("Removed proxies to chains that are not converted, remaining number of elements " << convProxies.size());
304 }
306 {
307 ATH_CHECK(allProxiesHaveChain(convProxies));
308 }
309 if (m_doCompression)
310 {
311 ATH_CHECK(doCompression(convProxies, *run2NavigationPtr));
312 // printProxies(convProxies, [](auto ){ return true;},
313 // {m_chainIdsPrinter});
314
315 }
316
318 auto decisionOutput = outputNavigation.ptr();
319 TrigCompositeUtils::newDecisionIn(decisionOutput, TCU::summaryPassNodeName()); // we rely on the fact that the 1st element is the top
320
322 {
323 ATH_CHECK(fillRelevantFeatures(convProxies, *run2NavigationPtr));
324 ATH_CHECK(fillRelevantRois(convProxies, *run2NavigationPtr));
325 ATH_CHECK(fillRelevantTracks(convProxies));
326 ATH_MSG_DEBUG("Features to link found");
327 }
328
329 ATH_CHECK(createIMHNodes(convProxies, *decisionOutput, context));
331 {
333 }
334
335 ATH_CHECK(createL1Nodes(convProxies, *decisionOutput, context));
336 ATH_CHECK(linkFeaNode(convProxies, *decisionOutput, *run2NavigationPtr, context));
337 ATH_CHECK(linkRoiNode(convProxies, *run2NavigationPtr));
338 ATH_CHECK(linkTrkNode(convProxies, *run2NavigationPtr));
339 ATH_CHECK(createSFNodes(convProxies, *decisionOutput, m_finalTEIdsToChains, context));
340 ATH_CHECK(updateTerminusNode(*decisionOutput, context));
341 ATH_MSG_DEBUG("Conversion done, from " << convProxies.size() << " elements to " << decisionOutput->size() << " elements");
342
343 printProxies(convProxies, [](auto ){ return true;},
345 ATH_MSG_DEBUG("Resulting nodes");
346 size_t index = 0;
347 for ( auto o: *decisionOutput) {
348 ATH_MSG_DEBUG("Index: " << index << " " << *o);
349 index++;
350 }
351
352 // dispose temporaries
353 for (auto proxy : convProxies)
354 {
355 delete proxy;
356 }
357
358 return StatusCode::SUCCESS;
359}
360
362 // chains of configs structure
363 // A B
364 // \/
365 // C
366 // where C is the output TE of sequence consuming A & B
367 // sometimes there are an additional leafs
368 // C
369 // |
370 // D
371 if ( not std::regex_match(ptrChain->name(), SpecialCases::isTopo) ) return 0;
372 size_t stepToConsider = 0;
373 const size_t sigsSize = ptrChain->signatures().size();
374 if ( sigsSize < 2 ) return 0;
375 for ( size_t step = sigsSize-1; step > 1; step --) {
376 if ( (ptrChain->signatures()[step-1])->outputTEs().size() == 2 and (ptrChain->signatures()[step])->outputTEs().size() == 1 ) {
377 stepToConsider = step;
378 break;
379 }
380 }
381 if ( stepToConsider == 0 ) return 0; // not a topo
382
383 //counting is right, need to see now if TEs are connected
384 auto finalTE = (ptrChain->signatures()[stepToConsider])->outputTEs()[0];
385 auto preFinalTEs = (ptrChain->signatures()[stepToConsider-1])->outputTEs();
386
387 auto finalSeq = m_configSvc->sequences().getSequence(finalTE->id());
388 std::set<HLT::te_id_type> tesInSeq;
389 std::set<HLT::te_id_type> tesInChain;
390
391 for ( auto te: finalSeq->inputTEs()) {
392 tesInSeq.insert(te->id());
393 }
394
395 for ( auto te: preFinalTEs) {
396 tesInChain.insert(te->id());
397 }
398
399 if (tesInSeq == tesInChain) {
400 return stepToConsider;
401 }
402 return 0;
403}
404
406{
407
408 ATH_CHECK(not m_configSvc->chains().empty());
409
410 // obtain map output TE -> input TE via sequences
411 for (auto ptrChain : m_configSvc->chains())
412 {
413 std::string chainName = ptrChain->name();
414
415 if (not m_chainsToSave.empty())
416 {
417 auto found = std::find(m_chainsToSave.begin(), m_chainsToSave.end(), chainName);
418 if (found == m_chainsToSave.end())
419 {
420 continue;
421 }
422 }
423
424 if (std::regex_match(chainName, SpecialCases::bjetMuChain )) {
425 ATH_CHECK(bjetMuChainConfigDecoder(allTEs, finalTEs, ptrChain));
426 continue;
427 }
428
429 // hack for etcut chains
430 // if we ever need to generalise that it should be moved to separate function
431 std::map<HLT::te_id_type, HLT::te_id_type> etcutReplacementTEs;
432 auto etcutReplacement = [&etcutReplacementTEs](HLT::te_id_type in) { auto out = etcutReplacementTEs.find(in); return (out == etcutReplacementTEs.end() ? in : out->second ); };
433 if ( chainName.find("etcut") != std::string::npos ) {
434 std::set<size_t> positionsOfEtCutLegs;
435 // use heuristics to mention
436 if( std::regex_match(chainName, SpecialCases::egammaDiEtcut) ) {
437 ATH_MSG_DEBUG("EtCut chains hack, chain with two etcut legs ");
438 positionsOfEtCutLegs.insert({0, 1});
439 } else if ( std::regex_match(chainName, SpecialCases::egammaCombinedWithEtcut) ) {
440 ATH_MSG_DEBUG("EtCut chains hack, egamma chain with second etcut leg ");
441 positionsOfEtCutLegs.insert({1});
442 } else if ( std::regex_match(chainName, SpecialCases::egammaEtcut) ) {
443 ATH_MSG_DEBUG("EtCut chains hack, single leg egamma chain");
444 positionsOfEtCutLegs.insert({0});
445 }
446
447 // pilot pass to fill the replacement map
448 std::map<size_t, HLT::te_id_type> positionToDesiredIDmap;
449 for (auto ptrHLTSignature : ptrChain->signatures()) {
450 size_t position = 0;
451 for (auto ptrHLTTE : ptrHLTSignature->outputTEs()) {
452 if (positionsOfEtCutLegs.count(position) and positionToDesiredIDmap.find(position) != positionToDesiredIDmap.end() ) {
453 etcutReplacementTEs[ptrHLTTE->id()] = positionToDesiredIDmap[position];
454 ATH_MSG_DEBUG("EtCut chains hack, TE " << ptrHLTTE->name() << " will be replaced by: " << TrigConf::HLTUtils::hash2string(positionToDesiredIDmap[position]));
455 } else {
456 if ( ptrHLTTE->name().find("calocalib") != std::string::npos and positionsOfEtCutLegs.count(position) ) { // we have final TE for this leg
457 positionToDesiredIDmap[position] = ptrHLTTE->id();
458 }
459 }
460 position++;
461 }
462 }
463 }
464
465 // chains with a single leg
466 HLT::Identifier chainId = HLT::Identifier(chainName);
467 ATH_MSG_DEBUG(" CHAIN name " << chainName << " CHAIN Id " << chainId);
468 for (auto ptrHLTSignature : ptrChain->signatures()) {
469 for (auto ptrHLTTE : ptrHLTSignature->outputTEs()) {
470 unsigned int teId = etcutReplacement(ptrHLTTE->id());
471 allTEs[teId].insert(chainId);
472 if (ptrHLTSignature == ptrChain->signatures().back()) {
473 finalTEs[teId].insert(chainId);
474 ATH_MSG_DEBUG("TE will be used to mark final chain decision " << ptrHLTTE->name() << " chain " << chainName );
475 }
476 }
477 }
478 // chains with a multiple legs
479 std::vector<int> multiplicities = ChainNameParser::multiplicities(chainName);
480
481 // dirty hacks for failing chains parsing
482 if(std::regex_match(chainName, SpecialCases::gammaXeChain))
483 multiplicities={1,1};
484
485 if ( multiplicities.size() > 1 ) {
486 ATH_MSG_DEBUG(" this " << (is2LegTopoChain(ptrChain) ? "is": "is not") << " topological chain");
487 // the chain structure (in terms of multiplicities) may change along the way
488 // we'll assign legs only to these TEs of the steps that have identical multiplicity pattern
489 // e.g. for the chain: HLT_2g25_loose_g20 the multiplicities are: [2, 1]
490
491 // hack for HLT.*tau.*xe.* case
492 if (std::regex_match(chainName, SpecialCases::tauXeChain)) {
493 // The Run-2 configuration lacks usable leg multiplicities for these
494 // chains. Set them to the chain-name structure: the TDT
495 // (Trig::ChainGroup, which follows getLegMultiplicities), the R3
496 // matching (which parses the chain name) and the leg assignment
497 // below must all agree on the leg structure, otherwise leg-filtered
498 // feature retrieval comes back empty. The xe leg keeps its place and
499 // simply owns no IParticle features.
500 std::vector<size_t> mult_hack(multiplicities.begin(), multiplicities.end());
501 ptrChain->set_leg_multiplicities(mult_hack); // HLTChain needs vector<size_t>
502 }
503
504 // hack for mu2MunoL1Special
505 if (std::regex_match(chainName, SpecialCases::mu2MunoL1Special)) {
506 std::vector<size_t> mult_hack;
507 if (multiplicities.size()==3) mult_hack={1,1};
508 else if (multiplicities.size()==2) mult_hack={2}; // HLT_mu11_nomucomb_2mu4noL1_nscan03_L1MU11_2MU6
509 ptrChain->set_leg_multiplicities(mult_hack);
510 multiplicities.assign(mult_hack.begin(), mult_hack.end());
511 }
512
513 ATH_MSG_DEBUG("CHAIN " << chainName << " needs legs: " << multiplicities );
514
515 if ( multiplicities.size() <= 1 ) {
516 ATH_MSG_DEBUG("Chain " << chainName << " has a single effective leg after special-case handling - no leg IDs assigned");
517 }
518 else {
519
520 // Build per-leg name tokens (e.g. "mu14", "tau35") from the chain name.
521 // The Run-2 menu does not guarantee that the TEs of a signature step are
522 // listed in the leg order of the chain name (e.g. for mu+tau chains the
523 // tau TEs come first), so a purely positional leg assignment can swap
524 // the legs and - via the chain-ID propagation - pool the features of
525 // different legs. Where possible the TE name, which carries the leg
526 // token, decides the leg number instead.
527 std::vector<std::string> legTokens;
528 bool legTokensUsable = true;
529 for (const ChainNameParser::LegInfo& legInfo : ChainNameParser::HLTChainInfo(chainName)) {
530 if (legInfo.signature.empty() || legInfo.threshold < 0) {
531 legTokensUsable = false;
532 break;
533 }
534 legTokens.push_back(legInfo.signature + std::to_string(legInfo.threshold));
535 }
536 if (legTokens.size() > multiplicities.size()) {
537 // special-case handling dropped trailing leg(s) (e.g. the xe leg)
538 legTokens.resize(multiplicities.size());
539 }
540 if (legTokens.size() != multiplicities.size()
541 || std::set<std::string>(legTokens.begin(), legTokens.end()).size() != legTokens.size()) {
542 // e.g. two legs with an identical token (HLT_mu13_mu13_idperf_Zmumu):
543 // fall back to the positional assignment
544 legTokensUsable = false;
545 }
546 ATH_MSG_DEBUG("Semantic leg tokens " << (legTokensUsable ? "usable" : "NOT usable") << ": " << legTokens);
547
548 // noL1 legs (e.g. mu8noL1, mu6noL1_nscan03) have no leg token in their
549 // TE names: the leg is implemented as a full-scan reconstruction
550 // (EF_*_FS*/FSHypo/FStracks TEs) plus a joint "MultiComb" multiplicity
551 // hypo TE which decides ALL thresholds at once. Identify such a leg so
552 // full-scan TEs can be routed to it by name pattern.
553 int noL1Leg = -1;
554 int legIndex = 0;
555 for (const ChainNameParser::LegInfo& legInfo : ChainNameParser::HLTChainInfo(chainName)) {
556 if (legInfo.legName().find("noL1") != std::string::npos) {
557 noL1Leg = (noL1Leg == -1) ? legIndex : -2; // -2: more than one noL1 leg -> rule unusable
558 }
559 ++legIndex;
560 }
561 if (noL1Leg == -2) noL1Leg = -1; // more than one noL1 leg: the rule is unusable
562
563 // Semantic leg for a single TE name:
564 // >= 0 : leg number
565 // -1 : unknown (positional fallback in pattern-matched steps)
566 // -2 : joint/combined decision TE (gets NO leg ID)
567 const size_t nLegs = multiplicities.size();
568 auto semanticLeg = [&legTokens, legTokensUsable, noL1Leg, nLegs](const std::string& teName) -> int {
569 if (teName.find("MultiComb") != std::string::npos)
570 return -2; // joint multiplicity hypo of a noL1 chain: chain-level, not a leg
571 if (legTokensUsable) {
572 int found = -1;
573 for (size_t l = 0; l < legTokens.size(); ++l) {
574 if (teName.find(legTokens[l]) != std::string::npos) {
575 if (found != -1) { found = -1; break; } // ambiguous TE name
576 found = static_cast<int>(l);
577 }
578 }
579 if (found >= 0) return found;
580 }
581 if (noL1Leg >= 0) {
582 if (teName.find("_FS") != std::string::npos || teName.find("FSHypo") != std::string::npos
583 || teName.find("FStracks") != std::string::npos) {
584 return noL1Leg; // full-scan reconstruction TE -> the noL1 leg
585 }
586 if (nLegs == 2) {
587 return 1 - noL1Leg; // two legs, one noL1: everything else is the RoI-seeded leg
588 }
589 }
590 return -1;
591 };
592
593 // Decide the leg number for each TE of a pattern-matched step: semantic
594 // assignment first, remaining TEs distributed over the remaining leg
595 // numbers in positional order (the previous behaviour).
596 auto assignLegNumbers = [&semanticLeg, nLegs](const std::vector<std::string>& teNames) {
597 const size_t n = teNames.size();
598 std::vector<int> legOf(n, -1);
599 std::vector<bool> legUsed(nLegs, false); // leg numbers range over the CHAIN's legs,
600 // not over the TEs of this step (they can
601 // differ, e.g. for the tau+xe special case)
602 for (size_t pos = 0; pos < n; ++pos) {
603 const int l = semanticLeg(teNames[pos]);
604 if (l >= 0 && l < static_cast<int>(nLegs) && !legUsed[l]) {
605 legOf[pos] = l;
606 legUsed[l] = true;
607 }
608 else if (l == -2) {
609 legOf[pos] = -2;
610 }
611 }
612 size_t nextFree = 0;
613 for (size_t pos = 0; pos < n; ++pos) {
614 if (legOf[pos] != -1) continue;
615 while (nextFree < nLegs && legUsed[nextFree]) ++nextFree;
616 if (nextFree >= nLegs) break;
617 legOf[pos] = static_cast<int>(nextFree);
618 legUsed[nextFree] = true;
619 }
620 return legOf;
621 };
622
623 // The terminus of each leg (for the SF nodes / per-leg retrieval
624 // anchors) is the LAST TE assigned to that leg over the signature steps.
625 std::map<int, unsigned int> lastTEofLeg;
626
627 for (auto ptrHLTSignature : ptrChain->signatures())
628 {
629 std::vector<int> teCounts;
630 std::vector<unsigned int> teIds;
631 std::vector<std::string> teNames;
632 unsigned int lastSeenId = 0;
633 for (auto ptrHLTTE : ptrHLTSignature->outputTEs())
634 {
635 if ( lastSeenId != ptrHLTTE->id()) {
636 teCounts.push_back(1);
637 teIds.push_back(ptrHLTTE->id());
638 teNames.push_back(ptrHLTTE->name());
639 } else {
640 teCounts.back()++;
641 }
642 lastSeenId = ptrHLTTE->id();
643 }
644
645 ATH_MSG_DEBUG("TE multiplicities seen in this step " << teCounts << " TEs: " << teNames);
646 bool multiplicityCounts = multiplicities == teCounts;
647 // hack for HLT.*tau.*xe.* case
648 if(std::regex_match(chainName, SpecialCases::tauXeChain)) multiplicityCounts = true;
649 if ( multiplicityCounts ) {
650 const std::vector<int> legOf = assignLegNumbers(teNames);
651 ATH_MSG_DEBUG("There is a match, will assign chain leg IDs to TEs " << teCounts << " " << teIds);
652 for ( size_t pos = 0; pos < teIds.size(); ++ pos){
653 if (legOf[pos] < 0) continue; // joint-decision TE: stays chain-level
654 HLT::Identifier chainLegId = TrigCompositeUtils::createLegName(chainId, legOf[pos]);
655 ATH_MSG_DEBUG(" TE " << teNames[pos] << " -> " << chainLegId);
656 allTEs[etcutReplacement(teIds[pos])].insert(chainLegId);
657 lastTEofLeg[legOf[pos]] = teIds[pos];
658 }
659 }
660 else {
661 // Steps whose TE pattern does not match the leg multiplicities
662 // (e.g. the interleaved single-TE steps of noL1 chains) still get
663 // a leg ID when the TE name identifies the leg unambiguously.
664 for ( size_t pos = 0; pos < teIds.size(); ++ pos){
665 const int leg = semanticLeg(teNames[pos]);
666 if (leg < 0) continue;
667 HLT::Identifier chainLegId = TrigCompositeUtils::createLegName(chainId, leg);
668 ATH_MSG_DEBUG(" (irregular step) TE " << teNames[pos] << " -> " << chainLegId);
669 allTEs[etcutReplacement(teIds[pos])].insert(chainLegId);
670 lastTEofLeg[leg] = teIds[pos];
671 }
672 }
673 }
674 for (const auto& [legNumber, teId] : lastTEofLeg) {
675 HLT::Identifier chainLegId = TrigCompositeUtils::createLegName(chainId, legNumber);
676 ATH_MSG_DEBUG("created leg id " << chainLegId << " that will replace TE ID " << etcutReplacement(teId));
677 finalTEs[etcutReplacement(teId)].insert(chainLegId);
678 }
679 } // end of the multi-effective-leg assignment
680 }
681 }
682 ATH_MSG_DEBUG("Recognised " << allTEs.size() << " kinds of TEs and among them " << finalTEs.size() << " final types");
683 return StatusCode::SUCCESS;
684}
685
687 HLT::Identifier chainId = HLT::Identifier(ptrChain->name());
688
689 std::vector<unsigned int> muons;
690 std::vector<unsigned int> jets;
691 bool switchedTojets =false;
692 for (auto ptrHLTSignature : ptrChain->signatures()) {
693 for (auto ptrHLTTE : ptrHLTSignature->outputTEs()) {
694 if ( ptrHLTTE->name().find("_mu") == std::string::npos ) {
695 switchedTojets = true;
696 }
697
698 if ( switchedTojets)
699 jets.push_back(ptrHLTTE->id());
700 else
701 muons.push_back(ptrHLTTE->id());
702 }
703 }
704 ATH_CHECK(not muons.empty());
705 ATH_CHECK(not jets.empty());
706 std::reverse(std::begin(muons), std::end(muons));
707 std::reverse(std::begin(jets), std::end(jets));
708 finalTEs[muons[0]].insert(TrigCompositeUtils::createLegName(chainId, 0));
709 finalTEs[muons[0]].insert(chainId);
710 finalTEs[jets[0]].insert(TrigCompositeUtils::createLegName(chainId, 1));
711 finalTEs[jets[0]].insert(chainId);
712
713 for ( size_t index = 0; index < std::min(muons.size(), jets.size()); ++index)
714 {
715 allTEs[muons[index]].insert(TrigCompositeUtils::createLegName(chainId, 0));
716 allTEs[muons[index]].insert(chainId);
717 allTEs[jets[index]].insert(TrigCompositeUtils::createLegName(chainId, 1));
718 allTEs[jets[index]].insert(chainId);
719 }
720 return StatusCode::SUCCESS;
721}
722
723
724
726{
727
728 // iterate over the TEs, for each make the ConvProxy and build connections
729 std::map<const HLT::TriggerElement *, ConvProxy *> teToProxy;
730 ATH_MSG_DEBUG("TrigNavStructure with " << run2Nav.getAllTEs().size() << " TEs acquired");
731 for (auto te : run2Nav.getAllTEs())
732 {
733 // skip event seed node
735 continue;
736 auto proxy = new ConvProxy(te);
737 convProxies.insert(proxy);
738 teToProxy[te] = proxy;
739 // add linking
740 for (auto predecessor : HLT::TrigNavStructure::getDirectPredecessors(te))
741 {
742 ConvProxy *predecessorProxy = teToProxy[predecessor];
743 if (predecessorProxy != nullptr)
744 { // because we skip some
745 proxy->parents.insert(predecessorProxy);
746 predecessorProxy->children.insert(proxy);
747 }
748 }
749 }
750
752 {
753 int counter = -1;
754 for (auto proxy : convProxies)
755 {
756 counter++;
757 ATH_MSG_DEBUG("Proxy " << counter << " " << proxy->description() << "ptr " << proxy);
758 for (auto p : proxy->children)
759 ATH_MSG_DEBUG("Child ptr " << p);
760 for (auto p : proxy->parents)
761 ATH_MSG_DEBUG("Parent ptr " << p);
762
763 for (auto p : proxy->parents)
764 {
765 for (auto pp : p->parents)
766 {
767 if (pp == proxy)
768 {
769 ATH_MSG_WARNING("Weird, proxy is in parents list of parents");
770 }
771 }
772 }
773 for (auto c : proxy->children)
774 {
775 for (auto cc : c->children)
776 {
777 if (cc == proxy)
778 {
779 ATH_MSG_WARNING("Weird, proxy is in children list of children");
780 }
781 }
782 }
783 }
784 }
785
786 ATH_MSG_DEBUG("Created " << convProxies.size() << " proxy objects");
787 return StatusCode::SUCCESS;
788}
789
790
792 std::function<bool(const ConvProxy*)> selector,
793 const std::vector<std::function<void(const ConvProxy*)>>& printers) const {
794 ATH_MSG_DEBUG("Printing proxies");
795 ATH_MSG_DEBUG("" );
796 for ( auto p: proxies) {
797 if ( selector(p) ){
798 ATH_MSG_DEBUG("Proxy " << p->description() );
799 for (auto& printer: printers) {
800 printer(p);
801 }
802 ATH_MSG_DEBUG("" );
803 }
804 }
805}
806
808{
809
810 for (auto &ptrConvProxy : convProxies)
811 {
812 auto teId = ptrConvProxy->te->getId();
813 bool teActive = ptrConvProxy->te->getActiveState();
814 auto iter = allTEs.find(teId);
815 if (iter != allTEs.end())
816 {
817 ptrConvProxy->runChains.insert(iter->second.begin(), iter->second.end());
818 if (teActive)
819 {
820 ptrConvProxy->passChains.insert(iter->second.begin(), iter->second.end());
821 }
822 }
823
824 for (auto &objTeIdToChain : allTEs)
825 {
826 if (teId == objTeIdToChain.first)
827 {
828 for (auto &chainId : objTeIdToChain.second)
829 {
830 (ptrConvProxy->runChains).insert(chainId);
831 }
832 break;
833 }
834 }
835 }
836 return StatusCode::SUCCESS;
837}
838
840{
841 // propagate up (towards L1) chain IDs if they are not in proxies
842 // technically each proxy looks at the children proxies and inserts from it all unseen chains
843 // procedure is repeated until, no single proxy needs an update (tedious - we may be smarter in future)
844
845 // Helper: insert the child's IDs into the given set. Across a multi-parent
846 // child (e.g. the combined TE of a multi-leg chain, which joins several
847 // branches) only chain-level IDs are propagated: propagating LEG IDs there
848 // would smear them onto all parent branches and pool the per-leg features.
849 auto insertFromChild = [](const ConvProxy& child, std::set<HLT::Identifier>& dest) {
850 if (child.parents.size() > 1)
851 {
852 for (const HLT::Identifier& id : child.runChains)
853 {
854 if (!TCU::isLegId(id))
855 dest.insert(id);
856 }
857 }
858 else
859 {
860 dest.insert(std::begin(child.runChains), std::end(child.runChains));
861 }
862 };
863
864 while (true)
865 {
866 size_t numberOfUpdates = 0;
867 for (auto p : convProxies)
868 {
869 for (auto child : p->children)
870 {
871 size_t startSize = p->runChains.size();
872 insertFromChild(*child, p->runChains);
873
874 if (startSize != p->runChains.size())
875 { // some chain needed to be inserted
876 numberOfUpdates++;
877 // if update was need, it means set of chains that passed need update as well
878 insertFromChild(*child, p->passChains);
879 }
880 }
881 }
882 ATH_MSG_DEBUG("Needed to propagate chains from " << numberOfUpdates << " child(ren)");
883 if (numberOfUpdates == 0)
884 {
885 break;
886 }
887 }
888 return StatusCode::SUCCESS;
889}
890
892{
893 // remove proxies that have no chains
894 for (auto i = std::begin(convProxies); i != std::end(convProxies);)
895 {
896 if ((*i)->runChains.empty())
897 {
898 ConvProxy *toDel = *i;
899 // remove it from parents/children
900 for (auto parent : toDel->parents)
901 {
902 parent->children.erase(toDel);
903 }
904 for (auto child : toDel->children)
905 {
906 child->parents.erase(toDel);
907 }
908 delete toDel;
909 i = convProxies.erase(i);
910 }
911 else
912 {
913 ++i;
914 }
915 }
916 ATH_MSG_DEBUG("After eliminating proxies not associated to chainsof intereset left with " << convProxies.size());
917 return StatusCode::SUCCESS;
918}
919
921{
922
923 ATH_CHECK(collapseFeaturesProxies(convProxies, run2Nav));
926 {
927 ATH_CHECK(allProxiesHaveChain(convProxies));
928 ATH_CHECK(allProxiesConnected(convProxies));
929 }
930 ATH_MSG_DEBUG("Compression done");
931
932 return StatusCode::SUCCESS;
933}
934
935template <typename MAP>
936StatusCode Run2ToRun3TrigNavConverterV2::collapseProxies(ConvProxySet_t &convProxies, MAP &keyToProxyMap) const
937{
938 // collapse proxies based on the mapping in the map argument(generic) and clean proxiesSet
939 std::vector<ConvProxy *> todelete;
940 for (auto &[key, proxies] : keyToProxyMap)
941 {
942 if (proxies.size() > 1)
943 {
944 ATH_MSG_DEBUG("Merging " << proxies.size() << " similar proxies");
945 for (auto p : proxies)
946 {
947 if (p->mergeAllowed(*proxies.begin()))
948 {
949 (*proxies.begin())->merge(p);
950 todelete.push_back(p);
951 }
952 // TODO consider scanning proxies another time if merge is not allowed, it may be allowed with other proxies here
953 }
954 }
955 }
956 for (auto proxy : todelete)
957 {
958 convProxies.erase(proxy);
959 delete proxy;
960 }
961 // remove from proxies set all elements that are now unassociated (remember to delete after)
962 return StatusCode::SUCCESS;
963}
964
966{
967
968 const size_t beforeCount = convProxies.size();
969 std::map<uint64_t, ConvProxySet_t> feaToProxyMap;
970 for (auto proxy : convProxies)
971 {
972 proxy->feaHash = feaToHash(proxy->te->getFeatureAccessHelpers(), proxy->te, run2Nav);
973 if (proxy->feaHash != ConvProxy::MissingFEA)
974 feaToProxyMap[proxy->feaHash].insert(proxy);
975
976 ATH_MSG_VERBOSE("TE " << TrigConf::HLTUtils::hash2string(proxy->te->getId()) << " FEA hash " << proxy->feaHash);
977 for (const HLT::TriggerElement::FeatureAccessHelper& fea : proxy->te->getFeatureAccessHelpers())
978 {
979 ATH_MSG_VERBOSE("FEA: " << fea);
980 }
981 }
982
983 for (auto [feaHash, proxies] : feaToProxyMap)
984 {
985 auto first = *proxies.begin();
986 for (auto p : proxies)
987 {
988 if (filterFEAs(first->te->getFeatureAccessHelpers(), run2Nav) !=
989 filterFEAs(p->te->getFeatureAccessHelpers(), run2Nav))
990 {
991 ATH_MSG_ERROR("Proxies grouped by FEA hash have actually distinct features (specific FEAs are different)");
992 for (auto id: p->passChains ) ATH_MSG_ERROR(" Chain ID: " << id);
993 ATH_MSG_ERROR(" TE ID: " << TrigConf::HLTUtils::hash2string(p->te->getId()));
994 for ( auto fea: first->te->getFeatureAccessHelpers() ) {
995 ATH_MSG_ERROR(" FEA (reference): " << fea);
996 }
997 for ( auto fea: p->te->getFeatureAccessHelpers() ) {
998 ATH_MSG_ERROR(" FEA (mismatched): " << fea);
999 }
1000
1001 return StatusCode::FAILURE;
1002 }
1003 }
1004 }
1005
1006
1007 ATH_CHECK(collapseProxies(convProxies, feaToProxyMap));
1008 ATH_MSG_DEBUG("Proxies with features collapsing reduces size from " << beforeCount << " to " << convProxies.size());
1009
1010 return StatusCode::SUCCESS;
1011}
1012
1014{
1015 // merge proxies bases on the parent child relation (this has to run after feature based collapsing)
1016 struct ParentChildCharacteristics
1017 {
1018 ConvProxy *parent = nullptr;
1019 ConvProxy *child = nullptr;
1020 size_t distanceFromParent = 0;
1021 bool operator<(const ParentChildCharacteristics &rhs) const
1022 {
1023 if (parent != rhs.parent)
1024 return parent < rhs.parent;
1025 if (child != rhs.child)
1026 return child < rhs.child;
1027 return distanceFromParent < rhs.distanceFromParent;
1028 }
1029 };
1030 const size_t beforeCount = convProxies.size();
1031 std::map<ParentChildCharacteristics, ConvProxySet_t> groupedProxies;
1032 for (auto proxy : convProxies)
1033 {
1034 if (proxy->feaHash == ConvProxy::MissingFEA)
1035 {
1036 ATH_MSG_VERBOSE("Featureless proxy to deal with: " << proxy->description());
1037 /* the canonical case
1038 merged parent
1039 / | | \
1040 C1 C2 C3 C4 <-- proxies to merge
1041 \ | | /
1042 merged child
1043 */
1044 auto hasSomeFeatures = [](const ConvProxy* p){ return p->feaHash != ConvProxy::MissingFEA; };
1045 if (proxy->children.size() == 1 and
1046 std::all_of(proxy->children.begin(), proxy->children.end(), hasSomeFeatures ) and
1047 proxy->parents.size() == 1 and
1048 std::all_of(proxy->parents.begin(), proxy->parents.end(), hasSomeFeatures )
1049 )
1050 {
1051 ATH_MSG_VERBOSE("Proxy to possibly merge: " << proxy->description());
1052 groupedProxies[{*(proxy->parents.begin()), *(proxy->children.begin()), 0}].insert(proxy);
1053 // TODO expand it to cover longer featureless sequences
1054 }
1055 else
1056 {
1057 ATH_MSG_VERBOSE("Featureless proxy in noncanonical situation " << proxy->description());
1058 ATH_MSG_VERBOSE("parents ");
1059 for (auto pp : proxy->parents)
1060 {
1061 ATH_MSG_VERBOSE(pp->description());
1062 }
1063 ATH_MSG_VERBOSE("children ");
1064 for (auto cp : proxy->children)
1065 {
1066 ATH_MSG_VERBOSE(cp->description());
1067 }
1068 }
1069 }
1070 }
1071
1072 ATH_CHECK(collapseProxies(convProxies, groupedProxies));
1073 ATH_MSG_DEBUG("Proxies without features collapsing reduces size from " << beforeCount << " to " << convProxies.size());
1074 return StatusCode::SUCCESS;
1075}
1076
1078{
1079 for (auto i = std::begin(convProxies); i != std::end(convProxies);)
1080 {
1081 if ((*i)->parents.size() > 1)
1082 {
1083 ConvProxy *toDel = *i;
1084 // remove it from parents/children
1085 for (auto parent : toDel->parents)
1086 {
1087 parent->children.erase(toDel);
1088 }
1089 for (auto child : toDel->children)
1090 {
1091 child->parents.erase(toDel);
1092 }
1093 delete toDel;
1094 i = convProxies.erase(i);
1095 }
1096 else
1097 {
1098 ++i;
1099 }
1100 }
1101 return StatusCode::SUCCESS;
1102}
1103
1105{
1106 // from all FEAs of the associated TE pick those objects that are to be linked
1107 for (auto &proxy : convProxies)
1108 {
1109 if (proxy->te != nullptr)
1110 {
1111 // Determine which particle type is expected based on TE name
1112 // This ensures Run3 retrieves the same object types as Run2
1113 std::string teName = TrigConf::HLTUtils::hash2string(proxy->te->getId());
1114 CLID expectedCLID = getExpectedParticleCLID(teName);
1115
1116 ATH_MSG_VERBOSE("TE " << teName << " expects CLID " << expectedCLID);
1117
1118 for (const HLT::TriggerElement::FeatureAccessHelper& helper : proxy->te->getFeatureAccessHelpers())
1119 {
1120 auto [sgKey, sgCLID, sgName] = getSgKey(run2Nav, helper);
1121 if (sgKey != 0)
1122 {
1123 if (feaToSave(helper, sgName))
1124 {
1125 // If we have a specific expected CLID, only save features matching that type
1126 if (expectedCLID != 0 && sgCLID != expectedCLID)
1127 {
1128 ATH_MSG_VERBOSE("Skipping feature with CLID " << sgCLID << " (name: " << sgName
1129 << ") for TE " << teName << " because expected CLID is " << expectedCLID);
1130 continue;
1131 }
1132 proxy->features.push_back(helper);
1133 ATH_MSG_VERBOSE("Added feature with CLID " << sgCLID << " (name: " << sgName << ") for TE " << teName);
1134 }
1135 }
1136 }
1137 }
1138 }
1139
1140 return StatusCode::SUCCESS;
1141}
1142
1144{
1145
1146 // ordered_sorter
1147 auto ordered_sorter = [&setRoiName = std::as_const(m_setRoiName)](const std::string &left, const std::string &right) -> bool
1148 {
1149 return std::find(cbegin(setRoiName), cend(setRoiName), left) < std::find(cbegin(setRoiName), cend(setRoiName), right);
1150 };
1151
1152 std::map<std::string, HLT::TriggerElement::FeatureAccessHelper, decltype(ordered_sorter)> mp(ordered_sorter);
1153
1154 for (auto &proxy : convProxies)
1155 {
1156 // TODO need check & handling of case when there is more RoIs, now overwriting
1157 if (HLT::TrigNavStructure::getRoINodes(proxy->te).size() > 1)
1158 ATH_MSG_DEBUG("Several RoIs pointing to a proxy, taking latest one for now");
1159
1160 mp.clear();
1161
1162 for (const HLT::TriggerElement::FeatureAccessHelper& helper : proxy->te->getFeatureAccessHelpers())
1163 {
1164 auto [sgKey, sgCLID, sgName] = getSgKey(run2Nav, helper);
1165 if (std::find(m_setRoiName.begin(), m_setRoiName.end(), sgName) == m_setRoiName.end())
1166 {
1167 // do not filter continue;
1168 continue;
1169 }
1170 mp[sgName] = helper;
1171 }
1172
1173 std::transform(cbegin(mp), cend(mp), back_inserter(proxy->rois),
1174 [](const std::map<std::string, HLT::TriggerElement::FeatureAccessHelper>::value_type &p)
1175 { return p.second; });
1176 }
1177
1178 // roiPropagator
1179 std::set<const ConvProxy*> visited;
1180 std::function<void(std::set<ConvProxy *> &, const std::vector<HLT::TriggerElement::FeatureAccessHelper> &)>
1181 roiPropagator = [&](std::set<ConvProxy *> &convProxyChildren, const std::vector<HLT::TriggerElement::FeatureAccessHelper> &roiParent)
1182 {
1183 for (auto &proxyChild : convProxyChildren)
1184 {
1185 if ( visited.count(proxyChild) == 1 ) {
1186 continue;
1187 }
1188 visited.insert(proxyChild);
1189 if (proxyChild->rois.empty())
1190 { // no roi update, copy from parent
1191 proxyChild->rois = roiParent;
1192 if (proxyChild->children.empty() == false)
1193 {
1194 roiPropagator(proxyChild->children, roiParent);
1195 }
1196 }
1197 }
1198 };
1199
1200 for (auto &proxy : convProxies)
1201 {
1202 roiPropagator(proxy->children, proxy->rois);
1203 }
1204
1205 return StatusCode::SUCCESS;
1206}
1207
1209{
1210 for (auto &proxy : convProxies)
1211 {
1212 for (const HLT::TriggerElement::FeatureAccessHelper& helper : proxy->te->getFeatureAccessHelpers())
1213 {
1214 if (helper.getCLID() == m_TrackParticleContainerCLID || helper.getCLID() == m_TauTrackContainerCLID)
1215 {
1216 proxy->tracks.push_back(helper);
1217 }
1218 }
1219 }
1220
1221 return StatusCode::SUCCESS;
1222}
1223
1224StatusCode Run2ToRun3TrigNavConverterV2::createIMHNodes(ConvProxySet_t &convProxies, xAOD::TrigCompositeContainer &decisions, const EventContext &context) const
1225{
1226 // Write the decision IDs of a proxy to a node, SKIPPING the plain chain ID
1227 // when the proxy also carries a leg ID of that chain. This follows the
1228 // native Run-3 convention: nodes of a leg carry only the leg ID, the plain
1229 // chain ID appears on chain-level (combined/terminus) nodes. The feature
1230 // retrieval (Trig::ChainGroup::features) always includes the plain chain ID
1231 // in the request - even for leg-restricted requests - so a plain chain ID
1232 // left on per-leg nodes would match every leg of the chain and pool the
1233 // features of all legs together (the "cross-leg feature pooling" defect).
1234 auto addNodeIDs = [](const std::set<HLT::Identifier>& ids, TrigCompositeUtils::Decision* node) {
1235 std::set<HLT::Identifier> chainsWithLegs;
1236 for (const HLT::Identifier& id : ids)
1237 {
1238 if (TCU::isLegId(id))
1239 chainsWithLegs.insert(TCU::getIDFromLeg(id));
1240 }
1241 for (const HLT::Identifier& id : ids)
1242 {
1243 if (!TCU::isLegId(id) && chainsWithLegs.count(id))
1244 continue;
1246 }
1247 };
1248
1249 for (auto &proxy : convProxies)
1250 {
1252 addNodeIDs(proxy->runChains, proxy->imNode);
1253 proxy->hNode.push_back(TrigCompositeUtils::newDecisionIn(&decisions, TrigCompositeUtils::hypoAlgNodeName())); // H
1254 addNodeIDs(proxy->passChains, proxy->hNode.back());
1255
1256 TrigCompositeUtils::linkToPrevious(proxy->hNode.front(), proxy->imNode, context); // H low IM up
1257 }
1258 // connecting current IM to all Hs in parent proxies
1259 for (auto &proxy : convProxies)
1260 {
1261 for (auto &parentProxy : proxy->parents)
1262 {
1263 TrigCompositeUtils::linkToPrevious(proxy->imNode, parentProxy->hNode.front(), context); // IM low H up (in parent)
1264 }
1265 }
1266 ATH_MSG_DEBUG("IM & H nodes made, output nav elements " << decisions.size());
1267 return StatusCode::SUCCESS;
1268}
1269
1271 const TEIdToChainsMap_t &terminalIds, const EventContext &context) const
1272{
1273 // make node & link it properly
1274 auto makeSingleSFNode = [&decisions, &context](auto lastDecisionNode, auto chainIds, TrigCompositeUtils::DecisionID idStore = 0)
1275 {
1276 auto sfNode = TrigCompositeUtils::newDecisionIn(&decisions);
1277 sfNode->setName("SF");
1278 TrigCompositeUtils::linkToPrevious(decisions.at(0), sfNode, context);
1279 TrigCompositeUtils::linkToPrevious(sfNode, lastDecisionNode, context);
1280 for (auto chainId : chainIds)
1281 {
1282 if (idStore == 0)
1283 {
1284 TrigCompositeUtils::addDecisionID(chainId, sfNode);
1285 TrigCompositeUtils::addDecisionID(chainId, decisions.at(0));
1286 }
1287 else if (chainId.numeric() == idStore)
1288 {
1289 TrigCompositeUtils::addDecisionID(chainId, sfNode);
1290 TrigCompositeUtils::addDecisionID(chainId, decisions.at(0));
1291 }
1292 }
1293 return sfNode;
1294 };
1295 auto makeSFNodes = [makeSingleSFNode](auto proxy, TrigCompositeUtils::DecisionID idToStore = 0)
1296 {
1297 if (proxy->hNode.empty())
1298 { // nothing has passed, so link to the IM node
1299 // TODO make sure it needs to be done like that
1300 makeSingleSFNode(proxy->imNode, proxy->runChains, idToStore);
1301 }
1302 else
1303 {
1304 // makeSFNode(proxy->hNode[0], TCU::decisionIDs(proxy->hNode[0])); // not using passChains as there may be additional filtering
1305 for (auto &hNode : proxy->hNode)
1306 {
1307 makeSingleSFNode(hNode, proxy->passChains, idToStore); // using passChains
1308 }
1309 }
1310 };
1311
1312 for (auto proxy : convProxies)
1313 {
1314 // associate terminal nodes to filter nodes,
1315 if (proxy->children.empty())
1316 { // the H modes are terminal
1317 makeSFNodes(proxy);
1318 }
1319 else
1320 {
1321 // likely need more iterations
1322 // nonterminal nodes that are nevertheless terminal for a given chain
1323 std::vector<TCU::DecisionID> toRetain;
1324 for (auto teId : proxy->teIDs)
1325 {
1326 auto whereInMap = terminalIds.find(teId);
1327 if (whereInMap != terminalIds.end())
1328 {
1329 toRetain.insert(toRetain.end(), whereInMap->second.begin(), whereInMap->second.end());
1330 }
1331 }
1332 for (auto chainIdstore : toRetain)
1333 {
1334 makeSFNodes(proxy, chainIdstore);
1335 }
1336 }
1337 }
1338 // associate all nodes designated as final one with the filter nodes
1339
1340 ATH_MSG_DEBUG("SF nodes made, output nav elements " << decisions.size());
1341 return StatusCode::SUCCESS;
1342}
1343
1345{
1346 // Check that only ChainIDs (not LegIDs) are present in the terminus "HLTPassRaw" node.
1347 // Check that only chains which pass the event are included.
1348 TCU::Decision* terminus = decisions.at(0);
1349 ATH_CHECK( terminus->name() == TCU::summaryPassNodeName() );
1350 TCU::DecisionIDContainer currentIDs;
1351 TCU::DecisionIDContainer filteredIDs;
1352 TCU::decisionIDs(terminus, currentIDs); // Extract, std::vector -> std::set
1353 for (const TCU::DecisionID id : currentIDs)
1354 {
1355 const TCU::DecisionID idToCheck = ( TCU::isLegId(id) ? TCU::getIDFromLeg( HLT::Identifier(id) ).numeric() : id );
1356 const std::string chainName = HLT::Identifier(idToCheck).name();
1357 // Sanity check
1358 if (!m_chainsToSave.empty())
1359 {
1360 if (std::find(m_chainsToSave.begin(), m_chainsToSave.end(), chainName) == m_chainsToSave.end())
1361 {
1362 ATH_MSG_ERROR("Navigation information for chain " << chainName << " in "
1363 << TCU::summaryPassNodeName() << " but this chain wasn't on the list of chains to save");
1364 return StatusCode::FAILURE;
1365 }
1366 }
1368 {
1369 filteredIDs.insert(idToCheck);
1370 }
1371 }
1372 terminus->setDecisions( std::vector<TCU::DecisionID>() ); // decisions.clear(), but via the xAOD setter function
1373 TCU::insertDecisionIDs(filteredIDs, terminus); // Insert, std::set -> std::vector
1374 ATH_MSG_VERBOSE("After filtering out leg IDs and checking isPassed, "
1375 "the terminus node goes from " << currentIDs.size() << " to " << filteredIDs.size() << " chain IDs.");
1376 if (msgLvl(MSG::VERBOSE))
1377 {
1378 for (const TCU::DecisionID id : filteredIDs)
1379 {
1380 ATH_MSG_VERBOSE(" -- Retained passing ID: " << HLT::Identifier(id));
1381 }
1382 }
1383 return StatusCode::SUCCESS;
1384}
1385
1387 const EventContext &context) const
1388{
1389
1390 auto makeL1Node = [&decisions, &context](auto firstDecisionNode, auto chainIds)
1391 {
1392 auto L1Node = TrigCompositeUtils::newDecisionIn(&decisions);
1393 L1Node->setName(TrigCompositeUtils::hltSeedingNodeName()); // L1
1394 for (auto chainId : chainIds)
1395 {
1396 TrigCompositeUtils::addDecisionID(chainId, L1Node);
1397 }
1398 TrigCompositeUtils::linkToPrevious(firstDecisionNode, L1Node, context); // IM -> L1
1399
1400 return L1Node;
1401 };
1402
1403 for (auto &proxy : convProxies)
1404 {
1405 // associate initial node to filter nodes,
1406 if (proxy->parents.empty())
1407 { // the IM node is initial
1408 proxy->l1Node = makeL1Node(proxy->imNode, TCU::decisionIDs(proxy->imNode)); // not using passChains as there may be additional filtering
1409 }
1410 }
1411
1412 ATH_MSG_DEBUG("L1 nodes made, output nav elements ");
1413 return StatusCode::SUCCESS;
1414}
1415
1417{
1418 size_t feaCount{0};
1419 if (proxy.features.empty())
1420 { // no features
1421 ++feaCount;
1422 }
1423 for (const auto &fea : proxy.features)
1424 {
1425 if (fea.getIndex().objectsBegin() == fea.getIndex().objectsEnd())
1426 {
1427 ++feaCount;
1428 }
1429 for (auto n = fea.getIndex().objectsBegin(); n < fea.getIndex().objectsEnd(); ++n)
1430 {
1431 ++feaCount;
1432 }
1433 }
1434 // 1 means a deafult H node created is enough, no need to expand H nodes
1435 return feaCount;
1436}
1437
1438StatusCode Run2ToRun3TrigNavConverterV2::linkFeaNode(ConvProxySet_t &convProxies, xAOD::TrigCompositeContainer &decisions, const HLT::TrigNavStructure &run2Nav, const EventContext &context) const
1439{
1440 // from all FEAs of the associated TE pick those objects that are to be linked
1441 for (const auto &proxy : convProxies)
1442 {
1443 auto [bestFeaIdx, bestObjIdx] = getHighestPtObject(*proxy, run2Nav);
1444
1445 auto feaN = getFeaSize(*proxy);
1446 if (feaN > 1)
1447 { // expand for more H nodes and connect them
1448 while (--feaN)
1449 {
1450 proxy->hNode.push_back(TrigCompositeUtils::newDecisionIn(&decisions, TrigCompositeUtils::hypoAlgNodeName())); // H
1451 for (auto chainId : proxy->passChains) // adding hash values of active chains to expanded H nodes
1452 {
1453 TrigCompositeUtils::addDecisionID(chainId, proxy->hNode.back());
1454 }
1455 // connecting to upeer IM node
1456 TrigCompositeUtils::linkToPrevious(proxy->hNode.back(), proxy->imNode, context); // H low IM up
1457 // connecting created H to IM in children proxies
1458 for (auto &childProxy : proxy->children)
1459 {
1460 TrigCompositeUtils::linkToPrevious(childProxy->imNode, proxy->hNode.back(), context); // IM child H up (just created))
1461 }
1462 }
1463 }
1464
1465 if (proxy->features.empty())
1466 { // no features attached, self link
1468 proxy->hNode.front()->setObjectLink<xAOD::TrigCompositeContainer>(TrigCompositeUtils::featureString(), linkToSelf);
1469 }
1470
1471 auto hNodeIter = proxy->hNode.begin();
1472 for (std::size_t feaIdx = 0; feaIdx < proxy->features.size(); ++feaIdx)
1473 {
1474 auto &fea = proxy->features[feaIdx];
1475 auto [sgKey, sgCLID, sgName] = getSgKey(run2Nav, fea);
1476 // link to itself when lined collection has size 0
1477 if (fea.getIndex().objectsBegin() == fea.getIndex().objectsEnd())
1478 {
1480 (*hNodeIter)->setObjectLink<xAOD::TrigCompositeContainer>(TrigCompositeUtils::featureString(), linkToSelf);
1481 ++hNodeIter;
1482 }
1483 for (auto n = fea.getIndex().objectsBegin(); n < fea.getIndex().objectsEnd(); ++n)
1484 {
1485 // connecting feature or subfeature
1486 const std::string& linkName = (feaIdx == bestFeaIdx && n == bestObjIdx) ?
1487 TrigCompositeUtils::featureString() : "subfeature";
1488 (*hNodeIter)->typelessSetObjectLink(linkName, sgKey, sgCLID, n, n + 1);
1489 ++hNodeIter;
1490 }
1491 }
1492 }
1493
1494 return StatusCode::SUCCESS;
1495}
1496
1498{
1499 // from all Rois of the associated TE pick those objects that are to be linked
1500 for (auto &proxy : convProxies)
1501 {
1502 for (auto &roi : proxy->rois)
1503 {
1504 auto [sgKey, sgCLID, sgName] = getSgKey(run2Nav, roi);
1505 if (proxy->l1Node)
1506 {
1507 proxy->l1Node->typelessSetObjectLink(TrigCompositeUtils::initialRoIString(), sgKey, sgCLID, roi.getIndex().objectsBegin());
1508 }
1509 if (proxy->rois.empty() == false)
1510 {
1511 proxy->imNode->typelessSetObjectLink(TrigCompositeUtils::roiString(), sgKey, sgCLID, roi.getIndex().objectsBegin());
1512 }
1513 }
1514 }
1515
1516 return StatusCode::SUCCESS;
1517}
1518
1520{
1521 for (auto &proxy : convProxies)
1522 {
1523 for (auto &trk : proxy->tracks)
1524 {
1525 if (proxy->imNode->hasObjectLink(TrigCompositeUtils::roiString()))
1526 {
1527 try
1528 {
1530 if (ROIElementLink.isValid())
1531 {
1532 static const SG::Decorator<ElementLink<TrigRoiDescriptorCollection>> viewBookkeeper("viewIndex");
1533 const auto & [sgKey, sgCLID, sgName] = getSgKey(run2Nav, trk);
1534 if (sgCLID == m_TrackParticleContainerCLID || sgCLID == m_TauTrackContainerCLID)
1535 {
1536 const char *tName = (sgCLID == m_TrackParticleContainerCLID) ? "TEMP_TRACKS" : "TEMP_TAU_TRACKS";
1537 const std::string tNameStr{tName};
1538 auto d = std::make_unique<TrigCompositeUtils::Decision>();
1539 d->makePrivateStore();
1540 d->typelessSetObjectLink(tName, sgKey, sgCLID, trk.getIndex().objectsBegin());
1541 if (sgCLID == m_TrackParticleContainerCLID)
1542 {
1543 for (const ElementLink<xAOD::TrackParticleContainer> &track : d->objectCollectionLinks<xAOD::TrackParticleContainer>(tNameStr))
1544 {
1545 if (track.isValid())
1546 {
1547 const xAOD::TrackParticle *t = *track;
1548 viewBookkeeper(*t) = ROIElementLink;
1549 }
1550 }
1551 }
1552 if (m_includeTauTrackFeatures == false && sgCLID == m_TauTrackContainerCLID)
1553 {
1554 for (const ElementLink<xAOD::TauTrackContainer> &track : d->objectCollectionLinks<xAOD::TauTrackContainer>(tNameStr))
1555 {
1556 if (track.isValid())
1557 {
1558 const xAOD::TauTrack_v1 *t = *track;
1559 viewBookkeeper(*t) = ROIElementLink;
1560 }
1561 }
1562 }
1563 }
1564 }
1565 } catch (SG::ExcBadForwardLink&) {
1566 ATH_MSG_WARNING("Unable to create an ElementLink into a container with no entries");
1567 }
1568 }
1569 }
1570 }
1571
1572 return StatusCode::SUCCESS;
1573}
1574
1575// does not need to be a method, so kept as local
1577{
1578 CLID thePassBitsCLID = ClassID_traits<xAOD::TrigPassBits>::ID();
1580
1581 return fea.getCLID() == thePassBitsCLID or fea.getCLID() == thePassBitsContCLID;
1582}
1583
1584std::vector<HLT::TriggerElement::FeatureAccessHelper> Run2ToRun3TrigNavConverterV2::filterFEAs(const std::vector<HLT::TriggerElement::FeatureAccessHelper> &feaVector, const HLT::TrigNavStructure &navigationDecoder) const {
1585 std::vector<HLT::TriggerElement::FeatureAccessHelper> out;
1586 for (auto fea : feaVector)
1587 {
1588 if (feaToSkip(fea))
1589 {
1590 ATH_MSG_VERBOSE("Skipping in FEA hash calculation");
1591 continue;
1592 }
1593
1594 auto [sgKey, sgCLID, sgName] = getSgKey(navigationDecoder, fea);
1595
1596 if (sgKey == 0)
1597 {
1598 ATH_MSG_VERBOSE("Skipping unrecorded (missing in SG) FEA hash calculation - name in SG: " << sgName << " FEA " << fea);
1599 continue;
1600 }
1601 out.push_back(fea);
1602 }
1603 return out;
1604}
1605
1606uint64_t Run2ToRun3TrigNavConverterV2::feaToHash(const std::vector<HLT::TriggerElement::FeatureAccessHelper> &feaVector, const HLT::TriggerElement *te_ptr, const HLT::TrigNavStructure &navigationDecoder) const
1607{
1608 // FEA vectors hashing
1609 // Filter by expected particle type based on TE name to ensure correct feature type
1610 std::string teName = TrigConf::HLTUtils::hash2string(te_ptr->getId());
1611 CLID expectedCLID = getExpectedParticleCLID(teName);
1612
1613 ATH_MSG_VERBOSE("Calculating FEA hash for TE " << teName << " expecting CLID " << expectedCLID);
1614 uint64_t hash = 0;
1615 for (auto fea : filterFEAs(feaVector, navigationDecoder))
1616 {
1617 const auto & [sgKey, sgCLID, sgName] = getSgKey(navigationDecoder, fea);
1618
1619 // Apply TE-based filtering during hash calculation to prevent wrong merges
1620 if (expectedCLID != 0 && sgCLID != expectedCLID)
1621 {
1622 ATH_MSG_VERBOSE("Skipping FEA with CLID " << sgCLID << " for TE " << teName
1623 << " (expected " << expectedCLID << ")");
1624 continue;
1625 }
1626
1627 ATH_MSG_VERBOSE("Including FEA in hash CLID: " << fea.getCLID() << " te Id: " << te_ptr->getId());
1628 boost::hash_combine(hash, fea.getCLID());
1629 boost::hash_combine(hash, fea.getIndex().subTypeIndex());
1630 boost::hash_combine(hash, fea.getIndex().objectsBegin());
1631 boost::hash_combine(hash, fea.getIndex().objectsEnd());
1632 }
1633 // Include the originating TE identifier and pointer to ensure that
1634 // navigation elements stemming from different Trigger Elements do not
1635 // collapse into a single proxy even if their features are otherwise
1636 // identical. The TE ID alone is not sufficient, as multiple clones of the
1637 // same TE share the ID, so we also add the pointer value to the hash.
1638 boost::hash_combine(hash, te_ptr->getId());
1639 boost::hash_combine(hash, reinterpret_cast<std::uintptr_t>(te_ptr));
1640 ATH_MSG_VERBOSE("Obtained FEA hash " << hash);
1641 return hash;
1642}
1643
1645{
1646 auto iter = m_collectionsToSaveDecoded.find(fea.getCLID());
1647 if (iter != m_collectionsToSaveDecoded.end())
1648 {
1649 if ( iter->second.empty() )
1650 return true; // feature accepted for saving
1651 ATH_MSG_DEBUG("fea to save CLID: " << fea.getCLID() << ", sgName: " << sgName << " " <<iter->second.size() << " " << iter->second.empty() );
1652 return iter->second.contains(sgName);
1653 }
1654
1655 return false;
1656}
1657
1658
1659
1661{
1662 auto [sgKey, sgCLID, sgName] = getSgKey(run2Nav, roi);
1663 if (std::find(m_setRoiName.begin(), m_setRoiName.end(), sgName) != m_setRoiName.end())
1664 {
1665 return true;
1666 }
1667
1668 return false;
1669}
1670
1672{
1673 for (auto p : proxies)
1674 {
1675 if (p->runChains.empty())
1676 {
1677 ATH_MSG_ERROR("Proxy with no chains");
1678 return StatusCode::FAILURE;
1679 }
1680 }
1681 ATH_MSG_DEBUG("CHECK OK, no proxies w/o a chain");
1682 return StatusCode::SUCCESS;
1683}
1684
1686{
1687 for (auto p : proxies)
1688 {
1689 if (p->children.empty() and p->parents.empty() and not p->runChains.empty())
1690 {
1691 ATH_MSG_ERROR("Orphaned proxy, N chains run: " << p->runChains.size());
1692 return StatusCode::FAILURE;
1693 }
1694 }
1695 ATH_MSG_DEBUG("CHECK OK, no orphaned proxies");
1696 return StatusCode::SUCCESS;
1697}
1698
1700{
1701 ATH_MSG_DEBUG("CHECK OK, no excessive number of H nodes per proxy");
1702 return StatusCode::SUCCESS;
1703}
1704
1706{
1707 // build map of all links to H nodes from IMs and FS
1708 std::set<const TrigCompositeUtils::Decision *> linkedHNodes;
1709 for (auto d : decisions)
1710 {
1711 if (d->name() == "IM" or d->name() == "FS")
1712 {
1713 for (auto el : TCU::getLinkToPrevious(d))
1714 {
1715 linkedHNodes.insert(*el); // dereferences to bare pointer
1716 }
1717 }
1718 }
1719 for (auto d : decisions)
1720 {
1721 if (d->name() == "H")
1722 {
1723 if (linkedHNodes.count(d) == 0)
1724 {
1725 ATH_MSG_ERROR("Orphaned H node");
1726 return StatusCode::FAILURE;
1727 }
1728 }
1729 }
1730 ATH_MSG_DEBUG("CHECK OK, all H modes are connected");
1731
1732 return StatusCode::SUCCESS;
1733}
1734
1735std::tuple<uint32_t, CLID, std::string> Run2ToRun3TrigNavConverterV2::getSgKey(const HLT::TrigNavStructure &navigationDecoder, const HLT::TriggerElement::FeatureAccessHelper &helper) const
1736{
1737 const std::string hltLabel = navigationDecoder.label(helper.getCLID(), helper.getIndex().subTypeIndex());
1738
1739 const CLID saveCLID = [&](const CLID &clid)
1740 {
1741 if (clid == m_roIDescriptorCLID)
1743 if (clid == m_TrigEMClusterCLID)
1745 if (clid == m_TrigRingerRingsCLID)
1747 return clid;
1748 }(helper.getCLID());
1749
1750 std::string type_name;
1751 if (m_clidSvc->getTypeNameOfID(saveCLID, type_name).isFailure())
1752 {
1753 return {0, 0, ""};
1754 }
1755
1756 const auto sgStringKey = HLTNavDetails::formatSGkey("HLT", type_name, hltLabel);
1757 const bool isAvailable = evtStore()->contains(saveCLID, sgStringKey);
1758 ATH_MSG_DEBUG(" Objects presence " << helper << " " << sgStringKey << (isAvailable ? " present" : " absent"));
1759 if (!isAvailable)
1760 {
1761 return {0, saveCLID, ""};
1762 }
1763
1764 return {evtStore()->stringToKey(sgStringKey, saveCLID), saveCLID, hltLabel}; // sgKey, sgCLID, sgName
1765}
1766
1767std::pair<std::size_t, std::size_t> Run2ToRun3TrigNavConverterV2::getHighestPtObject(
1768 const ConvProxy& proxy, const HLT::TrigNavStructure& run2Nav) const
1769{
1770 std::size_t bestFea = std::numeric_limits<std::size_t>::max();
1771 std::size_t bestObj = 0;
1772 float bestPt = -1.0;
1773
1774 for (std::size_t i = 0; i < proxy.features.size(); ++i) {
1775 const auto& fea = proxy.features[i];
1776 auto [sgKey, sgCLID, sgName] = getSgKey(run2Nav, fea);
1777 if (!feaToSave(fea, sgName)) {
1778 continue;
1779 }
1780 if (sgKey == 0) continue;
1781 const std::string* keyStr = evtStore()->keyToString(sgKey, sgCLID);
1782 if (!keyStr) continue;
1783 const xAOD::IParticleContainer* cont = nullptr;
1784 if (evtStore()->retrieve(cont, *keyStr).isFailure()) continue;
1785 for (auto n = fea.getIndex().objectsBegin(); n < fea.getIndex().objectsEnd(); ++n) {
1786 if (n >= cont->size()) continue;
1787 const xAOD::IParticle* p = (*cont)[n];
1788 if (!p) continue;
1789 if (p->pt() > bestPt) {
1790 bestPt = p->pt();
1791 bestFea = i;
1792 bestObj = n;
1793 }
1794 }
1795 }
1796 if (bestFea == std::numeric_limits<std::size_t>::max()) {
1797 for (std::size_t i = 0; i < proxy.features.size(); ++i) {
1798 auto [sgKey, sgCLID, sgName] = getSgKey(run2Nav, proxy.features[i]);
1799 if (!feaToSave(proxy.features[i], sgName)) continue;
1800 bestFea = i;
1801 bestObj = proxy.features[i].getIndex().objectsBegin();
1802 break;
1803 }
1804 }
1805 return {bestFea, bestObj};
1806}
1807
1809{
1810 // Determine which particle type is expected based on TE name
1811 // This mimics the logic from IParticleRetrievalTool::getEGammaTEType()
1812 // to ensure Run3 retrieves the same object types as Run2
1813
1814 // For egamma TEs, check for specific patterns
1815 if (teName.find("etcut") != std::string::npos &&
1816 teName.find("trkcut") == std::string::npos) {
1817 // etcut chains (without trkcut) use CaloCluster
1819 }
1820 else if (teName.rfind("EF_e", 0) == 0) {
1821 // TE name starts with "EF_e" -> Electron
1823 }
1824 else if (teName.rfind("EF_g", 0) == 0) {
1825 // TE name starts with "EF_g" -> Photon
1826 return m_PhotonContainerCLID;
1827 }
1828 else if (teName.rfind("EF_mu", 0) == 0 || teName.find("_mu") != std::string::npos) {
1829 // Muon TEs
1830 return m_MuonContainerCLID;
1831 }
1832 else if (teName.rfind("EF_tau", 0) == 0 || teName.find("_tau") != std::string::npos) {
1833 // Tau TEs
1834 return m_TauJetContainerCLID;
1835 }
1836
1837 // For non-physics TEs or TEs where we don't have a specific expectation,
1838 // return 0 to indicate all features should be saved
1839 return 0;
1840}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
#define ATH_MSG_INFO(x,...)
a traits class that associates a CLID to a type T It also detects whether T inherits from Gaudi DataO...
bool operator<(const DataVector< T > &a, const DataVector< T > &b)
Vector ordering relation.
uint32_t CLID
The Class ID type.
virtual void lock()=0
Interface to allow an object to lock itself when made const in SG.
bool feaToSkip(const HLT::TriggerElement::FeatureAccessHelper &fea)
std::map< HLT::te_id_type, std::set< HLT::Identifier > > TEIdToChainsMap_t
std::set< ConvProxy * > ConvProxySet_t
ServiceHandle< StoreGateSvc > & evtStore()
bool msgLvl(const MSG::Level lvl) const
An algorithm that can be simultaneously executed in multiple threads.
Helper class that provides access to information about individual legs.
size_type size() const noexcept
Returns the number of elements in the collection.
TrigCompositeUtils::DecisionID numeric() const
numeric ID
std::string name() const
reports human redable name
virtual bool deserialize(const std::vector< uint32_t > &input)
static bool isInitialNode(const TriggerElement *te)
queries if node is an initial one
std::vector< TriggerElement * > & getAllTEs()
access needed by slimming tools.
static const std::vector< TriggerElement * > & getRoINodes(const TriggerElement *somenode)
gets all RoI type nodes seeding indirectly this TriggerElement
static const std::vector< TriggerElement * > & getDirectPredecessors(const TriggerElement *te)
returns list of direct predecessors (nodes seeding me)
std::string label(class_id_type clid, const index_or_label_type &sti_or_label) const
the FeatureAccessHelper is a class used to keep track of features attached to this TE.
TriggerElement is the basic ingreedient of the interface between HLT algorithms and the navigation It...
StatusCode createIMHNodes(ConvProxySet_t &, xAOD::TrigCompositeContainer &, const EventContext &) const
StatusCode linkRoiNode(ConvProxySet_t &convProxies, const HLT::TrigNavStructure &run2Nav) const
Gaudi::Property< std::vector< std::string > > m_chainsToSave
std::function< void(const ConvProxy *)> m_teIDPrinter
PublicToolHandle< Trig::TrigDecisionTool > m_tdt
StatusCode extractTECtoChainMapping(TEIdToChainsMap_t &allTES, TEIdToChainsMap_t &finalTEs) const
Gaudi::Property< std::vector< std::string > > m_roisToSave
StatusCode collapseFeaturesProxies(ConvProxySet_t &convProxies, const HLT::TrigNavStructure &run2Nav) const
virtual StatusCode execute(const EventContext &context) const override
ServiceHandle< IClassIDSvc > m_clidSvc
StatusCode cureUnassociatedProxies(ConvProxySet_t &) const
StatusCode updateTerminusNode(xAOD::TrigCompositeContainer &, const EventContext &context) const
StatusCode mirrorTEsStructure(ConvProxySet_t &, const HLT::TrigNavStructure &run2Nav) const
StatusCode createSFNodes(const ConvProxySet_t &, xAOD::TrigCompositeContainer &, const TEIdToChainsMap_t &finalTEs, const EventContext &context) const
StatusCode removeTopologicalProxies(ConvProxySet_t &) const
SG::ReadHandleKey< xAOD::TrigNavigation > m_trigNavKey
Run2ToRun3TrigNavConverterV2(const std::string &name, ISvcLocator *pSvcLocator)
StatusCode removeUnassociatedProxies(ConvProxySet_t &) const
StatusCode linkFeaNode(ConvProxySet_t &convProxies, xAOD::TrigCompositeContainer &, const HLT::TrigNavStructure &run2Nav, const EventContext &context) const
StatusCode associateChainsToProxies(ConvProxySet_t &, const TEIdToChainsMap_t &) const
CLID getExpectedParticleCLID(const std::string &teName) const
Helper function to determine expected particle CLID based on TE name Returns 0 if no specific type is...
StatusCode bjetMuChainConfigDecoder(TEIdToChainsMap_t &allTES, TEIdToChainsMap_t &finalTEs, const TrigConf::HLTChain *ptrChain) const
std::pair< std::size_t, std::size_t > getHighestPtObject(const ConvProxy &, const HLT::TrigNavStructure &) const
Return pair of indices (feature index in proxy->features vector, object index) identifying the highes...
StatusCode noUnconnectedHNodes(const xAOD::TrigCompositeContainer &) const
bool feaToSave(const HLT::TriggerElement::FeatureAccessHelper &fea, const std::string &sgName) const
uint64_t feaToHash(const std::vector< HLT::TriggerElement::FeatureAccessHelper > &feaVector, const HLT::TriggerElement *te_ptr, const HLT::TrigNavStructure &navigationDecoder) const
returns true if this particular feature is to be saved (linked)
size_t is2LegTopoChain(const TrigConf::HLTChain *ptrChain) const
bool roiToSave(const HLT::TrigNavStructure &run2Nav, const HLT::TriggerElement::FeatureAccessHelper &fea) const
StatusCode doCompression(ConvProxySet_t &convProxies, const HLT::TrigNavStructure &run2Nav) const
StatusCode linkTrkNode(ConvProxySet_t &convProxies, const HLT::TrigNavStructure &run2Nav) const
StatusCode numberOfHNodesPerProxyNotExcessive(const ConvProxySet_t &) const
void printProxies(const ConvProxySet_t &proxies, std::function< bool(const ConvProxy *)> selector=[](const ConvProxy *){return true;}, const std::vector< std::function< void(const ConvProxy *)> > &printers={}) const
StatusCode fillRelevantTracks(ConvProxySet_t &convProxies) const
Gaudi::Property< bool > m_includeTauTrackFeatures
std::tuple< uint32_t, CLID, std::string > getSgKey(const HLT::TrigNavStructure &navigationDecoder, const HLT::TriggerElement::FeatureAccessHelper &helper) const
StatusCode fillRelevantRois(ConvProxySet_t &convProxies, const HLT::TrigNavStructure &run2Nav) const
StatusCode allProxiesHaveChain(const ConvProxySet_t &) const
SG::WriteHandleKey< xAOD::TrigCompositeContainer > m_trigOutputNavKey
StatusCode createL1Nodes(const ConvProxySet_t &convProxies, xAOD::TrigCompositeContainer &decisions, const EventContext &context) const
Gaudi::Property< std::vector< std::string > > m_collectionsToSave
std::function< void(const ConvProxy *)> m_chainIdsPrinter
std::map< CLID, std::set< std::string > > m_collectionsToSaveDecoded
StatusCode fillRelevantFeatures(ConvProxySet_t &convProxies, const HLT::TrigNavStructure &run2Nav) const
std::size_t getFeaSize(const ConvProxy &) const
virtual StatusCode initialize() override
StatusCode collapseFeaturelessProxies(ConvProxySet_t &) const
ServiceHandle< TrigConf::IHLTConfigSvc > m_configSvc
StatusCode allProxiesConnected(const ConvProxySet_t &) const
StatusCode collapseProxies(ConvProxySet_t &, MAP &) const
std::vector< HLT::TriggerElement::FeatureAccessHelper > filterFEAs(const std::vector< HLT::TriggerElement::FeatureAccessHelper > &feaVector, const HLT::TrigNavStructure &navigationDecoder) const
< both method skip TrigPassBits
virtual bool isValid() override final
Can the handle be successfully dereferenced?
pointer_type ptr()
Dereference the pointer.
HLT chain configuration information.
const std::vector< HLTSignature * > & signatures() const
static const std::string hash2string(HLTHash, const std::string &category=s_defaultCategory)
hash function translating identifiers into names (via internal dictionary)
const std::string & name() const
Definition node.h:24
Class providing the definition of the 4-vector interface.
void setDecisions(const std::vector< TrigCompositeUtils::DecisionID > &decisions)
Set positive HLT chain decisions associated with this TrigComposite. Navigation use.
const std::string & name() const
Get a human-readable name for the object.
bool add(const std::string &hname, TKey *tobj)
Definition fastadd.cxx:55
std::vector< int > multiplicities(const std::string &chain)
std::string formatSGkey(const std::string &prefix, const std::string &containername, const std::string &label)
declaration of formatting function.
Definition Holder.cxx:121
SG::Decorator< T, ALLOC > Decorator
Helper class to provide type-safe access to aux data, specialized for JaggedVecElt.
Definition AuxElement.h:576
const std::regex gammaXeChain
const std::regex egammaDiEtcut
const std::regex isTopo
const std::regex egammaCombinedWithEtcut
const std::regex egammaEtcut
const std::regex bjetMuChain
const std::regex mu2MunoL1Special
const std::regex tauXeChain
HLT::Identifier createLegName(const HLT::Identifier &chainIdentifier, size_t counter)
Generate the HLT::Identifier which corresponds to a specific leg of a given chain.
const std::string & inputMakerNodeName()
unsigned int DecisionID
xAOD::TrigComposite Decision
void insertDecisionIDs(const Decision *src, Decision *dest)
Appends the decision IDs of src to the dest decision object.
const std::string & roiString()
Decision * newDecisionIn(DecisionContainer *dc, const std::string &name)
Helper method to create a Decision object, place it in the container and return a pointer to it.
const std::vector< ElementLink< DecisionContainer > > getLinkToPrevious(const Decision *d)
returns links to previous decision object 'seed'
const std::string & featureString()
HLT::Identifier getIDFromLeg(const HLT::Identifier &legIdentifier)
Generate the HLT::Identifier which corresponds to the chain name from the leg name.
std::set< DecisionID > DecisionIDContainer
SG::WriteHandle< DecisionContainer > createAndStore(const SG::WriteHandleKey< DecisionContainer > &key, const EventContext &ctx)
Creates and right away records the DecisionContainer with the key.
const std::string & hypoAlgNodeName()
void linkToPrevious(Decision *d, const std::string &previousCollectionKey, size_t previousIndex)
Links to the previous object, location of previous 'seed' decision supplied by hand.
const std::string & initialRoIString()
const std::string & summaryPassNodeName()
void addDecisionID(DecisionID id, Decision *d)
Appends the decision (given as ID) to the decision object.
const std::string & hltSeedingNodeName()
void decisionIDs(const Decision *d, DecisionIDContainer &destination)
Extracts DecisionIDs stored in the Decision object.
ElementLink< DecisionContainer > decisionToElementLink(const Decision *d, const EventContext &ctx)
Takes a raw pointer to a Decision and returns an ElementLink to the Decision.
bool isLegId(const HLT::Identifier &legIdentifier)
Recognise whether the chain ID is a leg ID.
Definition index.py:1
void reverse(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of reverse for DataVector/List.
TrigCompositeContainer_v1 TrigCompositeContainer
Declare the latest version of the container.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".
TauTrackContainer_v1 TauTrackContainer
Definition of the current TauTrack container version.
DataVector< IParticle > IParticleContainer
Simple convenience declaration of IParticleContainer.
#define unlikely(x)
Struct containing information on each leg of a chain.
std::set< ConvProxy * > children
std::vector< HLT::te_id_type > teIDs
static const uint64_t MissingFEA
bool mergeAllowed(const ConvProxy *other) const
std::set< HLT::Identifier > runChains
std::set< HLT::Identifier > passChains
std::set< ConvProxy * > parents
const HLT::TriggerElement * te
bool isParent(const ConvProxy *other) const
bool isChild(const ConvProxy *other) const
void merge(ConvProxy *other)
ConvProxy(const HLT::TriggerElement *te)
std::string description() const