diff --git a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/ComboHypo.h b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/ComboHypo.h index ef7151cc847694c70ebffcfd65392689ca82b7c5..91fa2f8ca7669a474ab688bce59f7bde014532bc 100644 --- a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/ComboHypo.h +++ b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/ComboHypo.h @@ -74,7 +74,7 @@ class ComboHypo : public ::AthReentrantAlgorithm { * @param[out] roiIndex Index inside the roiKey collection. **/ StatusCode extractFeatureAndRoI(const ElementLink<TrigCompositeUtils::DecisionContainer>& EL, - uint32_t& featureKey, uint16_t& featureIndex, uint32_t& roiKey, uint16_t& roiIndex) const; + uint32_t& featureKey, uint16_t& featureIndex, uint32_t& roiKey, uint16_t& roiIndex, bool& roiFullscan) const; /** diff --git a/Trigger/TrigSteer/DecisionHandling/src/ComboHypo.cxx b/Trigger/TrigSteer/DecisionHandling/src/ComboHypo.cxx index 23db05d587b0c539e6cae98a896cde34a5ed67d9..d8a254693e95f82d08684da59033d56cfa2389d5 100644 --- a/Trigger/TrigSteer/DecisionHandling/src/ComboHypo.cxx +++ b/Trigger/TrigSteer/DecisionHandling/src/ComboHypo.cxx @@ -93,7 +93,7 @@ StatusCode ComboHypo::copyDecisions( const LegDecisionsMap & passingLegs, const if ( inputHandle.isValid() ) { for (const Decision* inputDecision : *inputHandle) { - auto thisEL = TrigCompositeUtils::decisionToElementLink(inputDecision, context); + auto thisEL = TrigCompositeUtils::decisionToElementLink(inputDecision, context); DecisionIDContainer inputDecisionIDs; decisionIDs( inputDecision, inputDecisionIDs ); @@ -102,28 +102,29 @@ StatusCode ComboHypo::copyDecisions( const LegDecisionsMap & passingLegs, const std::set_intersection( inputDecisionIDs.begin(), inputDecisionIDs.end(), passing.begin(), passing.end(), std::inserter( common, common.end() ) ); - // check if this EL is in the combination map for the passing decIDs: - ATH_MSG_DEBUG("Searching this element in the map: ("<<thisEL.dataID() << " , " << thisEL.index()<<")"); + // check if this EL is in the combination map for the passing decIDs: + ATH_MSG_DEBUG("Searching this element in the map: ("<<thisEL.dataID() << " , " << thisEL.index()<<")"); DecisionIDContainer finalIds; for (const DecisionID c : common){ const HLT::Identifier cID = HLT::Identifier(c); - // add teh decID only if this candidate passed the combination selection - const ElementLinkVector<DecisionContainer>& Comb=passingLegs.at(c); - if(std::find(Comb.begin(), Comb.end(), thisEL) == Comb.end()) continue; - - ATH_MSG_DEBUG(" Adding "<< cID <<" because EL is found in the passingLegs map"); + // add the decID only if this candidate passed the combination selection + const ElementLinkVector<DecisionContainer>& Comb=passingLegs.at(c); + if(std::find(Comb.begin(), Comb.end(), thisEL) == Comb.end()) { + continue; + } + ATH_MSG_DEBUG(" Adding "<< cID <<" because EL is found in the passingLegs map"); finalIds.insert( cID.numeric() ); // all Ids used by the Filter, including legs if (TrigCompositeUtils::isLegId ( cID )){ const HLT::Identifier mainChain = TrigCompositeUtils::getIDFromLeg( cID ); finalIds.insert( mainChain.numeric() ); - ATH_MSG_DEBUG(" Adding "<< mainChain <<" consequently"); + ATH_MSG_DEBUG(" Adding "<< mainChain <<" consequently"); } } Decision* newDec = newDecisionIn( outDecisions, inputDecision, "CH", context ); ATH_MSG_DEBUG("New decision (Container Index:" << input_counter << ", Element Index:"<< newDec->index() <<") has " - << (TrigCompositeUtils::findLink<TrigRoiDescriptorCollection>(newDec, initialRoIString())).isValid() - << " valid initialRoI, "<< TrigCompositeUtils::getLinkToPrevious(newDec).size() <<" previous decisions and "<<finalIds.size()<<" decision IDs") ; + << (TrigCompositeUtils::findLink<TrigRoiDescriptorCollection>(newDec, initialRoIString())).isValid() + << " valid initialRoI, "<< TrigCompositeUtils::getLinkToPrevious(newDec).size() <<" previous decisions and "<<finalIds.size()<<" decision IDs") ; insertDecisionIDs( finalIds, newDec ); } @@ -159,7 +160,6 @@ StatusCode ComboHypo::execute(const EventContext& context ) const { // loop over all chains in the mult-map for ( const auto& m : m_multiplicitiesReqMap ) { - uint32_t nRequiredUnique = 0; const HLT::Identifier chainId = HLT::Identifier(m.first); const std::vector<int>& multiplicityPerLeg = m.second; const DecisionID requiredDecisionID = chainId.numeric(); @@ -169,14 +169,15 @@ StatusCode ComboHypo::execute(const EventContext& context ) const { bool overallDecision = true; - std::set<uint32_t> uniqueDecisionFeatures; - LegDecisionsMap thisChainCombMap; + std::vector< std::set<uint32_t> > legFeatureHashes; //!< Keeps track per leg of the hash of the objects passing the leg + legFeatureHashes.resize( multiplicityPerLeg.size() ); + size_t fsCount = 0; //!< We allow the FullScan ROI to pass any multiplicity. So we may have to magic up some unique hashes. Counting integers work fine. + LegDecisionsMap thisChainCombMap; // Check multiplicity of each leg of this chain for ( size_t legIndex = 0; legIndex < multiplicityPerLeg.size(); ++legIndex ) { const size_t requiredMultiplicity = multiplicityPerLeg.at( legIndex ); - nRequiredUnique += requiredMultiplicity; HLT::Identifier legId = TrigCompositeUtils::createLegName(chainId, legIndex); // If there is only one leg, then we just use the chain's name. @@ -194,49 +195,102 @@ StatusCode ComboHypo::execute(const EventContext& context ) const { break; } - //check this leg of the chain passes with required multiplicity - const size_t observedMultiplicity = it->second.size(); + // Check the total number of decision objects we have available on this leg from which to satisfy its multiplicity requirement + const size_t nLegDecisionObjects = it->second.size(); - ATH_MSG_DEBUG( "Required multiplicity " << requiredMultiplicity << " for leg " << legId - << ": observed multiplicity " << observedMultiplicity << " in leg " << legIndex ); + ATH_MSG_DEBUG( "Will attempt to meet the required multiplicity of " << requiredMultiplicity << " for leg " << legId + << " with " << nLegDecisionObjects << " Decision Objects in leg " << legIndex << " of " << legId); - if ( observedMultiplicity < requiredMultiplicity ) { - overallDecision = false; - break; - } - - //keep track of the number of unique features/rois + // We extract unique passing features on the leg, if there are no features yet - then the L1 ROI is used as a fall-back feature + // A special behaviour is that if this fall-back triggers, and the full-scan ROI, then the leg is assumed valid. + // This is regardless of whether or not other legs also use the same FS ROI. Regardless of if the leg's required multiplicity. + // This keeps the leg alive until the actual FS reconstruction may occur. At which point, the following ComboHypo will begin + // to cut on the actual reconstructed physics objects. for (const ElementLink<DecisionContainer>& dEL : it->second){ uint32_t featureKey = 0, roiKey = 0; uint16_t featureIndex = 0, roiIndex = 0; + bool roiFullscan = false; // NOTE: roiKey, roiIndex are only currently used in the discrimination for L1 Decision objects (which don't have a 'feature' link) // NOTE: We should make it configurable to choose either the feature or the ROI here, as done in the InputMaker base class when merging. - ATH_CHECK( extractFeatureAndRoI(dEL, featureKey, featureIndex, roiKey, roiIndex) ); - const uint32_t uniquenessHash = (featureKey != 0 ? (featureKey + featureIndex) : (roiKey + roiIndex)); - if (uniquenessHash == 0) { - ATH_MSG_ERROR("Object has no feature, and no initialRoI. Cannot get obtain unique element to avoid double-counting."); - return StatusCode::FAILURE; + ATH_CHECK( extractFeatureAndRoI(dEL, featureKey, featureIndex, roiKey, roiIndex, roiFullscan) ); + if (roiKey != 0 && roiFullscan) { + // This fsCount integer is being to generate unique "hash" values to allow the FS ROI to meet the multiplicity requirements of this leg + for (size_t i = 0; i < requiredMultiplicity; ++i) { + legFeatureHashes.at( legIndex ).insert( ++fsCount ); + ATH_MSG_DEBUG(" -- Add feature hash '" << fsCount << "' to leg " << legIndex << ". (Note: passing hash generated from FullScan ROI)"); + } + } else { + const uint32_t uniquenessHash = (featureKey != 0 ? (featureKey + featureIndex) : (roiKey + roiIndex)); + legFeatureHashes.at( legIndex ).insert( uniquenessHash ); + ATH_MSG_DEBUG(" -- Add feature hash '" << uniquenessHash << "' to leg " << legIndex << "."); } - uniqueDecisionFeatures.insert( uniquenessHash ); } // save combinations of all legs for the tools thisChainCombMap.insert (*it); allDecisionIds.insert(requiredDecisionIDLeg); + + } // end loop over legIndex + + + // Remove any duplicated features which are shared between legs. + // Keep the feature only in the leg which can afford to loose the least number of object, given its multiplicity requirement. + std::set<uint32_t> allFeatureHashes; + for (const std::set<uint32_t>& legHashes : legFeatureHashes) { + allFeatureHashes.insert(legHashes.begin(), legHashes.end()); + } + for (const uint32_t featureHash : allFeatureHashes) { + size_t legsWithHash = 0; //!< If this grows greater than one then we have to start culling features from legs + size_t keepLegIndex = 0; //!< If the hash is used in multiple legs, which leg can least afford to loose it? + int32_t keepLegMargin = std::numeric_limits<int32_t>::max(); //!< How many features the leg at keepLegIndex can afford to loose before it starts to fail its multiplicity requirement. + for (size_t legIndex = 0; legIndex < multiplicityPerLeg.size(); ++legIndex) { + if (legFeatureHashes.at(legIndex).count(featureHash) == 0) { + continue; + } + ++legsWithHash; + const int32_t requiredMultiplicity = multiplicityPerLeg.at(legIndex); + const int32_t currentMultiplicity = legFeatureHashes.at(legIndex).size(); + const int32_t safetyMargin = currentMultiplicity - requiredMultiplicity; // Signed, if -ve then the chain has already been failed by the leg at legIndex + if (safetyMargin < keepLegMargin) { + keepLegMargin = safetyMargin; + keepLegIndex = legIndex; + } + } + if (legsWithHash == 1) { + continue; + } + // If a feature is found on multiple legs, then remove it from all but the leg which can afford to loose it the least + for (size_t legIndex = 0; legIndex < multiplicityPerLeg.size(); ++legIndex) { + if (legIndex == keepLegIndex) { + ATH_MSG_DEBUG("Keeping feature hash '" << featureHash << "', on leg " << legIndex << ". This leg can least afford to loose it. " + << "Leg has " << legFeatureHashes.at(legIndex).size() + << " features, and a multiplicity requirement of " << multiplicityPerLeg.at(legIndex)); + continue; + } + if (legFeatureHashes.at(legIndex).erase(featureHash)) { + ATH_MSG_DEBUG("Removed duplicate feature hash '" << featureHash << "', from leg " << legIndex << ". Leg now has " << legFeatureHashes.at(legIndex).size() + << " remaining features, and a multiplicity requirement of " << multiplicityPerLeg.at(legIndex)); + } + } } //check that the multiplicity of unique features is high enough - ATH_MSG_DEBUG("Number of unique features: " << uniqueDecisionFeatures.size() << ", number of required unique decisions: " << nRequiredUnique); - if ( uniqueDecisionFeatures.size() < nRequiredUnique ) { - overallDecision = false; + for (size_t legIndex = 0; legIndex < multiplicityPerLeg.size(); ++legIndex) { + const size_t requiredMultiplicity = multiplicityPerLeg.at(legIndex); + const size_t currentMultiplicity = legFeatureHashes.at(legIndex).size(); + if (currentMultiplicity < requiredMultiplicity) { + ATH_MSG_DEBUG("Leg " << legIndex << " fails multiplicity check. Required unique features:" << requiredMultiplicity << ", found unique features: " << currentMultiplicity); + overallDecision = false; + break; + } } //Overall chain decision ATH_MSG_DEBUG( "Chain " << chainId << ( overallDecision ? " is accepted" : " is rejected") <<" after multiplicity requirements" ); if ( overallDecision == true ) { for (auto decID: allDecisionIds) { - // saving the good combiantions - passingLegs.insert (thisChainCombMap.begin(), thisChainCombMap.end()); + // saving the good combinations + passingLegs.insert (thisChainCombMap.begin(), thisChainCombMap.end()); ATH_MSG_DEBUG(" Passing " << HLT::Identifier(decID)<<" after multiplicity test"); } } @@ -247,21 +301,21 @@ StatusCode ComboHypo::execute(const EventContext& context ) const { /////////////////////// if (m_hypoTools.size()>0){ for ( auto& tool: m_hypoTools ) { - ATH_MSG_DEBUG( "Calling tool "<<tool->name()); - ATH_CHECK( tool->decide( passingLegs, context ) ); + ATH_MSG_DEBUG( "Calling tool "<<tool->name()); + ATH_CHECK( tool->decide( passingLegs, context ) ); } } } - // this is only for debug: - if (msgLvl(MSG::DEBUG)){ - DecisionIDContainer passing; - for (auto const& element : passingLegs) { - passing.insert(element.first); - } - for (auto p: passing) - ATH_MSG_DEBUG("Passing "<<HLT::Identifier(p)); + // this is only for debug: + if (msgLvl(MSG::DEBUG)){ + DecisionIDContainer passing; + for (auto const& element : passingLegs) { + passing.insert(element.first); } + for (auto p: passing) + ATH_MSG_DEBUG("Passing "<<HLT::Identifier(p)); + } // need to pass all combinations, since not all element pass the decID ATH_CHECK( copyDecisions( passingLegs, context ) ); @@ -271,7 +325,7 @@ StatusCode ComboHypo::execute(const EventContext& context ) const { StatusCode ComboHypo::extractFeatureAndRoI(const ElementLink<DecisionContainer>& dEL, - uint32_t& featureKey, uint16_t& featureIndex, uint32_t& roiKey, uint16_t& roiIndex) const + uint32_t& featureKey, uint16_t& featureIndex, uint32_t& roiKey, uint16_t& roiIndex, bool& roiFullscan) const { uint32_t featureClid = 0; // Note: Unused. We don't care what the type of the feature is here const bool foundFeature = typelessFindLink((*dEL), featureString(), featureKey, featureClid, featureIndex); @@ -280,6 +334,7 @@ StatusCode ComboHypo::extractFeatureAndRoI(const ElementLink<DecisionContainer>& if (roiSeedLI.isValid()) { roiKey = roiSeedLI.link.key(); roiIndex = roiSeedLI.link.index(); + roiFullscan = (*(roiSeedLI.link))->isFullscan(); } if (!foundFeature && !roiSeedLI.isValid()) { ATH_MSG_WARNING("Did not find the feature or initialRoI for " << dEL.dataID() << " index " << dEL.index());