ATLAS Offline Software
Loading...
Searching...
No Matches
TrigCompositeUtilsRoot.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5// See similar workaround the lack of CLID in standalone releases in TrigComposite_v1.h
7
11
13
14#include <format>
15#include <unordered_set>
16
19
20
21
22namespace TrigCompositeUtils {
23
24 ANA_MSG_SOURCE (msgFindLink, "TrigCompositeUtils.findLink")
25 ANA_MSG_SOURCE (msgRejected, "TrigCompositeUtils.getRejectedDecisionNodes")
26
27
28 SG::WriteHandle<DecisionContainer> createAndStore( const SG::WriteHandleKey<DecisionContainer>& key, const EventContext& ctx ) {
29 SG::WriteHandle<DecisionContainer> handle( key, ctx );
30 auto data = std::make_unique<DecisionContainer>() ;
31 auto aux = std::make_unique<DecisionAuxContainer>() ;
32 data->setStore( aux.get() );
33 handle.record( std::move( data ), std::move( aux ) ).ignore();
34 return handle;
35 }
36
38 auto data = std::make_unique<DecisionContainer>() ;
39 auto aux = std::make_unique<DecisionAuxContainer>() ;
40 data->setStore( aux.get() );
41 handle.record( std::move( data ), std::move( aux ) ).ignore();
42 }
43
44 Decision* newDecisionIn ( DecisionContainer* dc, const std::string& name) {
45 Decision * x = new Decision;
46 dc->push_back( x );
47 if ( ! name.empty() ) {
48 x->setName( name );
49 }
50 return x;
51 }
52
53 Decision* newDecisionIn ( DecisionContainer* dc, const Decision* dOld, const std::string& name, const EventContext& ctx ) {
54 Decision* dNew = newDecisionIn( dc, name );
55 linkToPrevious(dNew, dOld, ctx); // Sets up link to 'seed' collection, points to dOld
56 return dNew;
57 }
58
60 std::vector<DecisionID>& decisions = readWriteAccessor( *d );
61 if ( decisions.size() == 0 or decisions.back() != id)
62 decisions.push_back( id );
63 }
64
65 void decisionIDs( const Decision* d, DecisionIDContainer& destination ) {
66 const std::vector<DecisionID>& decisions = readOnlyAccessor( *d );
67 destination.insert( decisions.begin(), decisions.end() );
68 }
69
70 const std::vector<DecisionID>& decisionIDs( const Decision* d ) {
71 return readOnlyAccessor( *d );
72 }
73
74 std::vector<DecisionID>& decisionIDs( Decision* d ) {
75 return readWriteAccessor( *d );
76 }
77
78 void insertDecisionIDs(const Decision* src, Decision* dest ){
80 decisionIDs( src, srcIds ); // Now stored in a set
81 insertDecisionIDs( srcIds, dest);
82 }
83
85 DecisionIDContainer collateIDs;
86 // Decision are xAOD objects backed by a std::vector
87 // Here we use a std::set to de-duplicate IDs from src and dest before setting dest
88 decisionIDs( dest, collateIDs ); // Set operation 1. Get from dest
89 collateIDs.insert( src.begin(), src.end() ); // Set operation 2. Get from src
90 std::vector<DecisionID>& vdest = decisionIDs( dest );
91 // Clear and reserve target
92 vdest.clear();
93 vdest.reserve(collateIDs.size());
94 // Copy from set to (ordered) vector
95 vdest.insert( vdest.end(), collateIDs.begin(), collateIDs.end() );
96 }
97
99 // Re-use above insertDecisionIDs method.
100 // This implicitly performs de-duplication
101 return insertDecisionIDs(dest, dest);
102 }
103
104 bool allFailed( const Decision* d ) {
105 const std::vector<DecisionID>& decisions = readOnlyAccessor( *d );
106 return decisions.empty();
107 }
108
109 bool isAnyIDPassing( const Decision* d, const DecisionIDContainer& required ) {
110 for ( DecisionID id : readOnlyAccessor( *d ) ) {
111 if ( required.find( id ) != required.end() ) {
112 return true;
113 }
114 }
115 return false;
116 }
117
118 bool passed( DecisionID id, const DecisionIDContainer& idSet ) {
119 return idSet.find( id ) != idSet.end();
120 }
121
122#if !defined(XAOD_STANDALONE) && !defined(XAOD_ANALYSIS) // Full athena
124 const DecisionContainer* container = dynamic_cast<const DecisionContainer*>( d->container() );
125 if( ! container ) {
126 throw std::runtime_error("TrigCompositeUtils::convertToElementLink Using convertToElementLink(d) requires that the Decision d is already in a container");
127 }
128 return ElementLink<DecisionContainer>(*container, d->index(), ctx);
129 }
130#else // Analysis or Standalone
131 ElementLink<DecisionContainer> decisionToElementLink(const Decision* d, const EventContext&) {
132 const DecisionContainer* container = dynamic_cast<const DecisionContainer*>( d->container() );
133 if( ! container ) {
134 throw std::runtime_error("TrigCompositeUtils::convertToElementLink Using convertToElementLink(d) requires that the Decision d is already in a container");
135 }
136 return ElementLink<DecisionContainer>(*container, d->index());
137 }
138#endif
139
140 void linkToPrevious( Decision* d, const std::string& previousCollectionKey, size_t previousIndex ) {
141 ElementLink<DecisionContainer> seed = ElementLink<DecisionContainer>( previousCollectionKey, previousIndex );
142 if (!seed.isValid()) {
143 throw std::runtime_error("TrigCompositeUtils::linkToPrevious Invalid Decision Link key or index provided");
144 } else {
145 d->addObjectCollectionLink(seedString(), seed);
146 }
147 }
148
149 void linkToPrevious( Decision* d, const Decision* dOld, const EventContext& ctx ) {
150 d->addObjectCollectionLink(seedString(), decisionToElementLink(dOld, ctx));
151 }
152
153 bool hasLinkToPrevious( const Decision* d ) {
154 return d->hasObjectCollectionLinks( seedString() );
155 }
156
157 const std::vector<ElementLink<DecisionContainer>> getLinkToPrevious( const Decision* d ) {
158 return d->objectCollectionLinks<DecisionContainer>( seedString() );
159 }
160
161
162 bool copyLinks(const Decision* src, Decision* dest) {
163 return dest->copyAllLinksFrom(src);
164 }
165
166 HLT::Identifier createLegName(const HLT::Identifier& chainIdentifier, size_t counter) {
167 return createLegName( chainIdentifier.name(), counter );
168 }
169
170 HLT::Identifier createLegName(const std::string& name, size_t counter) {
171 if (!isChainId(name)) {
172 throw std::runtime_error("TrigCompositeUtils::createLegName chainIdentifier '"+name+"' does not start with 'HLT_'");
173 }
174 if (counter > 999) {
175 throw std::runtime_error("TrigCompositeUtils::createLegName Leg counters above 999 are invalid.");
176 }
177 return HLT::Identifier( std::format("leg{:0>3d}_{}", counter, name) );
178 }
179
181 const std::string& name = legIdentifier.name();
182 if (isChainId(name)){
183 return legIdentifier;
184 } else if (isLegId(name)){
185 return HLT::Identifier(name.substr(7));
186 } else{
187 throw std::runtime_error("TrigCompositeUtils::getIDFromLeg legIdentifier '"+name+"' does not start with 'HLT_' or 'leg' ");
188 }
189 }
190
191 int32_t getIndexFromLeg(const HLT::Identifier& legIdentifier) {
192 return getIndexFromLeg(legIdentifier.name());
193 }
194
195 int32_t getIndexFromLeg(const std::string& name) {
196 int32_t id = 0;
197 if (isChainId(name)){
198 // pass
199 } else if (isLegId(name)) {
200 std::from_chars(name.data()+3, name.data()+6, id);
201 } else {
202 throw std::runtime_error("TrigCompositeUtils::getIndexFromLeg legIdentifier '"+name+"' does not start with 'HLT_' or 'leg' ");
203 }
204 return id;
205 }
206
207 std::pair<std::string, int32_t> getNameAndIndexFromLeg(const std::string& name) {
208 int32_t id = 0;
209 if (isChainId(name)) {
210 return {name, id};
211 } else if (isLegId(name)) {
212 std::from_chars(name.data()+3, name.data()+6, id);
213 return {name.substr(7), id};
214 } else {
215 throw std::runtime_error("TrigCompositeUtils::getIDFromLeg legIdentifier '"+name+"' does not start with 'HLT_' or 'leg' ");
216 }
217 }
218
219 bool isLegId(const HLT::Identifier& legIdentifier) {
220 return isLegId(legIdentifier.name());
221 }
222
223 bool isLegId(const std::string& name) {
224 return name.starts_with("leg");
225 }
226
227 bool isChainId(const HLT::Identifier& chainIdentifier) {
228 return isChainId(chainIdentifier.name());
229 }
230
231 bool isChainId(const std::string& name) {
232 return name.starts_with("HLT_");
233 }
234
235
236 const Decision* find( const Decision* start, const std::function<bool( const Decision* )>& filter ) {
237 if ( filter( start ) ) return start;
238
239 if ( hasLinkToPrevious(start) ) {
240 const std::vector<ElementLink<DecisionContainer>> seeds = getLinkToPrevious(start);
241 for (const ElementLink<DecisionContainer>& seedEL : seeds) {
242 const Decision* result = find( *seedEL, filter );
243 if (result) return result;
244 }
245 }
246
247 return nullptr;
248 }
249
250 bool HasObject::operator()( const Decision* composite ) const {
251 return composite->hasObjectLink( m_name );
252 }
253
254 bool HasObjectCollection::operator()( const Decision* composite ) const {
255 return composite->hasObjectCollectionLinks( m_name );
256 }
257
261
265
269
270 const Decision* getNodeByName(const DecisionContainer& container, const std::string& nodeName) {
271 const auto it = std::find_if(container.begin(), container.end(), [&nodeName](const Decision* d){return d->name()==nodeName;});
272 if (it==container.end()) {return nullptr;}
273 return *it;
274 }
275
276 std::vector<const Decision*> getRejectedDecisionNodes([[maybe_unused]] const asg::EventStoreType* eventStore,
277 const EventContext& ctx,
278 const std::string& summaryCollectionKey,
279 const DecisionIDContainer& ids,
280 const std::set<std::string>& keysToIgnore) {
281
282 // The following list contains all known summary store identifiers where the graph nodes are spread out over O(100s) or O(1000s)
283 // of different SG collections. This is the raw output from running the trigger online.
284 // When dealing with this, we need to query eventStore->keys in every event to obtain the full set of collections to process.
285 static const std::unordered_set<std::string> knownDistributedSummaryStores{
286 "HLTNav_Summary",
287 "_HLTNav_Summary"
288 };
289
290 // The following list contains all known summary store identifiers where all nodes from the graph have been compactified / condensed
291 // down into a single container. Here we just have to search this one container.
292 static const std::unordered_set<std::string> knownCompactSummaryStores{"HLTNav_Summary_OnlineSlimmed",
293 "HLTNav_Summary_ESDSlimmed",
294 "HLTNav_Summary_AODSlimmed",
295 "HLTNav_Summary_DAODSlimmed",
296 "HLTNav_R2ToR3Summary"
297 };
298
299 std::vector<std::string> keys; // The SG keys we will be exploring to find rejected decision nodes
300
301 if (knownDistributedSummaryStores.contains(summaryCollectionKey) or summaryCollectionKey.empty()) {
302
303 // If we have a distributed store then we need to query SG to find all keys.
304 // This should be a rare case now that we run compactification "online" (i.e. immediately after the trigger has executed)
305#ifndef XAOD_STANDALONE
306 // The list of containers we need to read can change on a file-by-file basis (it depends on the SMK)
307 // Hence we query SG for all collections rather than maintain a large and ever changing ReadHandleKeyArray
308 eventStore->keys<DecisionContainer>(keys);
309#else
310 throw std::runtime_error("Cannot obtain rejected HLT features in AnalysisBase when reading from uncompactified navigation containers, run trigger navigation slimming first if you really need this.");
311#endif
312
313 } else if (knownCompactSummaryStores.contains(summaryCollectionKey)) {
314
315 keys.push_back(summaryCollectionKey);
316
317 } else {
318
319 using namespace msgRejected;
320 ANA_MSG_WARNING("getRejectedDecisionNodes has not been told about final collection " << summaryCollectionKey << " please update this function. Assuming that it is already compact.");
321 // Safest to assume that this is a compact summary store
322 keys.push_back(summaryCollectionKey);
323
324 }
325
326 std::vector<const Decision*> output; // The return vector of identified nodes where one of the chains in 'ids' was rejected
327
328 // ReadHandleKey to be re-used in the loop to avoid repeated CLID lookups
329 SG::ReadHandleKey<DecisionContainer> containerRHKey("temp");
330 if (!containerRHKey.initialize().isSuccess()) {
331 throw std::runtime_error("Cannot initialize ReadHandleKey for DecisionContainer");
332 }
333
334 // Loop over each DecisionContainer,
335 for (const std::string& key : keys) {
336 // Get and check this container
337 if ( ! (key.starts_with( "HLTNav_") || key.starts_with("_HLTNav_")) ) {
338 continue; // Only concerned about the decision containers which make up the navigation, they have name prefix of HLTNav (or _HLTNav for transient-only mode)
339 }
340 if (keysToIgnore.contains(key)) {
341 continue; // Have been asked to not explore this SG container
342 }
343
344 // Create ReadHandle for this key
345 containerRHKey = key;
346 auto containerRH = SG::makeHandle(containerRHKey, ctx);
347 if (!containerRH.isValid()) {
348 throw std::runtime_error("Unable to retrieve " + key + " from event store.");
349 }
350
351 for (const Decision* d : *containerRH) {
352 if ( not (d->name() == hypoAlgNodeName() or d->name() == comboHypoAlgNodeName()) ) {
353 continue; // Only want Decision objects created by HypoAlgs or ComboHypoAlgs
354 }
355 const std::vector<ElementLink<DecisionContainer>> mySeeds = d->objectCollectionLinks<DecisionContainer>(seedString());
356 if (mySeeds.empty()) {
357 continue;
358 }
359 const bool allSeedsValid = std::all_of(mySeeds.begin(), mySeeds.end(), [](const ElementLink<DecisionContainer>& s) { return s.isValid(); });
360 if (!allSeedsValid) {
361 using namespace msgRejected;
362 ANA_MSG_WARNING("A Decision object in " << key << " has invalid seeds. "
363 << "The trigger navigation information is incomplete. Skipping this Decision object.");
364 continue;
365 }
366
367 DecisionIDContainer activeChainsIntoThisDecision;
368 decisionIDs(*mySeeds[0], activeChainsIntoThisDecision); // Get list of active chains from the first parent
369 if (mySeeds.size() > 1) {
370 for (size_t i = 1; i < mySeeds.size(); ++i) {
371 // If there are more than one parent, we only want to keep the intersection of all of the seeds
372 DecisionIDContainer moreActiveChains;
373 decisionIDs(*mySeeds[i], moreActiveChains);
375 std::set_intersection(activeChainsIntoThisDecision.begin(), activeChainsIntoThisDecision.end(),
376 moreActiveChains.begin(), moreActiveChains.end(),
377 std::inserter(intersection, intersection.begin()));
378 activeChainsIntoThisDecision = std::move(intersection); // Update the output to only be the intersection and continue to any other seeds
379 }
380 }
381 // We now know what chains were active coming into this Decision (d) from ALL seeds
382 // This is the logic required for each HypoTool to have activated and checked if its chain passes
383 // So the size of activeChainsIntoThisDecision corresponds to the number of HypoTools which will have run
384 // What do we care about? A chain, or all chains?
385 DecisionIDContainer chainsToCheck;
386 if (ids.empty()) { // We care about *all* chains
387 chainsToCheck = activeChainsIntoThisDecision;
388 } else { // We care about specified chains
389 chainsToCheck = ids;
390 }
391 // We have found a rejected decision node *iff* a chainID to check is *not* present here
392 // I.e. the HypoTool for the chain returned a NEGATIVE decision
393 DecisionIDContainer activeChainsPassedByThisDecision;
394 decisionIDs(d, activeChainsPassedByThisDecision);
395 for (const DecisionID checkID : chainsToCheck) {
396 if (not activeChainsPassedByThisDecision.contains(checkID) && // I was REJECTED here ...
397 activeChainsIntoThisDecision.contains(checkID)) { // ... but PASSSED by all my inputs
398 output.push_back(d);
399 break;
400 }
401 }
402 }
403 }
404 return output;
405 }
406
408 const Decision* comingFrom,
409 NavGraph& navGraph,
410 std::set<const Decision*>& fullyExploredFrom,
411 const DecisionIDContainer& ids,
412 const bool enforceDecisionOnNode) {
413
414 // Does this Decision satisfy the chain requirement?
415 if (enforceDecisionOnNode && ids.size() != 0 && !isAnyIDPassing(node, ids)) {
416 return; // Stop propagating down this leg. It does not concern the chain with DecisionID = id
417 }
418
419 // This Decision object is part of this path through the Navigation
420 navGraph.addNode(node, comingFrom);
421
422#if TRIGCOMPUTILS_ENABLE_EARLY_EXIT == 1
423 // Note we have to do this check here (after calling addNode) rather than just before calling recursiveGetDecisionsInternal
424 if (fullyExploredFrom.count(node) == 1) {
425 // We have fully explored this branch
426 return;
427 }
428#endif
429
430 // Continue to the path(s) by looking at this Decision object's seed(s)
431 if ( hasLinkToPrevious(node) ) {
432 // Do the recursion
434 const Decision* seedDecision = *(seed); // Dereference ElementLink
435 // Sending true as final parameter for enforceDecisionOnStartNode as we are recursing away from the supplied start node
436 recursiveGetDecisionsInternal(seedDecision, node, navGraph, fullyExploredFrom, ids, /*enforceDecisionOnNode*/ true);
437 }
438 }
439
440 // Have fully explored down from this point
441 fullyExploredFrom.insert(node);
442
443 return;
444 }
445
446 void recursiveGetDecisions(const Decision* start,
447 NavGraph& navGraph,
448 const DecisionIDContainer& ids,
449 const bool enforceDecisionOnStartNode) {
450
451 std::set<const Decision*> fullyExploredFrom;
452 // Note: we do not require navGraph to be an empty graph. We can extend it.
453 recursiveGetDecisionsInternal(start, /*comingFrom*/nullptr, navGraph, fullyExploredFrom, ids, enforceDecisionOnStartNode);
454
455 return;
456 }
457
458
460 const bool keepOnlyFinalFeatures,
461 const bool removeEmptySteps,
462 const std::vector<std::string>& nodesToDrop)
463 {
464 std::set<NavGraphNode*> fullyExploredFrom;
465 for (NavGraphNode* finalNode : graph.finalNodes()) {
466 recursiveFlagForThinningInternal(finalNode, /*modeKeep*/true, fullyExploredFrom, keepOnlyFinalFeatures, removeEmptySteps, nodesToDrop);
467 }
468 }
469
470
472 bool modeKeep,
473 std::set<NavGraphNode*>& fullyExploredFrom,
474 const bool keepOnlyFinalFeatures,
475 const bool removeEmptySteps,
476 const std::vector<std::string>& nodesToDrop)
477 {
478
479 // If modeKeep == true, then by default we are KEEPING the nodes as we walk up the navigation (towards L1),
480 // otherwise by default we are THINNING the nodes
481 bool keep = modeKeep;
482
483 // The calls to node->node() here are going from the transient NavGraphNode
484 // to the underlying const Decision* from the input collection. Cache these in local stack vars for better readability.
485
486 const Decision* const me = node->node();
487 const Decision* const myFirstParent = (node->seeds().size() ? node->seeds().at(0)->node() : nullptr);
488 const Decision* const myFirstChild = (node->children().size() ? node->children().at(0)->node() : nullptr);
489
490 // KEEP Section: The following code blocks may override the modeKeep default by setting keep=True for this node.
491
492 if (keepOnlyFinalFeatures) {
493 // Check if we have reached the first feature
494 if ( modeKeep == true && (me->hasObjectLink(featureString()) || me->hasObjectLink("subfeature")) ) {
495 // Just to be explicit, we keep this node
496 keep = true;
497
498 // Special BLS case: The bphysics object is attached exceptionally at the ComboHypo.
499 // We want to keep going up one more level in this case to get the Feature node too (we save the 4-vec of the BPhys and both of the muons/electrons)
500 const bool specialBphysCase = (me->name() == comboHypoAlgNodeName());
501
502 // Special R2->R3 case: We can get steps with dummy features, these feature links point back to the same node.
503 // First check again the feature, with an addition requirement on the feature CLID. Then check if the feature points back to the node.
504 const bool specialR2toR3Case = (me->hasObjectLink(featureString(), ClassID_traits<DecisionContainer>::ID()) && me->object<Decision>(featureString()) == me);
505
506 // We change the default behaviour to be modeKeep = false (unless we want to explore up one more level for BLS's special case
507 // or to the next feature for the R2->R3 special case) such that by default we start to NOT flag all the parent nodes to be kept
508 if (!specialBphysCase && !specialR2toR3Case) {
509 modeKeep = false;
510 }
511 }
512 }
513
514 // We always by default keep the initial node from the HLTSeeding, but this may be overridden below by nodesToDrop
515 if (me->name() == hltSeedingNodeName()) {
516 keep = true;
517 }
518
519 // DROP Section: The following code blocks may override both the current modeKeep default and the above KEEP section by setting keep=False for this node.
520
521 // Check also against NodesToDrop
522 for (const std::string& toDrop : nodesToDrop) {
523 if (me->name() == toDrop) {
524 keep = false;
525 break;
526 }
527 }
528
529 // Check against RemoveEmptySteps
530 // The signature we look for here is a InputMaker node connected directly to a ComboHypo node
531 // The key thing to identify is NO intermediate Hypo node
532 // This structure is created in Steps where some chain legs are running reconstruction, but other legs are idle - either due to
533 // menu alignment, or due to the legs being of different length.
534 // For passed chains, there is little point keeping these CH and IM nodes on the idle legs. So we have the option of slimming them here.
535 // First for the ComboHypo ...
536 if (removeEmptySteps && me->name() == comboHypoAlgNodeName() && myFirstParent && myFirstParent->name() == inputMakerNodeName()) {
537 keep = false;
538 }
539 // ... and also for the InputMaker, with flipped logic.
540 if (removeEmptySteps && me->name() == inputMakerNodeName() && myFirstChild && myFirstChild->name() == comboHypoAlgNodeName()) {
541 keep = false;
542 }
543
544 // Check against RemoveEmptySteps
545 // Check for the R2->R3 empty step case
546 if (removeEmptySteps
547 && me->name() == hypoAlgNodeName()
549 && me->object<Decision>(featureString()) == me)
550 {
551 keep = false;
552 }
553 // ... and also for the InputMaker
554 if (removeEmptySteps
555 && me->name() == inputMakerNodeName()
556 && myFirstChild
557 && myFirstChild->name() == hypoAlgNodeName()
559 && myFirstChild->object<Decision>(featureString()) == myFirstChild)
560 {
561 keep = false;
562 }
563
564 // APPLICATION Section:
565
566 if (keep) {
567 node->keep(); // Inform the node that it should NOT be thinned away.
568 }
569
570 for (NavGraphNode* seed : node->seeds()) {
571#if TRIGCOMPUTILS_ENABLE_EARLY_EXIT == 1
572 // NOTE: This code block cuts short the recursive graph exploration for any node which has already been fully-explored-from.
573 //
574 // If we are keeping final features, then processing each node exactly once is actually insufficient.
575 // This is because we may reach a node, X, with a feature which is penultimate/generally-non-final for some chain, A,
576 // but then later on we may follow Terminus->SummaryFilter->Hypothesis(X) for some chain, B, where this same
577 // feature from X _is_ the final-feature for chain B. Here we would not process node X again as we have already dealt with it.
578 // But we need to process it again as for chain B, modeKeep==true still and chain B wants to flag the node as keep().
579 //
580 // We only however need to force the exploration while we are in the mode where we are nominally keeping nodes (modeKeep == true).
581 // Once we have switched to dropping nodes by default, we should be OK to once again skip over nodes which we already processed.
582 // The only thing we should be keeping when modeKeep == false is the L1 node, any this is not dependent on path.
583 //
584 // See: ATR-28061
585 bool allowEarlyExit = true;
586 if (keepOnlyFinalFeatures) {
587 allowEarlyExit = (modeKeep == false);
588 }
589 if (allowEarlyExit && fullyExploredFrom.count(seed) == 1) {
590 // We have fully explored this branch
591 continue;
592 }
593#endif
594 // Recursively call all the way up the graph to the initial nodes from the HLTSeeding
595 recursiveFlagForThinningInternal(seed, modeKeep, fullyExploredFrom, keepOnlyFinalFeatures, removeEmptySteps, nodesToDrop);
596 }
597
598 // Have fully explored down from this point
599 fullyExploredFrom.insert(node);
600 }
601
602 // Note: This version of the function recurses through a full navigation graph (initial input: Decision Object)
603 bool typelessFindLinks(const Decision* start,
604 const std::string& linkName,
605 std::vector<sgkey_t>& keyVec,
606 std::vector<CLID>& clidVec,
607 std::vector<Decision::index_type>& indexVec,
608 std::vector<const Decision*>& sourceVec,
609 const unsigned int behaviour,
610 std::set<const Decision*>* fullyExploredFrom)
611 {
612 using namespace msgFindLink;
613
614 // As the append vectors are user-supplied, perform some input validation.
615 if (keyVec.size() != clidVec.size() or clidVec.size() != indexVec.size()) {
616 ANA_MSG_WARNING("In typelessFindLinks, keyVec, clidVec, indexVec must all be the same size. Instead have:"
617 << keyVec.size() << ", " << clidVec.size() << ", " << indexVec.size());
618 return false;
619 }
620
621 // Locate named links. Both collections of links and individual links are supported.
622 bool found = typelessFindLinksCommonLinkCollection(start, linkName, keyVec, clidVec, indexVec, sourceVec);
623
624 // Early exit
625 if (found && (behaviour & TrigDefs::lastFeatureOfType)) {
626 return true;
627 }
628 // If not Early Exit, then recurse
629 for (const ElementLink<DecisionContainer>& seed : getLinkToPrevious(start)) {
630#if TRIGCOMPUTILS_ENABLE_EARLY_EXIT == 1
631 if (fullyExploredFrom != nullptr) {
632 // We only need to recursively explore back from each node in the graph once.
633 // We can keep a record of nodes which we have already explored, these we can safely skip over.
634 if (fullyExploredFrom->count(*seed) == 1) {
635 continue;
636 }
637 }
638#endif
639 found |= typelessFindLinks(*seed, linkName, keyVec, clidVec, indexVec, sourceVec, behaviour, fullyExploredFrom);
640 }
641 // Fully explored this node
642 if (fullyExploredFrom != nullptr) {
643 fullyExploredFrom->insert(start);
644 }
645 return found;
646 }
647
648 // Note: This version of the function recurses through a sub-graph of the full navigation graph (initial input: NavGraphNode)
649 bool typelessFindLinks(const NavGraphNode* start,
650 const std::string& linkName,
651 std::vector<sgkey_t>& keyVec,
652 std::vector<CLID>& clidVec,
653 std::vector<Decision::index_type>& indexVec,
654 std::vector<const Decision*>& sourceVec,
655 const unsigned int behaviour,
656 std::set<const Decision*>* fullyExploredFrom)
657 {
658 using namespace msgFindLink;
659
660 // As the append vectors are user-supplied, perform some input validation.
661 if (keyVec.size() != clidVec.size() or clidVec.size() != indexVec.size()) {
662 ANA_MSG_WARNING("In typelessFindLinks, keyVec, clidVec, indexVec must all be the same size. Instead have:"
663 << keyVec.size() << ", " << clidVec.size() << ", " << indexVec.size());
664 return false;
665 }
666
667 const Decision* start_decisionObject = start->node();
668 // Locate named links. Both collections of links and individual links are supported.
669 bool found = typelessFindLinksCommonLinkCollection(start_decisionObject, linkName, keyVec, clidVec, indexVec, sourceVec);
670
671 // Early exit
672 if (found && (behaviour & TrigDefs::lastFeatureOfType)) {
673 return true;
674 }
675 // If not Early Exit, then recurse
676 for (const NavGraphNode* seed : start->seeds()) {
677#if TRIGCOMPUTILS_ENABLE_EARLY_EXIT == 1
678 if (fullyExploredFrom != nullptr) {
679 // We only need to recursively explore back from each node in the graph once.
680 // We can keep a record of nodes which we have already explored, these we can safely skip over.
681 const Decision* seed_decisionObject = seed->node();
682 if (fullyExploredFrom->count(seed_decisionObject) == 1) {
683 continue;
684 }
685 }
686#endif
687 found |= typelessFindLinks(seed, linkName, keyVec, clidVec, indexVec, sourceVec, behaviour, fullyExploredFrom);
688 }
689 // Fully explored this node
690 if (fullyExploredFrom != nullptr) {
691 fullyExploredFrom->insert(start_decisionObject);
692 }
693 return found;
694 }
695
696
698 const std::string& linkName,
699 std::vector<sgkey_t>& keyVec,
700 std::vector<CLID>& clidVec,
701 std::vector<Decision::index_type>& indexVec,
702 std::vector<const Decision*>& sourceVec)
703 {
704 bool found = false;
705 std::vector<sgkey_t> tmpKeyVec;
706 std::vector<CLID> tmpClidVec;
707 std::vector<Decision::index_type> tmpIndexVec;
708 if (start->hasObjectCollectionLinks(linkName)) {
709 found = start->typelessGetObjectCollectionLinks(linkName, tmpKeyVec, tmpClidVec, tmpIndexVec);
710 }
711 if (start->hasObjectLink(linkName)) {
712 sgkey_t tmpKey{0};
713 CLID tmpClid{0};
714 Decision::index_type tmpIndex{0};
715 found |= start->typelessGetObjectLink(linkName, tmpKey, tmpClid, tmpIndex);
716 tmpKeyVec.push_back(tmpKey);
717 tmpClidVec.push_back(tmpClid);
718 tmpIndexVec.push_back(tmpIndex);
719 }
720 // De-duplicate
721 for (size_t tmpi = 0; tmpi < tmpKeyVec.size(); ++tmpi) {
722 bool alreadyAdded = false;
723 const sgkey_t tmpKey = tmpKeyVec.at(tmpi);
724 const CLID tmpClid = tmpClidVec.at(tmpi);
725 const Decision::index_type tmpIndex = tmpIndexVec.at(tmpi);
726 for (size_t veci = 0; veci < keyVec.size(); ++veci) {
727 if (SG::sgkeyEqual (keyVec.at(veci), tmpKey)
728 and clidVec.at(veci) == tmpClid
729 and indexVec.at(veci) == tmpIndex)
730 {
731 alreadyAdded = true;
732 break;
733 }
734 }
735 if (!alreadyAdded) {
736 keyVec.push_back( tmpKey );
737 clidVec.push_back( tmpClid );
738 indexVec.push_back( tmpIndex );
739 sourceVec.push_back( start );
740 }
741 }
742 return found;
743 }
744
745
746
747 bool typelessFindLink(const Decision* start,
748 const std::string& linkName,
749 sgkey_t& key,
750 CLID& clid,
752 const Decision*& source,
753 const bool suppressMultipleLinksWarning)
754 {
755 using namespace msgFindLink;
756 // We use findLink in cases where there is only one link to be found, or if there are multiple then we
757 // only want the most recent.
758 // Hence we can supply TrigDefs::lastFeatureOfType. /--> parent3(link)
759 // We can still have more then one link found if there is a branch in the navigation. E.g. start --> parent1 --> parent2(link)
760 // If both parent2 and parent3 possessed an admissible ElementLink, then the warning below will trigger, and only one of the
761 // links will be returned (whichever of parent2 or parent3 happened to be the first seed of parent1).
762 std::vector<sgkey_t> keyVec;
763 std::vector<CLID> clidVec;
764 std::vector<Decision::index_type> indexVec;
765 std::vector<const Decision*> sourceVec;
766 std::set<const xAOD::TrigComposite*> fullyExploredFrom;
767
768 const bool result = typelessFindLinks(start, linkName, keyVec, clidVec, indexVec, sourceVec, TrigDefs::lastFeatureOfType, &fullyExploredFrom);
769 if (!result) {
770 return false; // Nothing found
771 }
772
773 if (keyVec.size() > 1 && !suppressMultipleLinksWarning) {
774 ANA_MSG_WARNING (keyVec.size() << " typeless links found for " << linkName
775 << " returning the first link, consider using findLinks.");
776 }
777 key = keyVec.at(0);
778 clid = clidVec.at(0);
779 index = indexVec.at(0);
780 source = sourceVec.at(0);
781 return true;
782 }
783
784
785 bool typelessFindLink(const NavGraph& subGraph,
786 const std::string& linkName,
787 sgkey_t& key,
788 CLID& clid,
790 const Decision*& source,
791 const bool suppressMultipleLinksWarning)
792 {
793 using namespace msgFindLink;
794 // Note: This function should be the same as its predecessor, just using a NavGraph to start rather than a Decision*
795 // As a result, it can search from more than one Decision* (the NavGraph may have more than one final node)
796 // but it will still warn if this results in more than one link being located.
797 std::vector<sgkey_t> keyVec;
798 std::vector<uint32_t> clidVec;
799 std::vector<Decision::index_type> indexVec;
800 std::vector<const Decision*> sourceVec;
801 std::set<const Decision*> fullyExploredFrom;
802
803 bool result = false;
804 for (const NavGraphNode* finalNode : subGraph.finalNodes()) {
805 result |= typelessFindLinks(finalNode, linkName, keyVec, clidVec, indexVec, sourceVec, TrigDefs::lastFeatureOfType, &fullyExploredFrom);
806 }
807
808 if (!result) {
809 return false; // Nothing found
810 }
811
812 if (keyVec.size() > 1 && !suppressMultipleLinksWarning) {
813 ANA_MSG_WARNING (keyVec.size() << " typeless links found for " << linkName
814 << " returning the first link, consider using findLinks.");
815 }
816 key = keyVec.at(0);
817 clid = clidVec.at(0);
818 index = indexVec.at(0);
819 source = sourceVec.at(0);
820 return true;
821 }
822
823
825 const std::string& chainName,
826 const std::vector<LinkInfo<xAOD::IParticleContainer>>& features,
827 const std::vector<std::size_t>& legMultiplicities,
828 const std::function<bool(const std::vector<LinkInfo<xAOD::IParticleContainer>>&)>& filter)
829 {
830 Combinations combinations(filter);
831 combinations.reserve(legMultiplicities.size());
832 if (legMultiplicities.size() == 1)
833 combinations.addLeg(legMultiplicities.at(0), features);
834 else
835 for (std::size_t legIdx = 0; legIdx < legMultiplicities.size(); ++legIdx)
836 {
837 // Skip any that will not provide IParticle features
838 if (legMultiplicities[legIdx] == 0)
839 continue;
840 HLT::Identifier legID = createLegName(chainName, legIdx);
841 std::vector<LinkInfo<xAOD::IParticleContainer>> legFeatures;
842 for (const LinkInfo<xAOD::IParticleContainer>& info : features) {
843 if (info.decisions->contains(legID.numeric()))
844 legFeatures.push_back(info);
845 }
846 combinations.addLeg(legMultiplicities.at(legIdx), std::move(legFeatures));
847 }
848 return combinations;
849 }
850
851
853 const std::string& chainName,
854 const std::vector<LinkInfo<xAOD::IParticleContainer>>& features,
855 const std::vector<std::size_t>& legMultiplicities,
856 FilterType filter)
857 {
858 return buildCombinations(chainName, features, legMultiplicities, getFilter(filter));
859 }
860
862 const std::string& chainName,
863 const std::vector<LinkInfo<xAOD::IParticleContainer>>& features,
864 const TrigConf::HLTChain *chainInfo,
865 const std::function<bool(const std::vector<LinkInfo<xAOD::IParticleContainer>>&)>& filter)
866 {
867 return buildCombinations(chainName, features, chainInfo->leg_multiplicities(), filter);
868 }
869
871 const std::string& chainName,
872 const std::vector<LinkInfo<xAOD::IParticleContainer>>& features,
873 const TrigConf::HLTChain *chainInfo,
874 FilterType filter)
875 {
876 return buildCombinations(chainName, features, chainInfo, getFilter(filter));
877 }
878
879
880 std::string dump( const Decision* tc, const std::function< std::string( const Decision* )>& printerFnc ) {
881 std::string ret;
882 ret += printerFnc( tc );
883 if ( hasLinkToPrevious(tc) ) {
884 const std::vector<ElementLink<DecisionContainer>> seeds = getLinkToPrevious(tc);
885 for (const ElementLink<DecisionContainer>& seedEL : seeds) {
886 ret += " -> " + dump( *seedEL, printerFnc );
887 }
888 }
889 return ret;
890 }
891
892}
893
Handle class for reading from StoreGate.
Handle class for recording to StoreGate.
Base class for elements of a container that can have aux data.
#define ANA_MSG_WARNING(xmsg)
Macro printing warning messages.
#define ANA_MSG_SOURCE(NAME, TITLE)
the source code part of ANA_MSG_SOURCE
uint32_t CLID
The Class ID type.
char data[hepevt_bytes_allocation_ATLAS]
Definition HepEvt.cxx:11
static Double_t tc
xAOD::TrigCompositeContainer DecisionContainer
static const SG::AuxElement::ConstAccessor< std::vector< TrigCompositeUtils::DecisionID > > readOnlyAccessor("decisions")
static const SG::AuxElement::Accessor< std::vector< TrigCompositeUtils::DecisionID > > readWriteAccessor("decisions")
#define x
value_type push_back(value_type pElem)
Add an element to the end of the collection.
TrigCompositeUtils::DecisionID numeric() const
numeric ID
std::string name() const
reports human redable name
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:569
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:572
Property holding a SG store/key/clid from which a ReadHandle is made.
StatusCode initialize(bool used=true)
If this object is used as a property, then this should be called during the initialize phase.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
bool operator()(const Decision *) const
checks if the arg Decision object has link collection of name specified at construction
bool operator()(const Decision *) const
checks if the arg TC has link of name specified at construction
Transient utility class to represent a node in a graph (m_decisionObject), and a vector of edges (m_f...
Definition NavGraph.h:20
Structure to hold a transient Directed Acyclic Graph (DAG) structure.
Definition NavGraph.h:111
void addNode(const Decision *node, const Decision *comingFrom=nullptr)
Add a new NavGraphNode which shadows the xAOD Decision object "node" from the full navigation graph.
Definition NavGraph.cxx:73
const std::vector< NavGraphNode * > & finalNodes() const
Get all final nodes.
Definition NavGraph.cxx:99
HLT chain configuration information.
const std::vector< size_t > & leg_multiplicities() const
Definition node.h:24
node(node *n=0, const std::string &d="", TObject *t=0)
Definition node.h:32
bool hasObjectLink(const std::string &name, const CLID clid=CLID_NULL) const
Check if a link to an object with a given name and type exists. CLID_NULL to not check type.
const OBJECT * object(const std::string &name) const
Get a bare pointer with the requested name.
bool hasObjectCollectionLinks(const std::string &collectionName, const CLID clid=CLID_NULL) const
Check if links exist to a collection of objects with given name and type. CLID_NULL to not check type...
const std::string & name() const
Get a human-readable name for the object.
std::vector< std::string > intersection(std::vector< std::string > &v1, std::vector< std::string > &v2)
Forward declaration.
constexpr bool sgkeyEqual(const sgkey_t a, const sgkey_t b)
Compare two sgkeys for equality.
Definition sgkey_t.h:39
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
bool isChainId(const HLT::Identifier &chainIdentifier)
Recognise whether the HLT identifier corresponds to a whole chain.
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
Combinations buildCombinations(const std::string &chainName, const std::vector< LinkInfo< xAOD::IParticleContainer > > &features, const std::vector< std::size_t > &legMultiplicities, const std::function< bool(const std::vector< LinkInfo< xAOD::IParticleContainer > > &)> &filter)
Produce the combinations for a set of features.
const Decision * find(const Decision *start, const std::function< bool(const Decision *)> &filter)
traverses Decision object links for another Decision object fulfilling the prerequisite specified by ...
const Decision * getNodeByName(const DecisionContainer &container, const std::string &nodeName)
Returns the navigation node with a given name from a collection or nullptr if missing.
void insertDecisionIDs(const Decision *src, Decision *dest)
Appends the decision IDs of src to the dest decision object.
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()
int32_t getIndexFromLeg(const HLT::Identifier &legIdentifier)
Extract the numeric index of a leg identifier.
const Decision * getExpressTerminusNode(const DecisionContainer &container)
Returns the express-accept navigation node from a collection or nullptr if missing.
void recursiveGetDecisions(const Decision *start, NavGraph &navGraph, const DecisionIDContainer &ids, const bool enforceDecisionOnStartNode)
Search back in time from "node" and locate all paths back through Decision objects for a given chain.
HLT::Identifier getIDFromLeg(const HLT::Identifier &legIdentifier)
Generate the HLT::Identifier which corresponds to the chain name from the leg name.
std::vector< const Decision * > getRejectedDecisionNodes(const asg::EventStoreType *eventStore, const EventContext &ctx, const std::string &summaryCollectionKey, const DecisionIDContainer &ids, const std::set< std::string > &keysToIgnore)
Query all DecisionCollections in the event store, locate all Decision nodes in the graph where an obj...
bool copyLinks(const Decision *src, Decision *dest)
copy all links from src to dest TC objects
bool hasLinkToPrevious(const Decision *d)
checks if there is at least one 'seed' link to previous object
bool passed(DecisionID id, const DecisionIDContainer &idSet)
checks if required decision ID is in the set of IDs in the container
void recursiveFlagForThinningInternal(NavGraphNode *node, bool modeKeep, std::set< NavGraphNode * > &fullyExploredFrom, const bool keepOnlyFinalFeatures, const bool removeEmptySteps, const std::vector< std::string > &nodesToDrop)
Used by recursiveFlagForThinning.
const std::string & comboHypoAlgNodeName()
std::set< DecisionID > DecisionIDContainer
std::function< bool(const std::vector< LinkInfo< xAOD::IParticleContainer > > &)> getFilter(FilterType filter)
Get a lambda corresponding to the specified FilterType enum.
const Decision * getTerminusNode(SG::ReadHandle< DecisionContainer > &container)
void recursiveFlagForThinning(NavGraph &graph, const bool keepOnlyFinalFeatures, const bool removeEmptySteps, const std::vector< std::string > &nodesToDrop)
Used by trigger navigation thinning.
SG::WriteHandle< DecisionContainer > createAndStore(const SG::WriteHandleKey< DecisionContainer > &key, const EventContext &ctx)
Creates and right away records the DecisionContainer with the key.
void recursiveGetDecisionsInternal(const Decision *node, const Decision *comingFrom, NavGraph &navGraph, std::set< const Decision * > &fullyExploredFrom, const DecisionIDContainer &ids, const bool enforceDecisionOnNode)
Used by recursiveGetDecisions.
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.
bool allFailed(const Decision *d)
return true if there is no positive decision stored
void uniqueDecisionIDs(Decision *dest)
Make unique list of decision IDs of dest Decision object.
bool typelessFindLinksCommonLinkCollection(const Decision *start, const std::string &linkName, std::vector< sgkey_t > &keyVec, std::vector< CLID > &clidVec, std::vector< Decision::index_type > &indexVec, std::vector< const Decision * > &sourceVec)
Common functionality shared by both typelessFindLinks interfaces Returns true if at least one link wa...
std::pair< std::string, int32_t > getNameAndIndexFromLeg(const std::string &name)
Extract the name and numeric index of a leg identifier.
const std::string & seedString()
const std::string & summaryPassNodeName()
bool typelessFindLinks(const Decision *start, const std::string &linkName, std::vector< sgkey_t > &keyVec, std::vector< CLID > &clidVec, std::vector< Decision::index_type > &indexVec, std::vector< const Decision * > &sourceVec, const unsigned int behaviour, std::set< const Decision * > *fullyExploredFrom)
search back the TC links for the object of type T linked to the one of TC (recursively) Returns the l...
const std::string & summaryPassExpressNodeName()
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.
bool isAnyIDPassing(const Decision *d, const DecisionIDContainer &required)
Checks if any of the DecisionIDs passed in arg required is availble in 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.
bool typelessFindLink(const Decision *start, const std::string &linkName, sgkey_t &key, CLID &clid, Decision::index_type &index, const Decision *&source, const bool suppressMultipleLinksWarning)
Perform a recursive search for ElementLinks of any time and name 'linkName', starting from Decision o...
static const unsigned int lastFeatureOfType
Run 3 "enum". Only return the final feature along each route through the navigation.
StoreGateSvc EventStoreType
the type returned by AsgTool::evtStore
-event-from-file
Definition index.py:1
Helper to keep a Decision object, ElementLink and ActiveState (with respect to some requested ChainGr...
Definition LinkInfo.h:22