ATLAS Offline Software
Loading...
Searching...
No Matches
DecisionSummaryMakerAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3*/
8
9#include <algorithm>
10#include <sstream>
11#include <unordered_set>
12
13namespace {
14 const std::string s_expressStreamName{"express"};
15}
16
17DecisionSummaryMakerAlg::DecisionSummaryMakerAlg(const std::string& name, ISvcLocator* pSvcLocator)
18 : AthReentrantAlgorithm(name, pSvcLocator) {}
19
22 ATH_CHECK( m_finalDecisionKeys.initialize() );
23 ATH_CHECK( m_summaryKey.initialize() );
24 ATH_CHECK( m_streamsSummaryKey.initialize() );
25 ATH_CHECK( m_hltSeedingSummaryKey.initialize() );
26 ATH_CHECK( m_hltMenuKey.initialize() );
27
28 for ( auto& [chain, collections]: m_lastStepForChain ) {
29 for ( auto& collection: collections ) {
30 m_collectionFilter[ collection ].insert( HLT::Identifier( chain ).numeric() );
31 ATH_MSG_DEBUG( "Final decision of the chain " << chain << " will be read from " << collection );
32 }
33 }
34
35 ATH_CHECK( m_prescaler.retrieve() );
36 if (!m_monTool.empty()) {
37 ATH_CHECK(m_monTool.retrieve());
38 }
39
40 return StatusCode::SUCCESS;
41}
42
45 ATH_CHECK(hltMenu.isValid());
46 m_chainToStreamsMap.clear();
47 // Fill the map of Chain ID -> stream names, omitting express which is treated separately due to express prescaling
48 for (const TrigConf::Chain& chain : *hltMenu) {
49 std::vector<std::string> streams = chain.streams();
50 streams.erase(std::remove(streams.begin(),streams.end(),s_expressStreamName), streams.end());
51 m_chainToStreamsMap.insert({HLT::Identifier(chain.name()).numeric(),std::move(streams)});
52 }
53
54 return StatusCode::SUCCESS;
55}
56
58 return StatusCode::SUCCESS;
59}
60
61StatusCode DecisionSummaryMakerAlg::execute(const EventContext& context) const {
62
63 using namespace TrigCompositeUtils;
64
66 auto container = outputHandle.ptr();
67
71
72 DecisionIDContainer allPassingFinalIDs;
73
74 // Collate final decisions into the passRawOutput object
75 for ( auto& key: m_finalDecisionKeys ) {
76 auto handle{ SG::makeHandle(key, context) };
77 if ( not handle.isValid() ) {
78 ATH_MSG_DEBUG("Input " << key.key() << " not present, did not run in this event.");
79 continue;
80 }
81 const auto thisCollFilter = m_collectionFilter.find( key.key() );
82 if ( thisCollFilter == m_collectionFilter.end() ) {
83 ATH_MSG_WARNING( "The collection " << key.key() << " is not configured to contain any final decision,"
84 << "remove it from the configuration of " << name() << " to save time" );
85 continue;
86 }
87
88 for ( const Decision* decisionObject: *handle ) {
89 // Filter out chains for which this is NOT the final step of their processing
90 const DecisionIDContainer& passingFinalIDs = passedDecisionIDs(decisionObject, thisCollFilter->second);
91
92 if (passingFinalIDs.empty()) {
93 continue;
94 }
95
96 // Create a node to act as a filter between the final summary node and this HypoAlg.
97 // This ensures that we follow this edge (now two edges) in the graph only for the chains in
98 // passingFinalIDs rather than for other chains which are active in decisionObject which
99 // may accept the event via other objects
100
101 // filter -> HypoAlg
102 Decision* filter = newDecisionIn( container, decisionObject, summaryFilterNodeName(), context );
103 decisionIDs(filter).insert( decisionIDs(filter).end(), passingFinalIDs.begin(), passingFinalIDs.end() );
104
105 // HLTPassRaw -> filter
106 linkToPrevious(passRawOutput, filter, context);
107
108 // Accumulate and de-duplicate passed IDs for which this hypo was the Chain's final step
109 allPassingFinalIDs.insert( passingFinalIDs.begin(), passingFinalIDs.end() );
110 }
111 }
112
113 // Copy decisions set into passRawOutput's persistent vector
114 decisionIDs(passRawOutput).insert( decisionIDs(passRawOutput).end(),
115 allPassingFinalIDs.begin(), allPassingFinalIDs.end() );
116
117 if (msgLvl(MSG::DEBUG)) {
118 ATH_MSG_DEBUG( "Number of positive decisions " << allPassingFinalIDs.size() << " passing chains");
119 for ( auto d: allPassingFinalIDs ) {
121 }
122 }
123
124 // Monitor RoI updates between initial and final RoI
125 monitorRoIs(passRawOutput);
126
127 // Get the data from the HLTSeeding, this is where prescales were applied
128 SG::ReadHandle<DecisionContainer> hltSeedingSummary( m_hltSeedingSummaryKey, context );
129 const Decision* l1SeededChains = nullptr; // Activated by L1
130 const Decision* activeChains = nullptr; // Activated and passed prescale check
131 const Decision* prescaledChains = nullptr; // Activated but failed prescale check
132 for (const Decision* d : *hltSeedingSummary) {
133 if (d->name() == "l1seeded") {
134 l1SeededChains = d;
135 } else if (d->name() == "unprescaled") {
136 activeChains = d;
137 } else if (d->name() == "prescaled") {
138 prescaledChains = d;
139 } else {
140 ATH_MSG_ERROR("DecisionSummaryMakerAlg encountered an unknown set of decisions from the HLTSeeding, name '" << d->name() << "'");
141 return StatusCode::FAILURE;
142 }
143 }
144
145 if (l1SeededChains == nullptr || activeChains == nullptr || prescaledChains == nullptr) {
146 ATH_MSG_ERROR("Unable to read in the summary from the HLTSeeding. Cannot write to the HLT output summary the prescale status of HLT chains.");
147 return StatusCode::FAILURE;
148 }
149
150 // l1SeededChains is not currently used here
151
152 // activeChains is not currently used here
153
154 // Get chains which were prescaled out. This is the set of chains which were seeded but which were NOT active (in the first pass)
155 DecisionIDContainer prescaledIDs;
156 decisionIDs( prescaledChains, prescaledIDs ); // Extract from prescaledChains (a Decision*) into prescaledIDs (a set<int>)
157 decisionIDs( prescaledOutput ).insert( decisionIDs( prescaledOutput ).end(),
158 prescaledIDs.begin(), prescaledIDs.end() ); // Save this to the output
159
160 // Calculate and save express stream prescale decisions
161 // TODO: this involves pointless conversions set<uint> -> vector<HLT::Identifier> -> set<uint>, adapt IPrescalingTool to operate on set<uint>
162 HLT::IDVec allPassingFinalIDsVec{allPassingFinalIDs.begin(),allPassingFinalIDs.end()}; // Convert set to vector
163 HLT::IDVec expressIDsVec;
164 ATH_CHECK( m_prescaler->prescaleChains(context, allPassingFinalIDsVec, expressIDsVec, /*forExpressStream=*/ true) );
165 DecisionIDContainer expressIDs; // Convert vector to set
166 for (const HLT::Identifier& id : expressIDsVec) {
167 expressIDs.insert(id.numeric());
168 }
169 decisionIDs( passExpressOutput ).insert( decisionIDs( passExpressOutput ).end(),
170 expressIDs.begin(),
171 expressIDs.end() ); // Save this to the output
172
173 // Fill and write out a set of passed streams
174 std::unordered_set<std::string> passStreamsSet;
175 for (const DecisionID chainID : allPassingFinalIDs) {
176 if (const auto it = m_chainToStreamsMap.find(chainID); it!=m_chainToStreamsMap.cend()) {
177 for (const std::string& streamName : it->second) {
178 passStreamsSet.insert(streamName);
179 }
180 } else {
181 ATH_MSG_ERROR("Passed chain " << HLT::Identifier(chainID).name() << " not in m_chainToStreamsMap");
182 }
183 }
184 // Add express
185 if (!expressIDs.empty()) {
186 passStreamsSet.insert(s_expressStreamName);
187 }
189 ATH_CHECK(passStreams.record(std::make_unique<std::vector<std::string>>(passStreamsSet.begin(),passStreamsSet.end())));
190
191 if (msgLvl(MSG::DEBUG)) {
192 std::ostringstream streamNames;
193 bool first{true};
194 for (const std::string& s : passStreamsSet) {
195 if (first) {first=false;}
196 else {streamNames << ", ";}
197 streamNames << s;
198 }
199 ATH_MSG_DEBUG("This event is accepted to " << passStreamsSet.size() << " streams: " << streamNames.str());
200 }
201
202 // Set the algorithm's filter status. This controlls the running of finalisation algs which we only want to execute
203 // in events which are accepted by one ore more chains.
204 bool filterStatus = true;
205 if (m_setFilterStatus) {
206 filterStatus = (not allPassingFinalIDs.empty());
207 }
208 setFilterPassed(filterStatus, context );
209
210 return StatusCode::SUCCESS;
211}
212
214 using namespace TrigCompositeUtils;
215 using RoILinkVec = std::vector<LinkInfo<TrigRoiDescriptorCollection>>;
216
217 auto printDecision = [this](const Decision* d){
218 ATH_MSG_INFO("The decision corresponds to chain(s):");
219 for (const DecisionID id : decisionIDs(d)) {
220 ATH_MSG_INFO("-- " << HLT::Identifier(id).name());
221 }
222 };
223 auto printDecisionAndRoI = [this,&printDecision](const Decision* d, const TrigRoiDescriptor& roi){
224 printDecision(d);
225 ATH_MSG_INFO("and final RoI: " << roi);
226 };
227
228 // Loop over all final RoIs
229 const RoILinkVec allFinalRoIs = findLinks<TrigRoiDescriptorCollection>(terminusNode, roiString(), TrigDefs::lastFeatureOfType);
230 for (const auto& finalRoILink : allFinalRoIs) {
231 // Get the final TrigRoiDescriptor reference
232 if (!finalRoILink.isValid() || *(finalRoILink.link)==nullptr) {
233 ATH_MSG_WARNING("Encountered invalid final RoI link");
234 printDecision(finalRoILink.source);
235 continue;
236 }
237 const TrigRoiDescriptor& finalRoI = **(finalRoILink.link);
238
239 // Skip full-scan and SuperRoI
240 if (finalRoI.isFullscan() || finalRoI.composite()) {continue;}
241
242 // Get all initial RoIs associated with this final RoI (should be exactly one)
243 const RoILinkVec initialRoIs = findLinks<TrigRoiDescriptorCollection>(finalRoILink.source, initialRoIString(), TrigDefs::lastFeatureOfType);
244
245 // Warn if the number of initial RoIs differs from one
246 if (initialRoIs.empty()) {
247 ATH_MSG_WARNING("Encountered decision node with no " << initialRoIString() << " link");
248 printDecisionAndRoI(finalRoILink.source, finalRoI);
249 continue;
250 }
251 if (initialRoIs.size()>1) {
252 ATH_MSG_WARNING("Encountered decision node with multiple (" << initialRoIs.size() << ") "
253 << initialRoIString() << " links");
254 printDecisionAndRoI(finalRoILink.source, finalRoI);
255 }
256
257 // Get the initial TrigRoiDescriptor reference
258 const auto& initialRoILink = initialRoIs.front();
259 if (!initialRoILink.isValid() || *(initialRoILink.link)==nullptr) {
260 ATH_MSG_WARNING("Encountered invalid initial RoI link");
261 printDecisionAndRoI(finalRoILink.source, finalRoI);
262 continue;
263 }
264 const TrigRoiDescriptor& initialRoI = **(initialRoILink.link);
265
266 // Skip full-scan
267 if (initialRoI.isFullscan()) {continue;}
268
269 // Fill the monitoring histograms
270 Monitored::Scalar dEta{"RoIsDEta", finalRoI.eta() - initialRoI.eta()};
271 Monitored::Scalar dPhi{"RoIsDPhi", CxxUtils::deltaPhi(finalRoI.phi(), initialRoI.phi())};
272 Monitored::Scalar dZed{"RoIsDZed", finalRoI.zed() - initialRoI.zed()};
273 Monitored::Group(m_monTool, dEta, dPhi, dZed);
274
275 // Print warnings on large updates
276 if (m_warnOnLargeRoIUpdates.value()) {
277 if (std::abs(dEta) > 1.0 || std::abs(dPhi) > 1.0 || std::abs(dZed) > 200) {
278 ATH_MSG_WARNING("Large RoI difference between initial and final RoI: "
279 << "dEta=" << dEta << ", dPhi=" << dPhi << ", dZed=" << dZed
280 << "initialRoI: " << initialRoI << ", finalRoI: " << finalRoI);
281 printDecision(finalRoILink.source);
282 }
283 }
284 }
285}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
unsigned int DecisionID
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.
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.
void decisionIDs(const Decision *d, DecisionIDContainer &id)
Extracts DecisionIDs stored in the Decision object.
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 & roiString()
DecisionIDContainer passedDecisionIDs(const Decision *d, const T &required)
return DecisionIDs in Decision object that match the required ones
void renounceArray(SG::VarHandleKeyArray &handlesArray)
bool msgLvl(const MSG::Level lvl) const
virtual void setFilterPassed(bool state, const EventContext &ctx) const
An algorithm that can be simultaneously executed in multiple threads.
Gaudi::Property< bool > m_warnOnLargeRoIUpdates
SG::WriteHandleKey< TrigCompositeUtils::DecisionContainer > m_summaryKey
Gaudi::Property< bool > m_setFilterStatus
SG::WriteHandleKey< std::vector< std::string > > m_streamsSummaryKey
virtual StatusCode initialize() override
void monitorRoIs(const TrigCompositeUtils::Decision *terminusNode) const
Monitor RoI updates between initial and final RoI.
ToolHandle< IPrescalingTool > m_prescaler
std::unordered_map< TrigCompositeUtils::DecisionID, std::vector< std::string > > m_chainToStreamsMap
Chain to streams map filled from the HLT Menu JSON.
virtual StatusCode start() override
DecisionSummaryMakerAlg(const std::string &name, ISvcLocator *pSvcLocator)
Gaudi::Property< std::map< std::string, std::vector< std::string > > > m_lastStepForChain
virtual StatusCode execute(const EventContext &context) const override
ToolHandle< GenericMonitoringTool > m_monTool
SG::ReadHandleKey< xAOD::TrigCompositeContainer > m_hltSeedingSummaryKey
virtual StatusCode finalize() override
std::map< std::string, TrigCompositeUtils::DecisionIDContainer > m_collectionFilter
SG::ReadHandleKeyArray< TrigCompositeUtils::DecisionContainer > m_finalDecisionKeys
SG::ReadHandleKey< TrigConf::HLTMenu > m_hltMenuKey
TrigCompositeUtils::DecisionID numeric() const
numeric ID
Group of local monitoring quantities and retain correlation when filling histograms
Declare a monitored scalar variable.
virtual double zed() const override final
virtual bool isFullscan() const override final
is this a full scan RoI?
virtual double phi() const override final
Methods to retrieve data members.
virtual double eta() const override final
virtual bool composite() const override final
SuperRoI compatability methods.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
pointer_type ptr()
Dereference the pointer.
nope - should be used for standalone also, perhaps need to protect the class def bits ifndef XAOD_ANA...
T deltaPhi(T phiA, T phiB)
Return difference phiA - phiB in range [-pi, pi].
Definition phihelper.h:42
std::vector< HLT::Identifier > IDVec
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
const std::string & summaryPrescaledNodeName()
const std::string & summaryFilterNodeName()
void findLinks(const Decision *start, const std::string &linkName, std::vector< LinkInfo< T > > &links, unsigned int behaviour=TrigDefs::allFeaturesOfType, std::set< const xAOD::TrigComposite * > *fullyExploredFrom=nullptr)
search back the TC links for the object of type T linked to the one of TC (recursively) Populates pro...
const std::string & initialRoIString()
const std::string & summaryPassNodeName()
const std::string & summaryPassExpressNodeName()
static const unsigned int lastFeatureOfType
Run 3 "enum". Only return the final feature along each route through the navigation.
DataModel_detail::iterator< DVL > remove(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end, const T &value)
Specialization of remove for DataVector/List.
Helper for azimuthal angle calculations.